function Users_user_validate() { Q_Valid::nonce(true); $type = isset($_REQUEST['identifierType']) ? $_REQUEST['identifierType'] : Q_Config::get("Users", "login", "identifierType", "email,mobile"); $parts = explode(',', $type); $accept_mobile = true; $expected = 'email address or mobile number'; $fields = array('emailAddress', 'mobileNumber', 'identifier'); if (count($parts) === 1) { if ($parts[0] === 'email') { $expected = 'email address'; $fields = array('emailAddress', 'identifier'); $accept_mobile = false; } else { if ($parts[0] === 'mobile') { $expected = 'mobile number'; $fields = array('mobileNumber', 'identifier'); } } } if (!isset($_REQUEST['identifier'])) { throw new Q_Exception("a valid {$expected} is required", $fields); } if (!Q_Valid::email($_REQUEST['identifier'])) { if (!$accept_mobile) { throw new Q_Exception("a valid {$expected} is required", $fields); } if (!Q_Valid::phone($_REQUEST['identifier'])) { throw new Q_Exception("a valid {$expected} is required", $fields); } } }
function Users_activate_validate() { $uri = Q_Dispatcher::uri(); $emailAddress = Q::ifset($_REQUEST, 'e', $uri->emailAddress); $mobileNumber = Q::ifset($_REQUEST, 'm', $uri->mobileNumber); if ($emailAddress && !Q_Valid::email($emailAddress, $e_normalized, array('no_ip' => 'false'))) { throw new Q_Exception_WrongValue(array('field' => 'email', 'range' => 'a valid email address'), 'emailAddress'); } if ($mobileNumber && !Q_Valid::phone($mobileNumber, $m_normalized)) { throw new Q_Exception_WrongValue(array('field' => 'mobile phone', 'range' => 'a valid phone number'), 'mobileNumber'); } if ($emailAddress or $mobileNumber) { if (empty($_REQUEST['code'])) { throw new Q_Exception("The activation code is missing"); } } else { throw new Q_Exception("The contact information is missing"); } if (!empty($e_normalized)) { Users::$cache['emailAddress'] = $e_normalized; } if (!empty($m_normalized)) { Users::$cache['mobileNumber'] = $m_normalized; } }
function Users_user_validate() { if (isset($_REQUEST['userIds']) or isset($_REQUEST['batch'])) { return; } $type = isset($_REQUEST['identifierType']) ? $_REQUEST['identifierType'] : Q_Config::get("Users", "login", "identifierType", "email,mobile"); $parts = explode(',', $type); $accept_mobile = true; $expected = 'email address or mobile number'; $fields = array('emailAddress', 'mobileNumber', 'identifier'); if (count($parts) === 1) { if ($parts[0] === 'email') { $expected = 'email address'; $fields = array('emailAddress', 'identifier'); $accept_mobile = false; } else { if ($parts[0] === 'mobile') { $expected = 'mobile number'; $fields = array('mobileNumber', 'identifier'); } } } if (!isset($_REQUEST['identifier'])) { throw new Q_Exception("a valid {$expected} is required", $fields); } if (!Q_Valid::email($_REQUEST['identifier'])) { if (!$accept_mobile) { throw new Q_Exception("a valid {$expected} is required", $fields); } if (!Q_Valid::phone($_REQUEST['identifier'])) { throw new Q_Exception("a valid {$expected} is required", $fields); } } $identifier = Users::requestedIdentifier($type); // check our db if ($user = Users::userFromContactInfo($type, $identifier)) { $verified = !!Users::identify($type, $identifier); return array('exists' => $user->id, 'verified' => $verified, 'username' => $user->username, 'icon' => $user->icon, 'passphrase_set' => !empty($user->passphraseHash), 'fb_uid' => $user->fb_uid ? $user->fb_uid : null); } if ($type === 'email') { $email = new Users_Email(); Q_Valid::email($identifier, $normalized); $email->address = $normalized; $exists = $email->retrieve(); } else { if ($type === 'mobile') { $mobile = new Users_Mobile(); Q_Valid::phone($identifier, $normalized); $mobile->number = $normalized; $exists = $mobile->retrieve(); } } if (empty($exists) and Q_Config::get('Users', 'login', 'noRegister', false)) { $nicetype = $type === 'email' ? 'email address' : 'mobile number'; throw new Q_Exception("This {$nicetype} was not registered", array('identifier')); } }
function Users_user_response_data($params) { $identifier = Users::requestedIdentifier($type); // check our db if ($user = Users::userFromContactInfo($type, $identifier)) { $verified = !!Users::identify($type, $identifier); return array('exists' => $user->id, 'verified' => $verified, 'username' => $user->username, 'icon' => $user->icon, 'passphrase_set' => !empty($user->passphraseHash), 'fb_uid' => $user->fb_uid ? $user->fb_uid : null); } if ($type === 'email') { $email = new Users_Email(); Q_Valid::email($identifier, $normalized); $email->address = $normalized; $exists = $email->retrieve(); } else { if ($type === 'mobile') { $mobile = new Users_Mobile(); Q_Valid::phone($identifier, $normalized); $mobile->number = $normalized; $exists = $mobile->retrieve(); } } if (empty($exists) and Q_Config::get('Users', 'login', 'noRegister', false)) { $nicetype = $type === 'email' ? 'email address' : 'mobile number'; throw new Q_Exception("This {$nicetype} was not registered", array('identifier')); } // Get Gravatar info // WARNING: INTERNET_REQUEST $hash = md5(strtolower(trim($identifier))); $thumbnailUrl = Q_Request::baseUrl() . "/action.php/Users/thumbnail?hash={$hash}&size=80&type=" . Q_Config::get('Users', 'login', 'iconType', 'wavatar'); $json = @file_get_contents("http://www.gravatar.com/{$hash}.json"); $result = json_decode($json, true); if ($result) { if ($type === 'email') { $result['emailExists'] = !empty($exists); } else { if ($type === 'mobile') { $result['mobileExists'] = !empty($exists); } } return $result; } // otherwise, return default $email_parts = explode('@', $identifier, 2); $result = array("entry" => array(array("id" => "571", "hash" => "357a20e8c56e69d6f9734d23ef9517e8", "requestHash" => "357a20e8c56e69d6f9734d23ef9517e8", "profileUrl" => "http://gravatar.com/test", "preferredUsername" => ucfirst($email_parts[0]), "thumbnailUrl" => $thumbnailUrl, "photos" => array(), "displayName" => "", "urls" => array()))); if ($type === 'email') { $result['emailExists'] = !empty($exists); } else { $result['mobileExists'] = !empty($exists); } if ($terms_label = Users::termsLabel('register')) { $result['termsLabel'] = $terms_label; } return $result; }
function Streams_user_response_data($params) { $identifier = Users::requestedIdentifier($type); $hash = md5(strtolower(trim($identifier))); $icon = Q_Config::get('Users', 'register', 'icon', 'leaveDefault', false) ? $url = "plugins/Users/img/icons/default/80.png" : Q_Request::baseUrl() . "/action.php/Users/thumbnail?hash={$hash}&size=80&type=" . Q_Config::get('Users', 'login', 'iconType', 'wavatar'); // check our db if ($user = Users::userFromContactInfo($type, $identifier)) { $displayname = Streams::displayName($user); $verified = !!Users::identify($type, $identifier); return array('exists' => $user->id, 'verified' => $verified, 'username' => $user->username, 'displayName' => $displayname, 'icon' => $verified ? $icon : $user->icon, 'passphrase_set' => !empty($user->passphraseHash), 'fb_uid' => $user->fb_uid ? $user->fb_uid : null); } if ($type === 'email') { $email = new Users_Email(); Q_Valid::email($identifier, $normalized); $email->address = $normalized; $exists = $email->retrieve(); } else { if ($type === 'mobile') { $mobile = new Users_Mobile(); Q_Valid::phone($identifier, $normalized); $mobile->number = $normalized; $exists = $mobile->retrieve(); } } if (empty($exists) and Q_Config::get('Users', 'login', 'noRegister', false)) { $nicetype = $type === 'email' ? 'email address' : 'mobile number'; throw new Q_Exception("This {$nicetype} was not registered", array('identifier')); } $result = array("entry" => array(array("thumbnailUrl" => $icon))); if ($type === 'email') { $result['emailExists'] = !empty($exists); } else { $result['mobileExists'] = !empty($exists); } if ($terms_label = Users::termsLabel('register')) { $result['termsLabel'] = $terms_label; } return $result; }
function Users_activate_objects_mobile($mobileNumber, &$mobile) { Q_Response::removeNotice('Users/activate/objects'); $mobile = new Users_Mobile(); if (!Q_Valid::phone($mobileNumber, $normalized)) { return; } $mobile->number = $normalized; if (!$mobile->retrieve()) { throw new Q_Exception_MissingRow(array('table' => 'mobile phone', 'criteria' => "number {$normalized}")); } $user = Users::loggedInUser(); if ($user) { if ($user->id != $mobile->userId) { throw new Q_Exception("You are logged in as a different user. Please log out and click the link again."); } } else { $user = new Users_User(); $user->id = $mobile->userId; if (!$user->retrieve()) { throw new Q_Exception_MissingRow(array('table' => 'user', 'criteria' => 'id = ' . $user->id)); } } if ($mobile->activationCode != $_REQUEST['code']) { throw new Q_Exception("The activation code does not match. Did you get a newer message?", 'code'); } $timestamp = Users_Mobile::db()->getCurrentTimestamp(); if ($timestamp > Users_Mobile::db()->fromDateTime($mobile->activationCodeExpires)) { throw new Q_Exception("Activation code expired"); } if (Q_Request::method() !== 'POST' and empty($_REQUEST['p']) and isset($user->mobileNumber) and $user->mobileNumber == $mobile->number) { $displayName = Streams::displayName($user); Q_Response::setNotice('Users/activate/objects', "{$normalized} has already been activated for {$displayName}", true); return $user; } return $user; }
/** * Get the email address or mobile number from the request, if it can be deduced. * Note: it should still be tested for validity. * @method requestedIdentifier * @static * @param {&string} [$type=null] The identifier's type will be filled here. Might be "email", "mobile" or "token". * @return {string|null} The identifier, or null if one wasn't requested */ static function requestedIdentifier(&$type = null) { $identifier = null; $type = null; if (!empty($_REQUEST['identifier'])) { $identifier = $_REQUEST['identifier']; if (strpos($identifier, ':') !== false) { list($type, $token) = explode(':', $identifier); if ($type === 'token') { return $token; } } if (Q_Valid::email($identifier, $normalized)) { $type = 'email'; } else { if (Q_Valid::phone($identifier, $normalized)) { $type = 'mobile'; } } } if (!empty($_REQUEST['emailAddress'])) { $identifier = $_REQUEST['emailAddress']; Q_Valid::email($identifier, $normalized); $type = 'email'; } if (!empty($_REQUEST['mobileNumber'])) { $identifier = $_REQUEST['mobileNumber']; Q_Valid::phone($identifier, $normalized); $type = 'mobile'; } return isset($normalized) ? $normalized : $identifier; }
/** * Check identifier or array of identifiers and return users - existing or future * @method idsFromIdentifiers * @static * @param $asUserId {string} The user id of inviting user * @param $identifiers {string|array} * @param $statuses {array} Optional reference to an array to populate with $userId => $status pairs. * @return {array} The array of user ids */ static function idsFromIdentifiers($identifiers, &$statuses = array()) { if (empty($identifiers)) { return array(); } if (!is_array($identifiers)) { $identifiers = array_map('trim', explode(',', $identifiers)); } $users = array(); foreach ($identifiers as $identifier) { if (Q_Valid::email($identifier, $emailAddress)) { $ui_identifier = $emailAddress; $type = 'email'; } else { if (Q_Valid::phone($identifier, $mobileNumber)) { $ui_identifier = $mobileNumber; $type = 'mobile'; } else { throw new Q_Exception_WrongType(array('field' => 'identifier', 'type' => 'email address or mobile number'), array('emailAddress', 'mobileNumber')); } } $status = null; $users[] = $user = Users::futureUser($type, $ui_identifier, $status); $statuses[$user->id] = $status; } return array_map(array('Users_User', '_getId'), $users); }
/** * 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.label] label or an array of labels for adding publisher's contacts * @param {string|array} [$options.myLabel] label or an array of labels for adding logged-in user'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 * @param {array} [$options.asUserId=null] Invite as this user id * @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::fetch($asUserId, $publisherId, $streamName); if (!$stream) { throw new Q_Exception_MissingRow(array('table' => 'stream', 'criteria' => 'with that name'), 'streamName'); } $stream = reset($stream); // 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", $labels)); } $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)) { 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)) { 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)) { 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; // let node handle the rest, and get the result $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" => Q::ifset($options, 'label', null), "myLabel" => Q::ifset($options, 'myLabel', null), "readLevel" => $readLevel, "writeLevel" => $writeLevel, "adminLevel" => $adminLevel, "displayName" => isset($options['displayName']) ? $options['displayName'] : Streams::displayName($asUser), "expiry" => $expiry); if ($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); }
/** * @method sendMessage * @param {string} $view * The name of a view for the message. Fields are passed to this array. * @param {array} $fields=array() * The fields referenced in the subject and/or view * @param {array} $options=array() * Array of options. Can include:<br/> * "delay" => A delay, in milliseconds, to wait until sending email. Only works if Node server is listening. * @return {boolean} * @throws {Q_Exception_WrongType} * If phone number is invalid */ function sendMessage($view, $fields = array(), $options = array()) { /** * @event Users/sms/sendMessage {before} * @param {string} view * @param {array} fields * @param {array} options * @return {boolean} */ $result = Q::event('Users/sms/sendMessage', compact('view', 'fields', 'options'), 'before'); if (isset($result)) { return $result; } if (!Q_Valid::phone($this->number, $number)) { throw new Q_Exception_WrongType(array('field' => '$this->number', 'type' => 'mobile number', 'mobileNumber' => $this->number)); } $app = Q_Config::expect('Q', 'app'); $body = Q::view($view, $fields); $overrideLog = Q::event('Users/mobile/log', compact('mobileNumber', 'body'), 'before'); if (is_null($overrideLog) and $key = Q_Config::get('Users', 'mobile', 'log', 'key', null)) { Q::log("\nSent mobile message to {$this->number}:\n{$body}", $key); } $sent = false; if (!empty($options['delay'])) { // Try to use Node.js to send the message $sent = Q_Utils::sendToNode(array("Q/method" => "Users/sendMessage", "delay" => $options['delay'], "mobileNumber" => $number, "body" => $body, "options" => $options)); } if (!$sent) { $from = Q::ifset($options, 'from', Q_Config::get('Users', 'mobile', 'from', null)); if (!isset($from)) { // deduce from base url $url_parts = parse_url(Q_Request::baseUrl()); $domain = $url_parts['host']; $from = array("notifications@{$domain}", $domain); } $sid = Q_Config::get('Users', 'mobile', 'twilio', 'sid', null); $token = Q_Config::get('Users', 'mobile', 'twilio', 'token', null); if ($sid and $token) { $client = new Services_Twilio($sid, $token); $message = $client->account->sms_messages->create($from, $number, Q::view($view, $fields)); } else { if (!Q_Config::get('Users', 'email', 'smtp', null)) { Q_Response::setNotice("Q/mobile", "Please set up transport in Users/mobile/twilio as in docs", false); return true; } if (!is_array($from)) { $from = array($from, "{$app} activation"); } // Set up the default mail transport $host = Q_Config::get('Users', 'email', 'smtp', 'host', 'sendmail'); if ($host === 'sendmail') { $transport = new Zend_Mail_Transport_Sendmail('-f' . reset($from)); } else { if (is_array($host)) { $smtp = $host; $host = $smtp['host']; unset($smtp['host']); } else { $smtp = null; } $transport = new Zend_Mail_Transport_Smtp($host, $smtp); } $mail = new Zend_Mail(); $from_name = reset($from); $mail->setFrom(next($from), $from_name); $gateways = Q_Config::get('Users', 'mobile', 'gateways', array('at&t' => 'txt.att.net', 'sprint' => 'messaging.sprintpcs.com', 'verizon' => 'vtext.com', 't-mobile' => 'tmomail.net')); $number2 = substr($this->number, 2); foreach ($gateways as $k => $v) { $mail->addTo($number2 . '@' . $v); } $mail->setBodyText($body); try { $mail->send($transport); } catch (Exception $e) { throw new Users_Exception_MobileMessage(array('error' => $e->getMessage())); } } } /** * @event Users/sms/sendMessage {after} * @param {string} view * @param {array} fields * @param {array} options * @param {string} mail */ Q::event('Users/email/sendMessage', compact('view', 'fields', 'options', 'mail', 'app'), 'after'); return true; }