/** * Helper function for walking the Mustache token parse tree. * * @throws {InvalidArgumentException} upon encountering unknown token types. * * @param array $tree Parse tree of Mustache tokens * @param int $level (default: 0) * * @return {string} Generated PHP source code */ private function walk(array $tree, $level = 0) { $code = ''; $level++; foreach ($tree as $node) { switch ($node[Mustache_Tokenizer::TYPE]) { case Mustache_Tokenizer::T_SECTION: $code .= $this->section($node[Mustache_Tokenizer::NODES], $node[Mustache_Tokenizer::NAME], $node[Mustache_Tokenizer::INDEX], $node[Mustache_Tokenizer::END], $node[Mustache_Tokenizer::OTAG], $node[Mustache_Tokenizer::CTAG], $level); break; case Mustache_Tokenizer::T_INVERTED: $code .= $this->invertedSection($node[Mustache_Tokenizer::NODES], $node[Mustache_Tokenizer::NAME], $level); break; case Mustache_Tokenizer::T_PARTIAL: case Mustache_Tokenizer::T_PARTIAL_2: $code .= $this->partial($node[Mustache_Tokenizer::NAME], isset($node[Mustache_Tokenizer::INDENT]) ? $node[Mustache_Tokenizer::INDENT] : '', $level); break; case Mustache_Tokenizer::T_UNESCAPED: case Mustache_Tokenizer::T_UNESCAPED_2: $code .= $this->variable($node[Mustache_Tokenizer::NAME], false, $level); break; case Mustache_Tokenizer::T_COMMENT: break; case Mustache_Tokenizer::T_ESCAPED: $code .= $this->variable($node[Mustache_Tokenizer::NAME], true, $level); break; case Mustache_Tokenizer::T_TEXT: $code .= $this->text($node[Mustache_Tokenizer::VALUE], $level); break; default: throw new InvalidArgumentException('Unknown node type: ' . Q::json_encode($node)); } } return $code; }
function Streams_interests_response() { // serve a javascript file and tell client to cache it $app = Q_Config::expect('Q', 'app'); $communityId = Q::ifset($_REQUEST, 'communityId', $app); $tree = new Q_Tree(); $tree->load("files/Streams/interests/{$communityId}.json"); $categories = $tree->getAll(); foreach ($categories as $category => &$v1) { foreach ($v1 as $k2 => &$v2) { if (!Q::isAssociative($v2)) { ksort($v1); break; } ksort($v2); } } header('Content-Type: text/javascript'); header("Pragma: ", true); // 1 day header("Cache-Control: public, max-age=86400"); // 1 day $expires = date("D, d M Y H:i:s T", time() + 86400); header("Expires: {$expires}"); // 1 day $json = Q::json_encode($categories, true); echo "Q.setObject(['Q', 'Streams', 'Interests', 'all', '{$communityId}'], {$json});"; return false; }
/** * Used to create a new stream * * @param {array} $_REQUEST * @param {String} [$_REQUEST.title] Required. The title of the interest. * @param {String} [$_REQUEST.publisherId] Optional. Defaults to the app name. * @return {void} */ function Streams_interest_delete() { $user = Users::loggedInUser(true); $title = Q::ifset($_REQUEST, 'title', null); if (!isset($title)) { throw new Q_Exception_RequiredField(array('field' => 'title')); } $app = Q_Config::expect('Q', 'app'); $publisherId = Q::ifset($_REQUEST, 'publisherId', $app); $name = 'Streams/interest/' . Q_Utils::normalize($title); $stream = Streams::fetchOne(null, $publisherId, $name); if (!$stream) { throw new Q_Exception_MissingRow(array('table' => 'stream', 'criteria' => Q::json_encode(compact('publisherId', 'name')))); } $miPublisherId = $user->id; $miName = 'Streams/user/interests'; $myInterests = Streams::fetchOne($user->id, $miPublisherId, $miName); if (!$myInterests) { throw new Q_Exception_MissingRow(array('table' => 'stream', 'criteria' => Q::json_encode(array('publisherId' => $miPublisherId, 'name' => $miName)))); } $stream->leave(); Streams::unrelate($user->id, $user->id, 'Streams/user/interests', 'Streams/interest', $publisherId, $name, array('adjustWeights' => true)); Q_Response::setSlot('publisherId', $publisherId); Q_Response::setSlot('streamName', $name); /** * Occurs when the logged-in user has successfully removed an interest via HTTP * @event Streams/interest/delete {after} * @param {string} publisherId The publisher of the interest stream * @param {string} title The title of the interest * @param {Users_User} user The logged-in user * @param {Streams_Stream} stream The interest stream * @param {Streams_Stream} myInterests The user's "Streams/user/interests" stream */ Q::event("Streams/interest/remove", compact('publisherId', 'title', 'subscribe', 'user', 'stream', 'myInterests'), 'after'); }
function Broadcast_before_Streams_message_Broadcast($params) { extract($params); if (!empty($_REQUEST['link'])) { $parts = parse_url($_REQUEST['link']); if (empty($parts['host'])) { throw new Q_Exception_WrongType(array('field' => 'link', 'type' => 'a valid url'), 'link'); } } $content = array(); foreach (array('link', 'description', 'picture') as $field) { if (!empty($_REQUEST[$field])) { $content[$field] = $_REQUEST[$field]; } } if (!empty($_REQUEST['content'])) { $content['message'] = $_REQUEST['content']; } if (!$content) { throw new Q_Exception_RequiredField(array('field' => 'content'), 'content'); } // Manually adding a link for 'Manage or Remove' $appUrl = Q_Config::get('Users', 'facebookApps', 'Broadcast', 'url', ''); $content['actions'] = Q::json_encode(array(array('name' => 'Manage or Remove', 'link' => $appUrl))); $message->broadcast_instructions = Q::json_encode($content); }
function Websites_seo_post() { if (empty($_REQUEST['streamName'])) { throw new Q_Exception_RequiredField(array('field' => 'streamName')); } $prefix = "Websites/seo/"; if (substr($_REQUEST['streamName'], 0, strlen($prefix)) !== $prefix) { throw new Q_Exception_WrongValue(array('field' => 'streamName', 'range' => "string beginning with {$prefix}")); } $user = Users::loggedInUser(true); $publisherId = Users::communityId(); $type = "Websites/seo"; if (!Streams::isAuthorizedToCreate($user->id, $publisherId, $type)) { throw new Users_Exception_NotAuthorized(); } $stream = new Streams_Stream($publisherId); $stream->publisherId = $publisherId; $stream->name = $_REQUEST['streamName']; $stream->type = $type; if (isset($_REQUEST['uri'])) { $stream->setAttribute('uri', $_REQUEST['uri']); } $stream->save(); $stream->post($user->id, array('type' => 'Streams/created', 'content' => '', 'instructions' => Q::json_encode($stream->toArray())), true); $stream->subscribe(); // autosubscribe to streams you yourself create, using templates Q_Response::setSlot('stream', $stream->exportArray()); }
function Users_before_Q_session_save($params) { $row = $params['row']; $row->deviceId = ""; $row->timeout = 0; $row->content = isset($_SESSION) ? Q::json_encode($_SESSION, JSON_FORCE_OBJECT) : "{}"; $row->duration = Q_Config::get('Q', 'session', 'durations', Q_Request::formFactor(), Q_Config::expect('Q', 'session', 'durations', 'session')); }
/** * This tool contains functionality to show things in columns * @class Q columns * @constructor * @param {array} [options] Provide options for this tool * @param {array} [options.animation] For customizing animated transitions * @param {integer} [options.animation.duration] The duration of the transition in milliseconds, defaults to 500 * @param {array} [options.animation.hide] The css properties in "hide" state of animation * @param {array} [options.animation.show] The css properties in "show" state of animation * @param {array} [options.back] For customizing the back button on mobile * @param {string} [options.back.src] The src of the image to use for the back button * @param {boolean} [options.back.triggerFromTitle] Whether the whole title would be a trigger for the back button. Defaults to true. * @param {boolean} [options.back.hide] Whether to hide the back button. Defaults to false, but you can pass true on android, for example. * @param {array} [options.close] For customizing the back button on desktop and tablet * @param {string} [options.close.src] The src of the image to use for the close button * @param {string} [options.title] You can put a default title for all columns here (which is shown as they are loading) * @param {string} [options.column] You can put a default content for all columns here (which is shown as they are loading) * @param {array} [options.clickable] If not null, enables the Q/clickable tool with options from here. Defaults to null. * @param {array} [options.scrollbarsAutoHide] If not null, enables Q/scrollbarsAutoHide functionality with options from here. Enabled by default. * @param {boolean} [options.fullscreen] Whether to use fullscreen mode on mobile phones, using document to scroll instead of relying on possibly buggy "overflow" CSS implementation. Defaults to true on Android, false everywhere else. * @param {array} [options.columns] In PHP only, an array of $name => $column pairs, where $column is in the form array('title' => $html, 'content' => $html, 'close' => true) * @return {string} */ function Q_columns_tool($options) { $jsOptions = array('animation', 'back', 'close', 'title', 'scrollbarsAutoHide', 'fullscreen'); Q_Response::setToolOptions(Q::take($options, $jsOptions)); if (!isset($options['columns'])) { return ''; } Q_Response::addScript('plugins/Q/js/tools/columns.js'); Q_Response::addStylesheet('plugins/Q/css/columns.css'); $result = '<div class="Q_columns_container Q_clearfix">'; $columns = array(); $i = 0; $closeSrc = Q::ifset($options, 'close', 'src', 'plugins/Q/img/x.png'); $backSrc = Q::ifset($options, 'back', 'src', 'plugins/Q/img/back-v.png'); foreach ($options['columns'] as $name => $column) { $close = Q::ifset($column, 'close', $i > 0); $Q_close = Q_Request::isMobile() ? 'Q_close' : 'Q_close Q_back'; $closeHtml = !$close ? '' : (Q_Request::isMobile() ? '<div class="Q_close Q_back">' . Q_Html::img($backSrc, 'Back') . '</div>' : '<div class="Q_close">' . Q_Html::img($closeSrc, 'Close') . '</div>'); $n = Q_Html::text($name); $columnClass = 'Q_column_' . Q_Utils::normalize($name) . ' Q_column_' . $i; if (isset($column['html'])) { $html = $column['html']; $columns[] = <<<EOT \t<div class="Q_columns_column {$columnClass}" data-index="{$i}" data-name="{$n}"> \t\t{$html} \t</div> EOT; } else { $titleHtml = Q::ifset($column, 'title', '[title]'); $columnHtml = Q::ifset($column, 'column', '[column]'); $classes = $columnClass . ' ' . Q::ifset($column, 'class', ''); $attrs = ''; if (isset($column['data'])) { $json = Q::json_encode($column['data']); $attrs = 'data-more="' . Q_Html::text($json) . '"'; foreach ($column['data'] as $k => $v) { $attrs .= 'data-' . Q_Html::text($k) . '="' . Q_Html::text($v) . '" '; } } $data = Q::ifset($column, 'data', ''); $columns[] = <<<EOT \t<div class="Q_columns_column {$classes}" data-index="{$i}" data-name="{$n}" {$attrs}> \t\t<div class="Q_columns_title"> \t\t\t{$closeHtml} \t\t\t<h2 class="Q_title_slot">{$titleHtml}</h2> \t\t</div> \t\t<div class="Q_column_slot">{$columnHtml}</div> \t</div> EOT; } ++$i; } $result .= "\n" . implode("\n", $columns) . "\n</div>"; return $result; }
function Platform_api_response() { $result = array(); if ($_REQUEST['discover']) { $discover = $_REQUEST['discover']; if (is_string($discover)) { $discover = explode(',', $_REQUEST['discover']); } $discover = array_flip($discover); if (isset($discover['user'])) { $result['user'] = '******'; } if (isset($discover['contacts'])) { $result['contacts'] = array('bhbsneuc' => array('labels' => array(1, 4)), 'bgeoekat' => array('labels' => array(1, 7))); } } $json = Q::json_encode($result); $referer = $_SERVER['HTTP_REFERER']; $parts = parse_url($referer); $origin = Q::json_encode($parts['scheme'] . '://' . $parts['host']); $appUrl = Q::json_encode(Q_Request::baseUrl()); echo <<<EOT <!doctype html> <html> <head> <title>Qbix Platform</title> \t\t<script type="text/javascript"> \t\twindow.addEventListener("message", receiveMessage, false); \t\tfunction receiveMessage(event) { \t\t\tvar request = event.data; \t\t\tvar response = ''; \t\t\tif (!request.method) { \t\t\t\tresponse = {"error": "Missing method"}; \t\t\t} \t\t\tif (request.appUrl.substr(0, event.origin.length) !== event.origin) { \t\t\t\tresponse = {"error": "Origin doesn't match"}; \t\t\t} else { \t\t\t\tresponse = {$json}; \t\t\t} \t\t\twindow.parent.postMessage(response, {$origin}); \t\t} \t\tvar ExposedMethods = function () { \t \t\t}; \t\t</script> </head> <body> </body> </html> EOT; return false; }
/** * Generates a form with inputs that modify various streams * @class Streams form * @constructor * @param {array} $options * An associative array of parameters, containing: * @param {array} [$options.fields] an associative array of $id => $fieldinfo pairs, * where $id is the id to append to the tool's id, to generate the input's id, * and fieldinfo is either an associative array with the following fields, * or a regular array consisting of fields in the following order: * "publisherId" => Required. The id of the user publishing the stream * "streamName" => Required. The name of the stream * "field" => The stream field to edit, or "attribute:$attributeName" for an attribute. * "input" => The type of the input (@see Q_Html::smartTag()) * "attributes" => Additional attributes for the input * "options" => options for the input (if type is "select", "checkboxes" or "radios") * "params" => array of extra parameters to Q_Html::smartTag */ function Streams_form_tool($options) { $fields = Q::ifset($options, 'fields', array()); $defaults = array('publisherId' => null, 'streamName' => null, 'field' => null, 'type' => 'text', 'attributes' => array(), 'value' => array(), 'options' => array(), 'params' => array()); $sections = array(); $hidden = array(); $contents = ''; foreach ($fields as $id => $field) { if (Q::isAssociative($field)) { $r = Q::take($field, $defaults); } else { $c = count($field); if ($c < 4) { throw new Q_Exception("Streams/form tool: field needs at least 4 values"); } $r = array('publisherId' => $field[0], 'streamName' => $field[1], 'field' => $field[2], 'type' => $field[3], 'attributes' => isset($field[4]) ? $field[4] : array(), 'value' => isset($field[5]) ? $field[5] : '', 'options' => isset($field[6]) ? $field[6] : null, 'params' => isset($field[7]) ? $field[7] : null); } $r['attributes']['name'] = "input_{$id}"; if (!isset($r['type'])) { var_dump($r['type']); exit; } $stream = Streams::fetchOne(null, $r['publisherId'], $r['streamName']); if ($stream) { if (substr($r['field'], 0, 10) === 'attribute:') { $attribute = trim(substr($r['field'], 10)); $value = $stream->get($attribute, $r['value']); } else { $field = $r['field']; $value = $stream->{$field}; } } else { $value = $r['value']; } $tag = Q_Html::smartTag($r['type'], $r['attributes'], $value, $r['options'], $r['params']); $class1 = 'publisherId_' . Q_Utils::normalize($r['publisherId']); $class2 = 'streamName_' . Q_Utils::normalize($r['streamName']); $contents .= "<span class='Q_before {$class1} {$class2}'></span>" . Q_Html::tag('span', array('data-publisherId' => $r['publisherId'], 'data-streamName' => $r['streamName'], 'data-field' => $r['field'], 'data-type' => $r['type'], 'class' => "{$class1} {$class2}"), $tag); $hidden[$id] = array(!!$stream, $r['publisherId'], $r['streamName'], $r['field']); } $contents .= Q_Html::hidden(array('inputs' => Q::json_encode($hidden))); return Q_Html::form('Streams/form', 'post', array(), $contents); // // $fields = array('onSubmit', 'onResponse', 'onSuccess', 'slotsToRequest', 'loader', 'contentElements'); // Q_Response::setToolOptions(Q::take($options, $fields)); // Q_Response::addScript('plugins/Q/js/tools/form.js'); // Q_Response::addStylesheet('plugins/Q/css/form.css'); // return $result; }
function Q_scripts_combine() { $environment = Q_Config::get('Q', 'environment', false); if (!$environment) { return "Config field 'Q'/'environment' is empty"; } $files = Q_Config::get('Q', 'environments', $environment, 'files', Q_Config::get('Q', 'environments', '*', 'files', false)); if (empty($files)) { return "Config field 'Q'/'environments'/'{$environment}'/files is empty"; } $filters = Q_Config::get('Q', 'environments', $environment, 'filters', Q_Config::get('Q', 'environments', '*', 'filters', false)); if (empty($filters)) { return "Config field 'Q'/'environments'/'{$environment}'/filters is empty"; } $combined = array(); foreach ($files as $src => $dest) { $f = Q_Uri::filenameFromUrl(Q_Html::themedUrl($src, true)); if (!file_exists($f)) { return "Aborting: File corresponding to {$src} doesn't exist"; } $content = file_get_contents($f); $combined[$dest][$src] = $content; } foreach ($combined as $dest => $parts) { $df = Q_Uri::filenameFromUrl(Q_Html::themedUrl($dest)); $ext = pathinfo($df, PATHINFO_EXTENSION); echo "Writing {$df}\n"; if (!empty($filters)) { foreach ($filters as $e => $filter) { if ($ext !== $e) { continue; } $p = !empty($filter['params']) ? Q::json_encode($filter['params']) : ''; echo "\t" . $filter['handler'] . "{$p}\n"; foreach ($parts as $src => $part) { echo "\t\t{$src}\n"; } $params = compact('dest', 'parts'); if (!empty($filter['params'])) { $params = array_merge($params, $filter['params']); } $content = Q::event($filter['handler'], $params); } } file_put_contents($df, $content); } echo "Success."; }
function Q_after_Q_tool_render($params, &$result) { $info = $params['info']; $extra = $params['extra']; if (!is_array($extra)) { $extra = array(); } $id_prefix = Q_Html::getIdPrefix(); $tool_ids = Q_Html::getToolIds(); $tag = Q::ifset($extra, 'tag', 'div'); if (empty($tag)) { Q_Html::popIdPrefix(); return; } $classes = ''; $data_options = ''; $count = count($info); foreach ($info as $name => $opt) { $classes = ($classes ? "{$classes} " : $classes) . implode('_', explode('/', $name)) . '_tool'; $options = Q_Response::getToolOptions($name); if (isset($options)) { $friendly_options = str_replace(array('"', '\\/'), array('"', '/'), Q_Html::text(Q::json_encode($options))); } else { $friendly_options = ''; } $normalized = Q_Utils::normalize($name, '-'); if (isset($options) or $count > 1) { $id = $tool_ids[$name]; $id_string = $count > 1 ? "{$id} " : ''; $data_options .= " data-{$normalized}='{$id_string}{$friendly_options}'"; } $names[] = $name; } if (isset($extra['classes'])) { $classes .= ' ' . $extra['classes']; } $attributes = isset($extra['attributes']) ? ' ' . Q_Html::attributes($extra['attributes']) : ''; $data_retain = !empty($extra['retain']) || Q_Response::shouldRetainTool($id_prefix) ? " data-Q-retain=''" : ''; $data_replace = !empty($extra['replace']) || Q_Response::shouldReplaceWithTool($id_prefix) ? " data-Q-replace=''" : ''; $names = $count === 1 ? ' ' . key($info) : 's ' . implode(" ", $names); $ajax = Q_Request::isAjax(); $result = "<{$tag} id='{$id_prefix}tool' " . "class='Q_tool {$classes}'{$data_options}{$data_retain}{$data_replace}{$attributes}>" . "{$result}</{$tag}>"; if (!Q_Request::isAjax()) { $result = "<!--\nbegin tool{$names}\n-->{$result}<!--\nend tool{$names} \n-->"; } Q_Html::popIdPrefix(); }
function Streams_interests_response() { // serve a javascript file and tell client to cache it $communityId = Q::ifset($_REQUEST, 'communityId', Users::communityId()); $interests = Streams::interests($communityId); header('Content-Type: text/javascript'); header("Pragma: ", true); // 1 day header("Cache-Control: public, max-age=86400"); // 1 day $expires = date("D, d M Y H:i:s T", time() + 86400); header("Expires: {$expires}"); // 1 day $json = Q::json_encode($interests, true); echo "Q.setObject(['Q', 'Streams', 'Interests', 'all', '{$communityId}'], {$json});"; return false; }
function Streams_after_Streams_message_Streams_unrelatedTo($params) { $message = $params['message']; $type = $message->getInstruction('type', null); $stream = $params['stream']; $rtypes = Q_Config::get('Streams', 'categorize', 'relationTypes', array()); $stypes = Q_Config::get('Streams', 'categorize', 'streamTypes', array()); if (!in_array($type, $rtypes) or !in_array($stream->type, $stypes)) { return; } $c = new Streams_Category(); $c->publisherId = $stream->publisherId; $c->streamName = $stream->name; $fromPublisherId = $message->getInstruction('fromPublisherId', null); $fromStreamName = $message->getInstruction('fromStreamName', null); if (!isset($fromPublisherId) or !isset($fromStreamName)) { return; } // Begin database transaction $relatedTo = $c->retrieve(null, array('ignoreCache' => true, 'begin' => true)) ? json_decode($c->relatedTo, true) : array(); if (isset($relatedTo[$type])) { foreach ($relatedTo[$type] as $weight => $info) { if ($info[0] === $fromPublisherId and $info[1] === $fromStreamName) { unset($relatedTo[$type][$weight]); break; } } $o = $message->getInstruction('options', null); $w = $message->getInstruction('weight', null); if (!empty($o['adjustWeights'])) { $rt = array(); foreach ($relatedTo[$type] as $weight => $info) { if ($weight > $w) { $rt[$weight - 1] = $info; } else { $rt[$weight] = $info; } } $relatedTo[$type] = $rt; } } $c->relatedTo = Q::json_encode($relatedTo); $c->save(); // End database transaction }
function Users_before_Q_session_save($params) { if (empty($params['row'])) { return; } $row = $params['row']; $user = Users::loggedInUser(false, false); $userId = $user ? $user->id : null; if (Q::ifset($row, 'userId', null) !== $userId) { $row->userId = $userId; } $row->content = isset($_SESSION) ? Q::json_encode($_SESSION, JSON_FORCE_OBJECT) : "{}"; if (empty($params['inserting'])) { return; } $row->deviceId = ""; $row->timeout = 0; }
function Streams_after_Streams_message_Streams_updateRelateTo($params) { $message = $params['message']; $type = $message->getInstruction('type', null); $stream = $params['stream']; $rtypes = Q_Config::get('Streams', 'categorize', 'relationTypes', array()); $stypes = Q_Config::get('Streams', 'categorize', 'streamTypes', array()); if (!in_array($type, $rtypes) or !in_array($stream->type, $stypes)) { return; } $c = new Streams_Category(); $c->publisherId = $stream->publisherId; $c->streamName = $stream->name; $fromPublisherId = $message->getInstruction('fromPublisherId', null); $fromStreamName = $message->getInstruction('fromStreamName', null); if (!isset($fromPublisherId) or !isset($fromStreamName)) { return; } // Begin database transaction $relatedTo = $c->retrieve(null, array('ignoreCache' => true, 'begin' => true)) ? json_decode($c->relatedTo, true) : array(); $weight = (double) $message->getInstruction('weight', null); $previousWeight = (double) $message->getInstruction('previousWeight', null); $adjustWeightsBy = $message->getInstruction('adjustWeightsBy', null); if (isset($relatedTo[$type])) { $prev = $relatedTo[$type][$previousWeight]; $rt = array(); foreach ($relatedTo[$type] as $w => $info) { if ($weight < $previousWeight and ($w < $weight or $previousWeight <= $w)) { $rt[$w] = $info; } else { if ($weight >= $previousWeight and ($w <= $previousWeight or $weight < $w)) { $rt[$w] = $info; } else { $rt[$w + $adjustWeightsBy] = $info; } } } $rt[$weight] = $prev; $relatedTo[$type] = $rt; } $c->relatedTo = Q::json_encode($relatedTo); $c->save(); // End database transaction }
function Users_after_Q_session_write($params) { Q::$state['session'] = true; if (!$params['changed']) { return; } // Q::autoload('Db'); // Q::autoload('Db_Mysql'); // Q::autoload('Db_Result'); // Q::autoload('Db_Expression'); // Q::autoload('Db_Query'); // Q::autoload('Db_Query_Mysql'); // Q::autoload('Db_Row'); // Q::autoload('Base_Users_Session'); // Q::autoload('Base_Users'); // Q::autoload('Users'); Q::autoload('Q_Utils'); Q::autoload('Q_Config'); Q::autoload('Q_Session'); $id = Q_Session::id(); if (!$id) { return; } $parts = explode('-', $id); $duration = count($parts) > 1 ? $parts[0] : 0; $content = Q::json_encode($_SESSION, JSON_FORCE_OBJECT); if (Users::$loggedOut) { Q_Utils::sendToNode(array("Q/method" => "Users/session", "sessionId" => $id, "content" => null, "duration" => $duration)); } else { if (Q_Session::id() and !empty($_SERVER['HTTP_HOST'])) { try { Q_Utils::sendToNode(array("Q/method" => "Users/session", "sessionId" => $id, "content" => $content, "duration" => $duration)); } catch (Exception $e) { // don't throw here, it would only result in a mysterious fatal error } } } }
function Streams_after_Streams_message_Streams_relatedTo($params) { $message = $params['message']; $type = $message->getInstruction('type', null); $stream = $params['stream']; $rtypes = Q_Config::get('Streams', 'categorize', 'relationTypes', array()); $stypes = Q_Config::get('Streams', 'categorize', 'streamTypes', array()); if (!in_array($type, $rtypes) or !in_array($stream->type, $stypes)) { return; } $c = new Streams_Category(); $c->publisherId = $stream->publisherId; $c->streamName = $stream->name; $fromPublisherId = $message->getInstruction('fromPublisherId', null); $fromStreamName = $message->getInstruction('fromStreamName', null); if (!isset($fromPublisherId) or !isset($fromStreamName)) { return; } // Begin database transaction $relatedTo = $c->retrieve(null, array('ignoreCache' => true, 'begin' => true)) ? json_decode($c->relatedTo, true) : array(); $weight = (double) $message->getInstruction('weight', null); if (!isset($weight)) { $rt = new Streams_RelatedTo(); $rt->toPublisherId = $stream->publisherId; $rt->toStreamName = $stream->name; $rt->type = $type; $rt->fromPublisherId = $fromPublisherId; $rt->fromStreamName = $fromStreamName; $rt->retrieve(null, null, array('ignoreCache' => true)); $weight = $rt->weight; } $fs = Streams::fetchOne($message->byUserId, $fromPublisherId, $fromStreamName); $weight = floor($weight); $relatedTo[$type][$weight] = array($fromPublisherId, $fromStreamName, $fs->title, $fs->icon); $c->relatedTo = Q::json_encode($relatedTo); $c->save(false, true); // End database transaction }
function Users_after_Q_session_destroy($params) { Q::$state['session'] = true; // Q::autoload('Db'); // Q::autoload('Db_Mysql'); // Q::autoload('Db_Result'); // Q::autoload('Db_Expression'); // Q::autoload('Db_Query'); // Q::autoload('Db_Query_Mysql'); // Q::autoload('Db_Row'); // Q::autoload('Base_Users_Session'); // Q::autoload('Base_Users'); // Q::autoload('Users'); Q::autoload('Q_Utils'); Q::autoload('Q_Config'); Q::autoload('Q_Session'); $id = Q_Session::id(); if (!$id) { return; } $content = Q::json_encode($_SESSION, JSON_FORCE_OBJECT); Q_Utils::sendToNode(array("Q/method" => "Users/session", "sessionId" => $id, "content" => null, "updatedTime" => null, "destroyed" => true)); }
/** * Adds a link to someone who is not yet a user * @method addLink * @static * @param {string} $address Could be email address, mobile number, etc. * @param {string} [$type=null] One of 'email', 'mobile', 'email_hashed', 'mobile_hashed', 'facebook', or 'twitter' for now. * If not indicated, the function tries to guess by using Q_Valid functions. * @param {array} [$extraInfo=array()] Associative array of information you have imported * from the address book. Should contain at least the keys: * * * "firstName" => the imported first name * * "lastName" => the imported last name * * "labels" => array of the imported names of the contact groups to add this user to once they sign up * * @return {boolean|integer} Returns true if the link row was created * Or returns a string $userId if user already exists and has verified this address. * @throws {Q_Exception_WrongValue} If $address is not a valid id * @throws {Users_Exception_NotLoggedIn} If user is not logged in */ static function addLink($address, $type = null, $extraInfo = array()) { // process the address first $address = trim($address); $ui_type = $type; switch ($type) { case 'email': if (!Q_Valid::email($address, $normalized)) { throw new Q_Exception_WrongValue(array('field' => 'address', 'range' => 'email address')); } $ui_type = 'email_hashed'; $ui_value = 'email_hashed:' . Q_Utils::hash($normalized); break; case 'email_hashed': // Assume that the $address was already hashed in the standard way // see Q_Utils::hash $ui_value = "email_hashed:{$address}"; break; case 'mobile': if (!isset($options['hashed']) and !Q_Valid::phone($address, $normalized)) { throw new Q_Exception_WrongValue(array('field' => 'address', 'range' => 'phone number')); } $ui_type = 'mobile_hashed'; $ui_value = 'mobile_hashed:' . Q_Utils::hash($normalized); break; case 'mobile_hashed': // Assume that the $address was already hashed in the standard way // see Q_Utils::hash $ui_value = "mobile_hashed:{$address}"; break; case 'facebook': if (!isset($options['hashed']) and !is_numeric($address)) { throw new Q_Exception_WrongValue(array('field' => 'address', 'range' => 'facebook uid')); } $ui_value = "facebook:{$address}"; $normalized = $address; break; case 'twitter': if (!isset($options['hashed']) and !is_numeric($address)) { throw new Q_Exception_WrongValue(array('field' => 'address', 'range' => 'twitter uid')); } $ui_value = "twitter:{$address}"; $normalized = $address; break; default: if (Q_Valid::email($address, $normalized)) { $ui_type = 'email_hashed'; $ui_value = 'email_hashed:' . Q_Utils::hash($normalized); } else { if (Q_Valid::phone($address, $normalized)) { $ui_type = 'mobile_hashed'; $ui_value = 'mobile_hashed:' . Q_Utils::hash($normalized); } else { throw new Q_Exception_WrongValue(array('field' => 'type', 'range' => 'one of email, mobile, email_hashed, mobile_hashed, facebook, twitter')); } } break; } $user = Users::loggedInUser(true); // Check if the contact user already exists, and if so, add a contact instead of a link $ui = Users::identify($ui_type, $ui_value); if ($ui) { // Add a contact instead of a link $user->addContact($ui->userId, Q::ifset($extraInfo, 'labels', null)); return $user->id; } // Add a link if one isn't already there $link = new Users_Link(); $link->identifier = $ui_value; $link->userId = $user->id; if ($link->retrieve()) { return false; } $link->extraInfo = Q::json_encode($extraInfo); $link->save(); return true; }
function Q_exception_native($params) { extract($params); /** * @var Exception $exception */ if ($is_ajax = Q_Request::isAjax()) { $json = @Q::json_encode(array('errors' => Q_Exception::toArray(array($exception)))); $callback = Q_Request::callback(); switch (strtolower($is_ajax)) { case 'iframe': // Render an HTML layout for ajax if (!Q_Response::$batch) { header("Content-type: text/html"); } echo <<<EOT <!doctype html><html lang=en> <head><meta charset=utf-8><title>Q Result</title></head> <body> <script type="text/javascript"> window.result = function () { return {$json} }; </script> </body> </html> EOT; break; case 'json': // Render a JSON layout for ajax // Render a JSON layout for ajax default: header("Content-type: " . ($callback ? "application/javascript" : "application/json")); echo $callback ? "{$callback}({$json})" : $json; } } else { if (Q::textMode()) { echo Q_Exception::coloredString($exception); exit; } $message = $exception->getMessage(); $file = $exception->getFile(); $line = $exception->getLine(); if (is_callable(array($exception, 'getTraceAsStringEx'))) { $trace_string = $exception->getTraceAsStringEx(); } else { $trace_string = $exception->getTraceAsString(); } if ($exception instanceof Q_Exception_PhpError or !empty($exception->messageIsHtml)) { // do not sanitize $message } else { $message = Q_Html::text($message); } $content = "<h1 class='exception_message'>{$message}</h1>"; if (Q_Config::get('Q', 'exception', 'showFileAndLine', true)) { $content .= "<h3 class='exception_fileAndLine'>in {$file} ({$line})</h3>"; } if (Q_Config::get('Q', 'exception', 'showTrace', true)) { $content .= "<pre class='exception_trace'>{$trace_string}</pre>"; } $content .= str_repeat(' ', 512); // because of chrome $title = "Exception occurred"; $dashboard = ""; echo Q::view('Q/layout/html.php', compact('content', 'dashboard', 'title')); } $app = Q_Config::get('Q', 'app', null); $colored = Q_Exception::coloredString($exception); Q::log("{$app}: Exception in " . ceil(Q::milliseconds()) . "ms:\n\n{$colored}\n", null, true, array('maxLength' => 10000)); }
/** * Starts a recurring subscription * @param {Streams_Stream} $plan The subscription plan stream * @param {string} [$payments=null] The type of payments processor, could be "authnet" or "stripe". If omitted, the subscription proceeds without any payments. * @param {array} [$options=array()] Options for the subscription * @param {date} [$options.startDate=today] The start date of the subscription * @param {date} [$options.endDate=today+year] The end date of the subscription * @param {Users_User} [$options.user=Users::loggedInUser()] Allows us to set the user to subscribe * @param {Users_User} [$options.publisherId=Users::communityId()] Allows us to override the publisher to subscribe to * @param {string} [$options.description=null] description of the charge, to be sent to customer * @param {string} [$options.metadata=null] any additional metadata to store with the charge * @param {string} [$options.subscription=null] if this charge is related to a subscription stream * @param {string} [$options.subscription.publisherId] * @param {string} [$options.subscription.streamName] * @throws Assets_Exception_DuplicateTransaction * @throws Assets_Exception_HeldForReview * @throws Assets_Exception_ChargeFailed * @return {Streams_Stream} A stream of type 'Assets/subscription' representing this subscription */ static function startSubscription($plan, $payments = null, $options = array()) { if (!isset($options['user'])) { $options['user'] = Users::loggedInUser(true); } $app = Q_Config::expect('Q', 'app'); $user = Q::ifset($options, 'user', Users::loggedInUser(true)); $currency = 'USD'; // TODO: may want to implement support for currency conversion $startDate = Q::ifset($options, 'startDate', date("Y-m-d")); $startDate = date('Y-m-d', strtotime($startDate)); $months = $plan->getAttribute('months', 12); $amount = $plan->getAttribute('amount'); $endDate = date("Y-m-d", strtotime("-1 day", strtotime("+{$months} month", strtotime($startDate)))); $endDate = date('Y-m-d', strtotime($endDate)); $publisherId = Q::ifset($options, 'publisherId', Users::communityId()); $publisher = Users_User::fetch($publisherId); $streamName = "Assets/subscription/{$user->id}/{$plan->name}"; if ($subscription = Streams::fetchOne($publisherId, $publisherId, $streamName)) { return $subscription; // it already started } $attributes = Q::json_encode(array('payments' => $payments, 'planPublisherId' => $plan->publisherId, 'planStreamName' => $plan->name, 'startDate' => $startDate, 'endDate' => $endDate, 'months' => $months, 'amount' => $amount, 'currency' => $currency)); $stream = Streams::create($publisherId, $publisherId, "Assets/subscription", array('name' => $streamName, 'title' => $plan->title, 'readLevel' => Streams::$READ_LEVEL['none'], 'writeLevel' => Streams::$WRITE_LEVEL['none'], 'adminLevel' => Streams::$ADMIN_LEVEL['none'], 'attributes' => $attributes)); $access = new Streams_Access(array('publisherId' => $publisherId, 'streamName' => $streamName, 'ofUserId' => $user->id, 'grantedByUserId' => $app, 'readLevel' => Streams::$READ_LEVEL['max'], 'writeLevel' => -1, 'adminLevel' => -1)); $access->save(); $amount = $plan->getAttribute('amount', null); if (!is_numeric($amount)) { throw new Q_Exception_WrongValue(array('field' => 'amount', 'range' => 'an integer')); } $options['stream'] = $stream; if ($payments) { Assets::charge($payments, $amount, $currency, $options); } /** * @event Assets/startSubscription {before} * @param {Streams_Stream} plan * @param {Streams_Stream} subscription * @param {string} startDate * @param {string} endDate * @return {Users_User} */ Q::event('Assets/startSubscription', compact('plan', 'user', 'publisher', 'stream', 'startDate', 'endDate', 'months', 'currency'), 'after'); return $stream; }
/** * Saves parameters to a file * @method save * @param {string} $filename Name of file to save to. If tree was loaded, you can leave this blank to update that file. * @param {array} [$array_path=array()] Array of keys identifying the path of the config subtree to save * @return {boolean} Returns true if saved, otherwise false; **/ function save($filename = null, $array_path = array(), $prefix_path = null) { if (empty($filename) and !empty($this->filename)) { $filename = $this->filename; } if (!($filename2 = Q::realPath($filename))) { $filename2 = $filename; } if (empty($array_path)) { $array_path = array(); $toSave = $this->parameters; } else { $array_path[] = null; $toSave = call_user_func_array(array($this, 'get'), $array_path); } if (is_null($prefix_path)) { $prefix_path = $array_path; } $prefix_path = array_reverse($prefix_path); foreach ($prefix_path as $ap) { if ($ap) { $toSave = array($ap => $toSave); } } $mask = umask(Q_Config::get('Q', 'internal', 'umask', 00)); $success = file_put_contents($filename2, !empty($toSave) ? Q::json_encode($toSave, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) : '{}', LOCK_EX); clearstatcache(true, $filename2); umask($mask); if ($success) { self::$cache[$filename] = $toSave; Q_Cache::set("Q_Tree\t{$filename}", $toSave); // no need to check result - on failure Q_Cache is disabled } return $success; }
/** * Unsubcsribe from all or specific stream's messages * @method unsubscribe * @param $options=array() {array} * "userId": The user who is unsubscribing from the stream. Defaults to the logged-in user. * "skipAccess": if true, skip access check for whether user can unsubscribe * @return {boolean} */ function unsubscribe($options = array()) { $stream = $this->fetchAsUser($options, $userId); if (empty($options['skipAccess']) and !$stream->testReadLevel('messages')) { if (!$stream->testReadLevel('see')) { throw new Streams_Exception_NoSuchStream(); } throw new Users_Exception_NotAuthorized(); } $participant = $stream->join(array("userId" => $userId, 'subscribed' => false, 'noVisit' => true, "skipAccess" => Q::ifset($options, 'skipAccess', false))); Q_Utils::sendToNode(array("Q/method" => "Streams/Stream/unsubscribe", "stream" => Q::json_encode($stream->toArray()), "participant" => Q::json_encode($participant), "success" => Q::json_encode(!!$participant))); // Post Streams/unsubscribe message to the stream $stream->post($userId, array('type' => 'Streams/unsubscribe'), true); // Now post Streams/unsubscribed message to Streams/participating Streams_Message::post($userId, $userId, 'Streams/participating', array('type' => 'Streams/unsubscribed', 'instructions' => Q::json_encode(array('publisherId' => $stream->publisherId, 'streamName' => $stream->name))), true); return !!$participant; }
<?php echo Q::json_encode(compact(Q_Response::getRequestedSlots()));
/** * Subscription tool * @param array $options * "publisherId" => the id of the user who is publishing the stream * "streamName" => the name of the stream for which to edit access levels */ function Streams_subscription_tool($options) { $subscribed = 'no'; extract($options); $user = Users::loggedInUser(true); if (!isset($publisherId)) { $publisherId = Streams::requestedPublisherId(true); } if (!isset($streamName)) { $streamName = Streams::requestedName(); } $stream = Streams::fetchOne($user->id, $publisherId, $streamName); if (!$stream) { throw new Q_Exception_MissingRow(array('table' => 'stream', 'criteria' => compact('publisherId', 'streamName'))); } $streams_participant = new Streams_Participant(); $streams_participant->publisherId = $publisherId; $streams_participant->streamName = $streamName; $streams_participant->userId = $user->id; if ($streams_participant->retrieve()) { $subscribed = $streams_participant->subscribed; } $types = Q_Config::get('Streams', 'types', $stream->type, 'messages', array()); $messageTypes = array(); foreach ($types as $type => $msg) { $name = Q::ifset($msg, 'title', $type); /* * group by name */ foreach ($messageTypes as $msgType) { if ($msgType['name'] == $name) { continue 2; } } $messageTypes[] = array('value' => $type, 'name' => $name); } $usersFetch = array('userId' => $user->id, 'state' => 'active'); $devices = array(); $emails = Users_Email::select('address')->where($usersFetch)->fetchAll(PDO::FETCH_COLUMN); $mobiles = Users_Mobile::select('number')->where($usersFetch)->fetchAll(PDO::FETCH_COLUMN); foreach ($emails as $email) { $devices[] = array('value' => Q::json_encode(array('email' => $email)), 'name' => 'my email'); } foreach ($mobiles as $mobile) { $devices[] = array('value' => Q::json_encode(array('mobile' => $mobile)), 'name' => 'my mobile'); } $items = array(); $rules = Streams_Rule::select('deliver, filter')->where(array('ofUserId' => $user->id, 'publisherId' => $publisherId, 'streamName' => $streamName))->fetchAll(PDO::FETCH_ASSOC); while ($rule = array_pop($rules)) { $filter = json_decode($rule['filter']); /* * group by name */ foreach ($rules as $val) { if (json_decode($val['filter'])->labels == $filter->labels) { continue 2; } } $items[] = array('deliver' => json_decode($rule['deliver']), 'filter' => $filter); } Q_Response::addScript("plugins/Streams/js/Streams.js"); Q_Response::addScript("plugins/Streams/js/tools/subscription.js"); Q_Response::setToolOptions(compact('items', 'subscribed', 'messageTypes', 'devices', 'publisherId', 'streamName')); }
/** * 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(); } }
/** * Validates the request sent by the javascript * @method validateRequest * @static */ static function validateRequest() { if (empty($_REQUEST['url'])) { self::respond(Q::json_encode(array('errors' => 'url is missing'))); exit; } }
function clearInstruction($instructionName) { $instr = $this->getAllInstructions(); unset($instr[$instructionName]); $this->instructions = Q::json_encode($instr); }
/** * Send credits, as the logged-in user, to another user * @method send * @static * @param {integer} $amount The amount of credits to send. * @param {string} $toUserId The id of the user to whom you will send the credits * @param {array} $more An array supplying more info, including * "reason" => Identifies the reason for sending, if any */ static function send($amount, $toUserId, $more = array()) { if (!is_int($amount) or $amount <= 0) { throw new Q_Exception_WrongType(array('field' => 'amount', 'type' => 'integer')); } $instructions_json = Q::json_encode(array_merge(array('app' => Q_Config::expect('Q', 'app')), $more)); $from_stream = self::userStream(null, null, true); $existing_amount = $from_stream->getAttribute('amount'); if ($existing_amount < $amount) { throw new Awards_Exception_NotEnoughCredits(array('missing' => $amount - $existing_amount)); } $from_stream->setAttribute('amount', $from_stream->getAttribute('amount') - $amount); $from_stream->save(); $from_stream->post($user->id, array('type' => 'Awards/credits/sent', 'content' => $amount, 'instructions' => $instructions_json)); // TODO: add journaling system // Because if the following fails, then someone will lose credits // without the other person getting them. For now we will rely on the user complaining. $to_stream = self::userStream($toUserId, $toUserId, true); $to_stream->setAttribute('amount', $to_stream->getAttribute('amount') + $amount); $to_stream->save(); $to_stream->post($user->id, array('type' => 'Awards/credits/received', 'content' => $amount, 'instructions' => $instructions_json)); }
/** * 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); }