/** * Removes a contact from the system. * @param {array} $_REQUEST * @param {string} $_REQUEST.label The label of the contact * @param {string} $_REQUEST.contactUserId The contactUserId of the contact * @param {string} [$_REQUEST.userId=Users::loggedInUser(true)->id] You can override the user id, if another plugin adds a hook that allows you to do this */ function Users_contact_delete($params = array()) { $req = array_merge($_REQUEST, $params); Q_Request::requireFields(array('label', 'contactUserId'), $req, true); $loggedInUserId = Users::loggedInUser(true)->id; $userId = Q::ifset($req, 'userId', $loggedInUserId); $label = $req['label']; $contactUserId = $req['contactUserId']; Users::canManageContacts($loggedInUserId, $userId, $label, true); $contact = new Users_Contact(); $contact->userId = $userId; $contact->label = $label; $contact->contactUserId = $contactUserId; return $contact->remove(); }
/** * Removes a contact from the system. * @param {array} $_REQUEST * @param {string} $_REQUEST.label The label of the contact * @param {string} $_REQUEST.contactUserId The contactUserId of the contact * @param {string} [$_REQUEST.userId=Users::loggedInUser(true)->id] You can override the user id, if another plugin adds a hook that allows you to do this */ function Users_contact_delete($params = array()) { $req = array_merge($_REQUEST, $params); Q_Request::requireFields(array('label', 'contactUserId'), $req, true); $loggedInUserId = Users::loggedInUser(true)->id; $userId = Q::ifset($req, 'userId', $loggedInUserId); $label = $req['label']; $contactUserId = $req['contactUserId']; return !!Users_Contact::removeContact($userId, $label, $contactUserId); }
/** * Adds contacts to the system. Fills the "contacts" slot. * @param {array} $_REQUEST * @param {string} $_REQUEST.label The label of the contact * @param {string} $_REQUEST.contactUserId The contactUserId of the contact * @param {string} [$_REQUEST.nickname] The nickname of the contact * @param {string} [$_REQUEST.userId=Users::loggedInUser(true)->id] You can override the user id, if another plugin adds a hook that allows you to do this */ function Users_contact_post($params = array()) { $req = array_merge($_REQUEST, $params); Q_Request::requireFields(array('label', 'contactUserId'), $req, true); $loggedInUserId = Users::loggedInUser(true)->id; $userId = Q::ifset($req, 'userId', $loggedInUserId); $contactUserId = $req['contactUserId']; $nickname = Q::ifset($req, 'nickname', null); $contacts = Users_Contact::addContact($userId, $req['label'], $contactUserId, $nickname); Q_Response::setSlot('contacts', Db::exportArray($contacts)); }
/** * Edits a contact in the system. Fills the "contact" slot. * @param {array} $_REQUEST * @param {string} $_REQUEST.label The label of the contact * @param {string} $_REQUEST.contactUserId The contactUserId of the contact * @param {string} [$_REQUEST.nickname] The nickname of the contact * @param {string} [$_REQUEST.userId=Users::loggedInUser(true)->id] You can override the user id, if another plugin adds a hook that allows you to do this */ function Users_contact_put($params = array()) { $req = array_merge($_REQUEST, $params); Q_Request::requireFields(array('label', 'contactUserId'), $req, true); $loggedInUserId = Users::loggedInUser(true)->id; $userId = Q::ifset($req, 'userId', $loggedInUserId); $label = $req['label']; $contactUserId = $req['contactUserId']; $nickname = Q::ifset($req, 'nickname', null); $contact = Users_Contact::updateContact($userId, $label, $contactUserId, compact('nickname')); Q_Response::setSlot('contact', $contact->exportArray()); }
function Q_response_dialogs() { // Here is where you would pre-generate various dialog elements // that you might show with Q.Dialogs.push if (!Users::roles(null, array('Websites/admins'))) { return ''; } $app = Q_Config::expect('Q', 'app'); $userIds = Users_Contact::select('contactUserId')->where(array('userId' => $app, 'label' => 'Websites/admins'))->fetchAll(PDO::FETCH_COLUMN, 'contactUserId'); $admins = Users_User::select('*')->where(array('id' => $userIds))->fetchDbRows(); return Q::view('Trump/dialogs/common.php', compact('admins')); }
function Streams_before_Users_Contact_saveExecute($params) { $contacts = array($params['row']); // the new values about to be written if ($params['query']->type === Db_Query::TYPE_UPDATE) { // we are updating an existing contact $contacts = array_merge($contacts, Users_Contact::select('*')->where($params['where'])->limit(1)->fetchDbRows()); } // Update avatar as viewed by everyone who was in that contacts list foreach ($contacts as $contact) { Streams::updateAvatar($contact->contactUserId, $contact->userId); } }
/** * Edits a contact in the system. Fills the "contact" slot. * @param {array} $_REQUEST * @param {string} $_REQUEST.label The label of the contact * @param {string} $_REQUEST.contactUserId The contactUserId of the contact * @param {string} [$_REQUEST.nickname] The nickname of the contact * @param {string} [$_REQUEST.userId=Users::loggedInUser(true)->id] You can override the user id, if another plugin adds a hook that allows you to do this */ function Users_contact_put($params = array()) { $req = array_merge($_REQUEST, $params); Q_Request::requireFields(array('label', 'contactUserId'), $req, true); $loggedInUserId = Users::loggedInUser(true)->id; $userId = Q::ifset($req, 'userId', $loggedInUserId); $label = $req['label']; $contactUserId = $req['contactUserId']; $nickname = Q::ifset($req, 'nickname', null); Users::canManageContacts($loggedInUserId, $userId, $label, true); $contact = new Users_Contact(); $contact->userId = $userId; $contact->label = $label; $contact->contactUserId = $contactUserId; if (!$contact->retrieve()) { throw new Q_Exception_MissingRow(array('table' => 'Users_Contact', 'criteria' => json_encode($contact->fields))); } if ($nickname) { $contact->nickname = $nickname; } $contact->save(); Q_Response::setSlot('contact', $contact->exportArray()); }
/** * Adds contacts to the system. Fills the "contacts" slot. * @param {array} $_REQUEST * @param {string} $_REQUEST.label The label of the contact * @param {string} $_REQUEST.contactUserId The contactUserId of the contact * @param {string} [$_REQUEST.nickname] The nickname of the contact * @param {string} [$_REQUEST.userId=Users::loggedInUser(true)->id] You can override the user id, if another plugin adds a hook that allows you to do this */ function Users_contact_post($params = array()) { $req = array_merge($_REQUEST, $params); Q_Request::requireFields(array('label', 'contactUserId'), $req, true); $loggedInUserId = Users::loggedInUser(true)->id; $userId = Q::ifset($req, 'userId', $loggedInUserId); $contactUserId = $req['contactUserId']; $nickname = Q::ifset($req, 'nickname', null); $l = $req['label']; if ($userId !== $loggedInUserId) { Users_User::fetch($userId, true); } Users_User::fetch($contactUserId, true); Users::canManageContacts($loggedInUserId, $userId, $l, true); $label = new Users_Label(); $label->userId = $userId; $label->label = $l; if (!$label->retrieve()) { throw new Q_Exception_MissingRow(array('table' => 'Users_Label', 'criteria' => json_encode($label->fields))); } $contacts = Users_Contact::addContact($userId, $l, $contactUserId, $nickname); Q_Response::setSlot('contacts', Db::exportArray($contacts)); }
/** * Remove contact from label * @method removeContact * @static * @param {string} $userId * @param {string} $label * @param {string} $contactId * @return {boolean} */ static function removeContact($userId, $label, $contactId) { foreach (array('userId', 'label', 'contactUserId') as $field) { if (empty(${$field})) { throw new Q_Exception_RequiredField(compact('field')); } } $contact = new Users_Contact(); $contact->userId = $userId; $contact->label = $label; $contact->contactUserId = $contactId; return !!$contact->remove(); }
/** * Inserts some Users_Contact rows for the locally registered users * who have added links to this particular contact information. * Removes the links after successfully adding the Users_Contact rows. * @method saveContactsFromLinks * @static * @param {array} $contact_info An array of key => value pairs, where keys can be: * * * "email" => the user's email address * * "mobile" => the user's mobile number * * "email_hashed" => the standard hash of the user's email address * * "mobile_hashed" => the standard hash of the user's mobile number * * "facebook" => the user's facebook uid * * "twitter" => the user's twitter uid * * @param {string} $userId The id of the user who has verified these identifiers */ static function saveContactsFromLinks() { /** * @event Users/saveContactsFromLinks {before} */ Q::event('Users/saveContactsFromLinks', array(), 'before'); $user = self::loggedInUser(); $contact_info = array(); foreach (self::$types as $type => $field) { if (!empty($user->{$field})) { $contact_info[$type] = $user->{$field}; } } $links = $contact_info ? Users::links($contact_info) : array(); $contacts = array(); foreach ($links as $link) { $extraInfo = json_decode($link->extraInfo, true); $firstName = Q::ifset($extraInfo, 'firstName', ''); $lastName = Q::ifset($extraInfo, 'lastName', ''); $fullName = $firstName ? $lastName ? "{$firstName} {$lastName}" : $firstName : ($lastName ? $lastName : ""); if (!empty($extraInfo['labels']) and is_array($extraInfo['labels'])) { foreach ($extraInfo['labels'] as $label) { // Insert the contacts one by one, so if an error occurs // we can continue right on inserting the rest. $contact = new Users_Contact(); $contact->userId = $link->userId; $contact->contactUserId = $user->id; $contact->label = $label; $contact->nickname = $fullName; $contact->save(true); $link->remove(); // we don't need this link anymore // TODO: Think about porting this to Node // and setting a flag when done. // Perhaps we should send a custom message through socket.io // which would cause Users.js to add a notice to the interface } } } /** * @event Users/saveContactsFromLinks {after} * @param {array} contacts */ Q::event('Users/saveContactsFromLinks', compact('contacts'), 'after'); // TODO: Add a handler to this event in the Streams plugin, so that // we post this information to a stream on the hub, which will // update all its subscribers, who will also run saveContactsFromLinks // for their local users. }
function Streams_before_Users_Contact_removeExecute($params) { // Save the contacts list that will be deleted, so we can update the avatars later Streams::$cache['contacts_removed'] = Users_Contact::select('*')->where($params['criteria'])->fetchDbRows(); }
/** * Check label or array of labels and return existing users * @method labelsToIds * @static * @param $asUserId {string} The user id of inviting user * @param $labels {string|array} * @return {array} The array of user ids */ static function labelsToIds($asUserId, $labels) { if (empty($labels)) { return array(); } if (!is_array($labels)) { $labels = array_map('trim', explode(',', $labels)); } $userIds = array(); foreach ($labels as $label) { $userIds = array_merge($userIds, Users_Contact::select('contactUserId')->where(array('userId' => $asUserId, 'label' => $label))->fetchAll(PDO::FETCH_COLUMN)); } return $userIds; }
/** * Updates the publisher's avatars, which may have changed with the taintedAccess. * This function should be called during rare events that may cause the * publisher's avatar to change appearance for certain users viewing it.<br/> * * You should rarely have to call this function. It is used internally by the model, * in two main situations: * * 1) adding, removing or modifying a Streams_Access row for Streams/user/firstName or Streams/user/lastName * In this case, the function is able to update exactly the avatars that need updating. * * 2) adding, removing or modifying a Stream row for Streams/user/firstName or Streams/user/lastName * In this case, there may be some avatars which this function will miss. * These correspond to users which are reachable by the access array for one stream, * but not the other. For example, if Streams/user/firstName is being updated, but * a particular user is reachable only by the access array for Streams/user/lastName, then * their avatar will not be updated and contain a stale value for firstName. * To fix this, the Streams_Stream model passes true in the 4th parameter to this function. * @method updateAvatars * @static * @param {string} $publisherId * id of the publisher whose avatar to update * @param {array} $taintedAccess * array of Streams_Access objects representing access information that is either * about to be saved, are about to be overwritten, or will be deleted * @param {string|Streams_Stream} $streamName * pass the stream name here. You can also pass a Stream_Stream object here, * in which case it will be used, instead of selecting that stream from the database. * @param {boolean} $updateToPublicValue=false * if you want to first update all the avatars for this stream * to the what the public would see, to avoid the situation described in 2). */ static function updateAvatars($publisherId, $taintedAccess, $streamName, $updateToPublicValue = false) { if (!isset($streamName)) { $streamAccesses = array(); foreach ($taintedAccess as $access) { $streamAccesses[$access->streamName][] = $access; } if (count($streamAccesses) > 1) { foreach ($streamAccesses as $k => $v) { self::updateAvatars($publisherId, $v, $k); } return false; } } if ($streamName instanceof Streams_Stream) { $stream = $streamName; $streamName = $stream->name; } // If we are here, all the Stream_Access objects have the same streamName if ($streamName !== 'Streams/user/firstName' and $streamName !== 'Streams/user/lastName' and $streamName !== 'Streams/user/username') { // we don't care about access to other streams being updated return false; } $showToUserIds = array(); // Select the user corresponding to this publisher $user = new Users_User(); $user->id = $publisherId; if (!$user->retrieve(null, null, array('ignoreCache' => true))) { throw new Q_Exception_MissingRow(array('table' => 'user', 'criteria' => 'id = ' . $user->id)); } // Obtain the stream object to use if (isset($stream)) { if (!isset($stream->content)) { $stream->content = ''; } } else { // If the $stream isn't already defined, select it $stream = new Streams_Stream(); $stream->publisherId = $publisherId; $stream->name = $streamName; if (!$stream->retrieve()) { // Strange, this stream doesn't exist. // Well, we will just silently set the content to '' then $stream->content = ''; } } $content_readLevel = Streams::$READ_LEVEL['content']; $readLevels = array(); $label_readLevels = array(); $contact_label_list = array(); $removed_labels = array(); // First, assign all the readLevels that are directly set for specific users, // and aggregate the contact_labels from the other accesses, for an upcoming select. foreach ($taintedAccess as $access) { if ($userId = $access->ofUserId) { $readLevel = $access->readLevel; $readLevels[$userId] = $readLevel; if ($readLevel < 0) { $showToUserIds[$userId] = null; // not determined yet } else { if ($readLevel >= $content_readLevel) { $showToUserIds[$userId] = true; } else { $showToUserIds[$userId] = false; } } } else { if ($access->ofContactLabel) { $ofContactLabel = $access->ofContactLabel; $contact_label_list[] = $ofContactLabel; if ($access->get('removed', false)) { $removed_labels[$ofContactLabel] = true; } else { $label_readLevels[$ofContactLabel] = $access->readLevel; } } } } // Now, get all the people affected by this change, and their readLevels $readLevels2 = array(); if ($contact_label_list) { $contact_label_list = array_unique($contact_label_list); $contacts = Users_Contact::select('*')->where(array('userId' => $publisherId, 'label' => $contact_label_list))->fetchDbRows(null, '', 'contactUserId'); foreach ($contacts as $contact) { $contactUserId = $contact->contactUserId; if (isset($showToUserIds[$contactUserId])) { // this user had their read level set directly by the access, // which overrides read levels set by access using ofContactLabel continue; } if (isset($removed_labels[$ofContactLabel])) { // this label doesn't affect readLevels anymore, since it was deleted // but put this contact's id on a list whose readLevels need to be determined $showToUserIds[$contactUserId] = null; continue; } if (!isset($label_readLevels[$contact->label])) { continue; } $readLevel = $label_readLevels[$contact->label]; if (!isset($readLevels2[$contactUserId])) { $readLevels2[$contactUserId] = $readLevel; } else { $readLevels2[$contactUserId] = max($readLevels2[$contactUserId], $readLevel); } } } // Now step through all the users we found who were found through ofContactLabel // and make sure we update the avatar rows that were meant for them. foreach ($readLevels2 as $userId => $rl) { if ($rl >= $content_readLevel) { $showToUserIds[$userId] = true; } else { // in order for this to happen, two things had to be true: // 1) there was no access that directly set a readLevel >= $content_readLevel // 2) there was no access that set a readLevel >= $content_readLevel for any label containing this user // therefore, their view should be the public view $showToUserIds[$userId] = 'public'; } } // Resolve all the undetermined readLevels foreach ($showToUserIds as $userId => $v) { if (!isset($v)) { // if the readLevel hasn't been determined by now, it's the same as the public one $showToUserIds[$userId] = 'public'; } } // Set up the self avatar: $showToUserIds[$publisherId] = true; // Finally, set up the public avatar: if (!isset($stream->readLevel)) { $stream->readLevel = Streams_Stream::$DEFAULTS['readLevel']; } $showToUserIds[""] = $stream->readLevel >= $content_readLevel; // Now, we update the avatars: $parts = explode('/', $streamName); $field = end($parts); $rows_that_show = array(); $rows_that_hide = array(); foreach ($showToUserIds as $userId => $show) { if ($show === 'public') { // If no show is explicitly specified, use the value used for the rest of the public $show = $showToUserIds[""]; } if ($show === true) { $rows_that_show[] = array('publisherId' => $publisherId, 'toUserId' => $userId, 'username' => $user->username, 'icon' => $user->icon, 'updatedTime' => new Db_Expression("CURRENT_TIMESTAMP"), $field => $stream->content); } else { if ($show === false) { $rows_that_hide[] = array('publisherId' => $publisherId, 'toUserId' => $userId, 'username' => $user->username, 'icon' => $user->icon, 'updatedTime' => new Db_Expression("CURRENT_TIMESTAMP"), $field => ''); } } } $updates_that_show = array('username' => $user->username, 'icon' => $user->icon, 'updatedTime' => new Db_Expression("CURRENT_TIMESTAMP"), $field => $stream->content); $updates_that_hide = array('username' => $user->username, 'icon' => $user->icon, 'updatedTime' => new Db_Expression("CURRENT_TIMESTAMP"), $field => ''); // We are now ready to make changes to the database. if ($updateToPublicValue) { Streams_Avatar::update()->set(array($field => $showToUserIds[""] ? $stream->content : ''))->where(compact('publisherId'))->execute(); } Streams_Avatar::insertManyAndExecute($rows_that_show, array('onDuplicateKeyUpdate' => $updates_that_show)); Streams_Avatar::insertManyAndExecute($rows_that_hide, array('onDuplicateKeyUpdate' => $updates_that_hide)); }
/** * Fetch an array of labels. By default, returns all the labels. * @method fetch * @param {string} [$userId=null] The id of the user whose contact labels should be fetched * @param {string|Db_Expression} [$filter=''] Pass a string prefix such as "Users/", or some db expression, to get only a particular subset of labels. * @param {boolean} [$checkContacts=false] Whether to also look in the Users_Contact table and only return labels that have at least one contact. * @return {array} An array of array(label => title) pairs */ static function fetch($userId = null, $filter = '', $checkContacts = false) { if (!isset($userId)) { $user = Users::loggedInUser(true); $userId = $user->id; } $criteria = array('userId' => $userId); if ($filter) { $criteria['label'] = is_string($filter) ? new Db_Range($filter, true, false, null) : $filter; } if ($checkContacts) { $contact_array = Users_Contact::select('*')->where($criteria)->groupBy('userId, label')->fetchDbRows(); } $labels = Users_Label::select('*')->where($criteria)->fetchDbRows(null, null, 'label'); $icons = array(); if (!$checkContacts) { return $labels; } $contacts = array(); foreach ($contact_array as $contact) { $contacts[$contact->label] = $contact->label; } foreach ($labels as $label) { if (!isset($contacts[$label->label])) { unset($labels[$label->label]); } } return $labels; }
/** * Check if a contact with this label exists * @method checkLabel * @static * @param {string} $userId * @param {string} $label * @param {string} $contactId * @return {Db_Row|false} */ static function checkLabel($userId, $label, $contactId) { if (!$userId or !$contactId) { return null; } if ($userId instanceof Users_User) { $userId = $userId->id; } if ($contactId instanceof Users_User) { $contactId = $contactId->id; } $contact = new Users_Contact(); $contact->userId = $userId; $contact->label = $label; $contact->contactUserId = $contactId; return $contact->retrieve(); }
/** * Invites a user (or a future user) to a stream . * @method invite * @static * @param {string} $publisherId The id of the stream publisher * @param {string} $streamName The name of the stream the user will be invited to * @param {array} $who Array that can contain the following keys: * @param {string|array} [$who.userId] user id or an array of user ids * @param {string|array} [$who.fb_uid] fb user id or array of fb user ids * @param {string|array} [$who.label] label or an array of labels, or tab-delimited string * @param {string|array} [$who.identifier] identifier or an array of identifiers, or tab-delimited string * @param {integer} [$who.newFutureUsers] the number of new Users_User objects to create via Users::futureUser in order to invite them to this stream. This typically is used in conjunction with passing the "html" option to this function. * @param {array} [$options=array()] * @param {string|array} [$options.addLabel] label or an array of labels for adding publisher's contacts * @param {string|array} [$options.addMyLabel] label or an array of labels for adding asUserId's contacts * @param {integer} [$options.readLevel] => the read level to grant those who are invited * @param {integer} [$options.writeLevel] => the write level to grant those who are invited * @param {integer} [$options.adminLevel] => the admin level to grant those who are invited * @param {string} [$options.displayName] => the display name to use to represent the inviting user * @param {string} [$options.appUrl] => Can be used to override the URL to which the invited user will be redirected and receive "Q.Streams.token" in the querystring. * @param {array} [$options.html] => an array of ($template, $batchName) such as ("MyApp/foo.handlebars", "foo") for generating html snippets which can then be viewed from and printed via the action Streams/invitations?batchName=$batchName&invitingUserId=$asUserId&limit=$limit&offset=$offset * @param {string} [$options.asUserId=Users::loggedInUser(true)->id] Invite as this user id, defaults to logged-in user * @param {boolean} [$options.skipAccess] whether to skip access checks when adding labels and contacts * @see Users::addLink() * @return {array} returns array with keys "success", "invited", "statuses", "identifierTypes", "alreadyParticipating" */ static function invite($publisherId, $streamName, $who, $options = array()) { if (isset($options['asUserId'])) { $asUserId = $options['asUserId']; $asUser = Users_User::fetch($asUserId); } else { $asUser = Users::loggedInUser(true); $asUserId = $asUser->id; } // Fetch the stream as the logged-in user $stream = Streams::fetchOne($asUserId, $publisherId, $streamName); if (!$stream) { throw new Q_Exception_MissingRow(array('table' => 'stream', 'criteria' => 'with that name'), 'streamName'); } // Do we have enough admin rights to invite others to this stream? if (!$stream->testAdminLevel('invite') || !$stream->testWriteLevel('join')) { throw new Users_Exception_NotAuthorized(); } if (isset($options['html'])) { $html = $options['html']; if (!is_array($html) or count($html) < 2) { throw new Q_Exception_WrongType(array('field' => "options.html", 'type' => 'array of 2 strings')); } list($template, $batchName) = $html; // validate these paths $filename = APP_VIEWS_DIR . DS . $template; if (!Q::realPath($filename)) { throw new Q_Exception_MissingFile(compact('filename')); } $ext = $pathinfo = pathinfo($template, PATHINFO_EXTENSION); if ($ext !== 'handlebars') { throw new Q_Exception_WrongValue(array('field' => 'options.html[0]', 'range' => 'a filename with extension .handlebars')); } $path = Streams::invitationsPath($asUserId) . DS . $batchName; Q_Utils::canWriteToPath($path, true, true); } // get user ids if any to array, throw if user not found $raw_userIds = isset($who['userId']) ? Users_User::verifyUserIds($who['userId'], true) : array(); // merge labels if any if (isset($who['label'])) { $label = $who['label']; if (is_string($label)) { $label = array_map('trim', explode("\t", $label)); } $raw_userIds = array_merge($raw_userIds, Users_User::labelsToIds($asUserId, $label)); } // merge identifiers if any $identifierType = null; $statuses = null; if (isset($who['identifier'])) { $identifier = $who['identifier']; if (is_string($identifier)) { if (Q_Valid::email($who['identifier'])) { $identifierType = 'email'; } else { if (Q_Valid::phone($who['identifier'])) { $identifierType = 'mobile'; } } $identifier = array_map('trim', explode("\t", $identifier)); } $statuses = array(); $identifier_ids = Users_User::idsFromIdentifiers($identifier, $statuses); $raw_userIds = array_merge($raw_userIds, $identifier_ids); } // merge fb uids if any if (isset($who['fb_uid'])) { $fb_uids = $who['fb_uid']; if (is_string($fb_uids)) { $fb_uids = array_map('trim', explode("\t", $fb_uids)); } $raw_userIds = array_merge($raw_userIds, Users_User::idsFromFacebook($fb_uids)); } if (!empty($who['newFutureUsers'])) { $nfu = $who['newFutureUsers']; for ($i = 0; $i < $nfu; ++$i) { $raw_userIds[] = Users::futureUser('none', null)->id; } } // ensure that each userId is included only once // and remove already participating users $raw_userIds = array_unique($raw_userIds); $total = count($raw_userIds); $userIds = Streams_Participant::filter($raw_userIds, $stream); $to_invite = count($userIds); $appUrl = !empty($options['appUrl']) ? $options['appUrl'] : Q_Request::baseUrl() . '/' . Q_Config::get("Streams", "types", $stream->type, "invite", "url", "plugins/Streams/stream"); // now check and define levels for invited user $readLevel = isset($options['readLevel']) ? $options['readLevel'] : null; if (isset($readLevel)) { $readLevel = Streams_Stream::numericReadLevel($readLevel); if (!$stream->testReadLevel($readLevel)) { // We can't assign greater read level to other people than we have ourselves! throw new Users_Exception_NotAuthorized(); } } $writeLevel = isset($options['writeLevel']) ? $options['writeLevel'] : null; if (isset($writeLevel)) { $writeLevel = Streams_Stream::numericWriteLevel($writeLevel); if (!$stream->testWriteLevel($writeLevel)) { // We can't assign greater write level to other people than we have ourselves! throw new Users_Exception_NotAuthorized(); } } $adminLevel = isset($options['adminLevel']) ? $options['adminLevel'] : null; if (isset($adminLevel)) { $adminLevel = Streams_Stream::numericAdminLevel($adminLevel); if (!$stream->testAdminLevel($adminLevel + 1)) { // We can't assign an admin level greater, or equal, to our own! // A stream's publisher can assign owners. Owners can assign admins. // Admins can confer powers to invite others, to some people. // Those people can confer the privilege to publish a message re this stream. // But admins can't assign other admins, and even stream owners // can't assign other owners. throw new Users_Exception_NotAuthorized(); } } // calculate expiry time $duration = Q_Config::get("Streams", "types", $stream->type, "invite", "duration", false); $expiry = $duration ? strtotime($duration) : null; $asUserId2 = empty($options['skipAccess']) ? $asUserId : false; if ($label = Q::ifset($options, 'addLabel', null)) { if (is_string($label)) { $label = explode("\t", $label); } Users_Label::addLabel($label, $publisherId, null, null, $asUserId2, true); } if ($myLabel = Q::ifset($options, 'addMyLabel', null)) { if (is_string($myLabel)) { $myLabel = explode("\t", $myLabel); } Users_Label::addLabel($myLabel, $asUserId, null, null, $asUserId2, true); } foreach ($raw_userIds as $userId) { Users_Contact::addContact($asUserId, "Streams/invited", $userId, null, false, true); Users_Contact::addContact($asUserId, "Streams/invited/{$stream->type}", $userId, null, false, true); Users_Contact::addContact($userId, "Streams/invitedMe", $asUserId, null, false, true); Users_Contact::addContact($userId, "Streams/invitedMe/{$stream->type}", $asUserId, null, false, true); if ($label) { Users_Contact::addContact($publisherId, $label, $userId, null, $asUserId2, true); } if ($myLabel) { Users_Contact::addContact($asUserId, $label, $userId, null, $asUserId2, true); } } // let node handle the rest, and get the result $displayName = isset($options['displayName']) ? $options['displayName'] : Streams::displayName($asUser); $params = array("Q/method" => "Streams/Stream/invite", "invitingUserId" => $asUserId, "username" => $asUser->username, "userIds" => Q::json_encode($userIds), "stream" => Q::json_encode($stream->toArray()), "appUrl" => $appUrl, "label" => $label, "myLabel" => $myLabel, "readLevel" => $readLevel, "writeLevel" => $writeLevel, "adminLevel" => $adminLevel, "displayName" => $displayName, "expiry" => $expiry); if (!empty($template)) { $params['template'] = $template; $params['batchName'] = $batchName; } $result = Q_Utils::queryInternal('Q/node', $params); return array('success' => $result, 'invited' => $userIds, 'statuses' => $statuses, 'identifierType' => $identifierType, 'alreadyParticipating' => $total - $to_invite); }