/** * Used by HTTP clients to upload a new file to the server * @class Q/file * @method post * @param {array} [$params] Parameters that can come from the request * @param {string} [$params.data] Required if $_FILES is empty. Base64-encoded image data URI - see RFC 2397 * @param {string} [$params.path="uploads"] parent path under web dir (see subpath) * @param {string} [$params.subpath=""] subpath that should follow the path, to save the image under * @param {string} [$params.name] override the name of the file, after the subpath */ function Q_file_post($params = null) { $p = $params ? $params : Q::take($_REQUEST, array('data', 'path', 'subpath')); if (!empty($_FILES)) { $file = reset($_FILES); if ($tmp = $file['tmp_name']) { if (empty($p['data'])) { $p['data'] = file_get_contents($tmp); $p['name'] = $file['name']; } unlink($tmp); } } else { if (empty($p['data'])) { throw new Q_Exception_RequiredField(array('field' => 'data'), 'data'); } $p['data'] = base64_decode(chunk_split(substr($p['data'], strpos($p['data'], ',') + 1))); } $timeLimit = Q_Config::get('Q', 'uploads', 'limits', 'file', 'time', 5 * 60 * 60); set_time_limit($timeLimit); // default is 5 min $data = Q_File::save($p); if (empty($params)) { Q_Response::setSlot('data', $data); } return $data; }
/** * This tool generates an inline editor to edit the content or attribute of a stream. * @class Streams inplace * @constructor * @param {array} $options Options for the tool * An associative array of parameters, containing: * @param {string} [$options.inplaceType='textarea'] The type of the fieldInput. Can be "textarea" or "text" * @param {array} [$options.convert] The characters to convert to HTML. Pass an array containing zero or more of "\n", " " * @param {Streams_Stream} $options.stream A Streams_Stream object * @param {string} [$options.field] Optional, name of an field to change instead of the content of the stream * @param {string} [$options.attribute] Optional, name of an attribute to change instead of any field. * @param {string} [$options.beforeSave] Reference to a callback to call after a successful save. This callback can cancel the save by returning false. * @param {string} [$options.onSave] Reference to a callback or event to run after a successful save. * @param {string} [$options.onCancel] Reference to a callback or event to run after cancel. * @param {array} [$options.inplace=array()] Additional fields to pass to the child Q/inplace tool, if any * @uses Q inplace */ function Streams_inplace_tool($options) { if (empty($options['stream'])) { if (empty($options['publisherId']) or empty($options['streamName'])) { throw new Q_Exception_RequiredField(array('field' => 'stream')); } $publisherId = $options['publisherId']; $streamName = $options['streamName']; $stream = Streams::fetchOne(null, $publisherId, $streamName); if (!$stream) { throw new Q_Exception_MissingRow(array('table' => 'stream', 'criteria' => "publisherId={$publisherId}, name={$streamName}")); } } else { $stream = $options['stream']; } $inplaceType = Q::ifset($options, 'inplaceType', 'textarea'); $inplace = array('action' => $stream->actionUrl(), 'method' => 'PUT', 'type' => $inplaceType); if (isset($options['inplace'])) { $inplace = array_merge($options['inplace'], $inplace); } $convert = Q::ifset($options, 'convert', array("\n")); $inplace['hidden']['convert'] = json_encode($convert); if (!empty($options['attribute'])) { $field = 'attributes[' . urlencode($options['attribute']) . ']'; $content = $stream->get($options['attribute'], ''); $maxlength = $stream->maxSize_attributes - strlen($stream->maxSize_attributes) - 10; } else { $field = !empty($options['field']) ? $options['field'] : 'content'; $content = $stream->{$field}; $maxlength = $stream->maxSizeExtended($field); } switch ($inplaceType) { case 'text': $inplace['fieldInput'] = Q_Html::input($field, $content, array('placeholder' => Q::ifset($input, 'placeholder', null), 'maxlength' => $maxlength)); $inplace['staticHtml'] = Q_Html::text($content); break; case 'textarea': $inplace['fieldInput'] = Q_Html::textarea($field, 5, 80, array('placeholder' => Q::ifset($inplace, 'placeholder', null), 'maxlength' => $maxlength), $content); $inplace['staticHtml'] = Q_Html::text($content, $convert); break; default: return "inplaceType must be 'textarea' or 'text'"; } if (!$stream->testWriteLevel('suggest')) { if (!isset($options['classes'])) { $options['classes'] = ''; } Q_Response::setToolOptions(array('publisherId' => $stream->publisherId, 'streamName' => $stream->name)); $staticClass = $options['inplaceType'] === 'textarea' ? 'Q_inplace_tool_blockstatic' : 'Q_inplace_tool_static'; return "<span class='Q_inplace_tool_container {$options['classes']}' style='position: relative;'>" . "<div class='{$staticClass}'>{$inplace['staticHtml']}</div></span>"; } $toolOptions = array('publisherId' => $stream->publisherId, 'streamName' => $stream->name, 'inplaceType' => $options['inplaceType']); Q::take($options, array('attribute', 'field', 'convert'), $toolOptions); $toolOptions['inplace'] = $inplace; Q_Response::setToolOptions($toolOptions); return Q::tool("Q/inplace", $inplace); }
/** * Adds a device to the system, after sending a test notification to it * @param {array} $device * @param {string} $device.userId * @param {string} $device.deviceId * @param {string} [$device.formFactor] * @param {string} [$device.platform] * @param {string} [$device.version] * @param {string} [$device.sessionId] * @param {boolean} [$device.sandbox] * @param {string} [$device.passphrase] * @param {boolean} [$skipNotification=false] if true, skips sending notification * @return {Users_Device} */ static function add($device, $skipNotification = false) { Q_Valid::requireFields(array('userId', 'deviceId'), $device, true); $userId = $device['userId']; $deviceId = $device['deviceId']; if (!$skipNotification) { $app = Q::app(); $sandbox = Q::ifset($device, 'sandbox', null); if (!isset($sandbox)) { $sandbox = Q_Config::get($app, "cordova", "ios", "sandbox", false); } $env = $sandbox ? ApnsPHP_Abstract::ENVIRONMENT_SANDBOX : ApnsPHP_Abstract::ENVIRONMENT_PRODUCTION; $s = $sandbox ? 'sandbox' : 'production'; $cert = APP_LOCAL_DIR . DS . 'Users' . DS . 'certs' . DS . $app . DS . $s . DS . 'bundle.pem'; $authority = USERS_PLUGIN_FILES_DIR . DS . 'Users' . DS . 'certs' . DS . 'EntrustRootCA.pem'; $logger = new Users_ApnsPHP_Logger(); $push = new ApnsPHP_Push($env, $cert); $push->setLogger($logger); $push->setRootCertificationAuthority($authority); if (isset($device['passphrase'])) { $push->setProviderCertificatePassphrase($device['passphrase']); } $push->connect(); $message = new ApnsPHP_Message($deviceId); $message->setCustomIdentifier('Users_Device-adding'); $message->setBadge(0); $message->setText(Q_Config::get($app, "cordova", "ios", "device", "text", "Notifications have been enabled")); $message->setCustomProperty('userId', $userId); $message->setExpiry(5); $push->add($message); $push->send(); $push->disconnect(); $errors = $push->getErrors(); if (!empty($errors)) { $result = reset($errors); throw new Users_Exception_DeviceNotification($result['ERRORS'][0]); } } $sessionId = Q_Session::id(); $user = Users::loggedInUser(); $info = array_merge(Q_Request::userAgentInfo(), array('sessionId' => $sessionId, 'userId' => $user ? $user->id : null, 'deviceId' => null)); $device2 = Q::take($device, $info); $d = new Users_Device($device2); $d->save(true); if ($sessionId) { $s = new Users_Session(); $s->id = $sessionId; if (!$s->retrieve()) { $s->deviceId = $deviceId; } } $_SESSION['Users']['deviceId'] = $deviceId; $device2['Q/method'] = 'Users/device'; Q_Utils::sendToNode($device2); return $d; }
function Streams_after_Users_Label_saveExecute($params) { // The icon or title might have been modified $modifiedFields = $params['modifiedFields']; $label = $params['row']; $updates = Q::take($modifiedFields, array('icon', 'title')); $updates['userId'] = $label->userId; $updates['label'] = $label->label; return Streams_Message::post(null, $label->userId, "Streams/labels", array('type' => 'Streams/labels/updated', 'instructions' => compact('updates')), true); }
/** * 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 Streams_after_Users_Contact_saveExecute($params) { $inserted = $params['inserted']; $modifiedFields = $params['modifiedFields']; $contact = $params['row']; if ($inserted) { Streams_Message::post(null, $contact->userId, 'Streams/contacts', array('type' => 'Streams/contacts/inserted', 'instructions' => array('contact' => $contact->exportArray())), true); } else { $updates = Q::take($modifiedFields, array('nickname')); $updates = array_merge($contact->toArray(), $updates); Streams_Message::post(null, $contact->userId, 'Streams/contacts', array('type' => 'Streams/contacts/updated', 'instructions' => compact('updates')), true); } }
function Streams_after_Users_Label_saveExecute($params) { // The icon or title might have been modified $inserted = $params['inserted']; $modifiedFields = $params['modifiedFields']; $label = $params['row']; if ($inserted) { Streams_Message::post(null, $label->userId, 'Streams/labels', array('type' => 'Streams/labels/inserted', 'instructions' => array('label' => $label->exportArray())), true); } else { $updates = Q::take($modifiedFields, array('icon', 'title')); $updates = array_merge($label->toArray(), $updates); Streams_Message::post(null, $label->userId, "Streams/labels", array('type' => 'Streams/labels/updated', 'instructions' => compact('updates')), true); } }
/** * 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 Users_before_Q_responseExtras() { Q_Response::addScript('plugins/Users/js/Users.js'); $app = Q_Config::expect('Q', 'app'); $requireLogin = Q_Config::get('Users', 'requireLogin', array()); $rl_array = array(); foreach ($requireLogin as $rl => $value) { $rl_array[Q_Uri::url($rl)] = $value; } if (!Q_Request::isAjax()) { Q_Response::setScriptData('Q.plugins.Users.requireLogin', $rl_array); $successUrl = Q_Config::get('Users', 'uris', "{$app}/successUrl", "{$app}/home"); $afterActivate = Q_Config::get('Users', 'uris', "{$app}/afterActivate", $successUrl); $loginOptions = Q_Config::get('Users', 'login', array("identifierType" => 'email,mobile', "userQueryUri" => 'Users/user', "using" => "native,facebook", "noRegister" => false)); $loginOptions["afterActivate"] = Q_Uri::url($afterActivate); $loginOptions["successUrl"] = Q_Uri::url($successUrl); Q_Response::setScriptData('Q.plugins.Users.login.serverOptions', $loginOptions); $setIdentifierOptions = Q::take($loginOptions, array('identifierType')); Q_Response::setScriptData('Q.plugins.Users.setIdentifier.serverOptions', $setIdentifierOptions); } $fb_app_info = Q_Config::get('Users', 'facebookApps', $app, array()); if ($fb_app_info) { unset($fb_app_info['secret']); Q_Response::setScriptData("Q.plugins.Users.facebookApps.{$app}", $fb_app_info); } if ($node_server_url = Q_Config::get('Users', 'nodeServer', 'url', null)) { Q_Response::setScriptData("Q.plugins.Users.nodeServer", parse_url($node_server_url)); } if (Q_Config::get('Users', 'showLoggedInUser', true)) { $user = Q_Session::id() ? Users::loggedInUser() : null; if ($user) { $u = $user->exportArray(); $u['sessionCount'] = $user->sessionCount; Q_Response::setScriptData("Q.plugins.Users.loggedInUser", $u); Q_Response::addScriptLine("Q.plugins.Users.loggedInUser = new Q.plugins.Users.User(Q.plugins.Users.loggedInUser);"); } } Q_Response::setScriptData('Q.plugins.Users.communityId', Users::communityId()); Q_Response::setScriptData('Q.plugins.Users.communityName', Users::communityName()); Q_Response::setScriptData('Q.plugins.Users.communitySuffix', Users::communitySuffix()); Q_Response::setScriptData('Q.plugins.Users.hinted', Q::ifset($_SESSION, 'Users', 'hinted', array())); if ($sizes = Q_Config::expect('Users', 'icon', 'sizes')) { sort($sizes); Q_Response::setScriptData('Q.plugins.Users.icon.sizes', $sizes); } $defaultSize = Q_Config::get('Users', 'icon', 'defaultSize', 40); Q_Response::setScriptData('Q.plugins.Users.icon.defaultSize', $defaultSize); Q_Response::addStylesheet("plugins/Users/css/Users.css"); }
function Websites_before_Q_responseExtras() { $user = Users::loggedInUser(false, false); $userId = $user ? $user->id : ""; $websitesUserId = Users::communityId(); $sha1 = sha1(Q_Dispatcher::uri()); $seoStreamName = "Websites/seo/{$sha1}"; $stream = Streams::fetchOne($userId, $websitesUserId, $seoStreamName); if ($stream) { $fields = Q::take($stream->getAllAttributes(), array('keywords', 'description')); foreach ($fields as $k => $v) { Q_Response::setMeta($k, $v); } Q_Response::setSlot('title', $stream->getAttribute('title')); } Q_Response::setScriptData('Q.plugins.Websites.seoStreamName', $seoStreamName); Q_Response::setScriptData('Q.plugins.Websites.userId', Users::communityId()); Q_Response::setScriptData('Q.plugins.Websites.seoReload', Q_Config::expect('Websites', 'seoReload')); }
function Users_oAuth_post() { // Validate the inputs $fields = array('response_type', 'token_type', 'access_token', 'expires_in', 'scope', 'state', 'Q_Users_oAuth'); Q_Request::requireFields($fields, true); $params = Q::take($_REQUEST, $fields); $params['Q.Users.oAuth'] = $params['Q_Users_oAuth']; unset($params['Q_Users_oAuth']); Q_Valid::signature(true, $params, array('Q.Users.oAuth')); // Set the session id to the access_token Q_Session::id($params['access_token']); // Add a device, if any if ($deviceId = Q::ifset($_REQUEST, 'deviceId', null)) { $fields2 = array('deviceId', 'platform', 'version', 'formFactor'); Q_Request::requireFields($fields2); $device = Q::take($_REQUEST, $fields2); $device['userId'] = Users::loggedInUser(true)->id; Users_Device::add($device); } }
/** * Make a one-time charge using the payments processor * @method charge * @param {double} $amount specify the amount (optional cents after the decimal point) * @param {string} [$currency='USD'] set the currency, which will affect the amount * @param {array} [$options=array()] Any additional options * @param {string} [$options.token=null] required unless the user is an existing customer * @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 \Stripe\Error\Card * @return {string} The customerId of the Assets_Customer that was successfully charged */ function charge($amount, $currency = 'USD', $options = array()) { $options = array_merge($this->options, $options); Q_Valid::requireFields(array('secret', 'user'), $options, true); \Stripe\Stripe::setApiKey($options['secret']); $user = $options['user']; $customer = new Assets_Customer(); $customer->userId = $user->id; $customer->payments = 'stripe'; if (!$customer->retrieve()) { Q_Valid::requireFields(array('token'), $options, true); $sc = \Stripe\Customer::create(array("source" => $options['token']["id"], "description" => $options['user']->displayName())); $customer->customerId = $sc->id; $customer->save(); } $params = array("amount" => $amount * 100, "currency" => $currency, "customer" => $customer->customerId); Q::take($options, array('description', 'metadata'), $params); \Stripe\Charge::create($params); // can throw some exception return $customer->customerId; }
function Websites_before_Q_responseExtras() { $user = Users::loggedInUser(false, false); $userId = $user ? $user->id : ""; $websitesUserId = Q_Config::expect("Websites", "user", "id"); $sha1 = sha1(Q_Dispatcher::uri()); $seoStreamName = "Websites/seo/{$sha1}"; $streams = Streams::fetch($userId, $websitesUserId, array("Websites/header", "Websites/title", "Websites/slogan", $seoStreamName)); if (!empty($streams[$seoStreamName])) { $fields = Q::take($streams[$seoStreamName]->getAllAttributes(), array('keywords', 'description')); foreach ($fields as $k => $v) { Q_Response::setMeta($k, $v); } Q_Response::setSlot('title', $streams[$seoStreamName]->getAttribute('title')); } foreach ($streams as $name => $s) { if ($s) { $s->addPreloaded($userId); } } Q_Response::setScriptData('Q.plugins.Websites.seoStreamName', $seoStreamName); Q_Response::setScriptData('Q.plugins.Websites.userId', Q_Config::expect('Websites', 'user', 'id')); Q_Response::setScriptData('Q.plugins.Websites.seoReload', Q_Config::expect('Websites', 'seoReload')); }
/** * Used by HTTP clients to create a new stream in the system. * @class HTTP Streams stream * @method post * @param {array} [$params] Parameters that can come from the request * @param {string} $params.publisherId Required. The id of the user to publish the stream. * @param {string} $params.type Required. The type of the stream. * @param {string} [$params.Q_Streams_related_publisherId] Optionally indicate the publisher of the stream to relate the newly created to. Used together with the related.streamName option. * @param {string} [$params.Q_Streams_related_streamName] Optionally indicate the name of a stream to relate the newly crated stream to. This is often necessary in order to obtain permissions to create the stream. * @param {bool} [$params.dontSubscribe=false] Pass 1 or true here in order to skip auto-subscribing to the newly created stream. * @param {array} [$params.icon] This is used to upload a custom icon for the stream which will then be saved in different sizes. See fields for Q/image/post method * @param {string} [$params.icon.data] Required if $_FILES is empty. Base64-encoded data URI - see RFC 2397 * @param {string} [$params.icon.path="uploads"] parent path under web dir (see subpath) * @param {string} [$params.icon.subpath=""] subpath that should follow the path, to save the image under * @param {string} [$params.icon.merge=""] path under web dir for an optional image to use as a background * @param {string} [$params.icon.crop] array with keys "x", "y", "w", "h" to crop the original image * @param {string} [$params.icon.save=array("x" => "")] array of $size => $basename pairs * where the size is of the format "WxH", and either W or H can be empty. * @param {array} [$params.file] This is used to upload a custom icon for the stream which will then be saved in different sizes. See fields for Q/image/post method * @param {string} [$params.file.data] Required if $_FILES is empty. Base64-encoded data URI - see RFC 2397 * @param {string} [$params.file.path="uploads"] parent path under web dir (see subpath) * @param {string} [$params.file.subpath=""] subpath that should follow the path, to save the file under * @param {string} [$params.file.name] override name of the file, after the subpath */ function Streams_stream_post($params = array()) { $user = Users::loggedInUser(true); $publisherId = Streams::requestedPublisherId(); if (empty($publisherId)) { $publisherId = $_REQUEST['publisherId'] = $user->id; } $req = array_merge($_REQUEST, $params); $type = Streams::requestedType(true); $types = Q_Config::expect('Streams', 'types'); if (!array_key_exists($type, $types)) { throw new Q_Exception("This app doesn't support streams of type {$type}", 'type'); } $create = Streams_Stream::getConfigField($type, 'create', false); if (!$create) { throw new Q_Exception("This app doesn't let clients directly create streams of type {$type}", 'type'); } // Should this stream be related to another stream? $relate = array(); $relate['streamName'] = Q_Request::special("Streams.related.streamName", null, $req); if (isset($relate['streamName'])) { $relate['publisherId'] = Q_Request::special("Streams.related.publisherId", $publisherId, $req); $relate['type'] = Q_Request::special("Streams.related.type", "", $req); $relate['weight'] = "+1"; // TODO: introduce ways to have "1" and "+1" for some admins etc. } // Split the id for saving files in the filesystem $splitId = Q_Utils::splitId($publisherId); // Hold on to any icon that was posted $icon = null; if (!empty($req['icon']) and is_array($req['icon'])) { $icon = $req['icon']; unset($req['icon']); } // Hold on to any file that was posted $file = null; if (!empty($req['file']) and is_array($req['file'])) { $file = $req['file']; unset($req['file']); } // Check if the user owns the stream if ($user->id === $publisherId) { $asOwner = true; } else { $streamTemplate = Streams_Stream::getStreamTemplate($publisherId, $type, 'Streams_Stream'); $asOwner = $streamTemplate ? $streamTemplate->testAdminLevel('own') : false; } // Check if client can set the name of this stream if (isset($req['name'])) { $possible = Q_Config::get('Streams', 'possibleUserStreams', $req['name'], false); if (!$asOwner or !$possible) { throw new Users_Exception_NotAuthorized(); } } // Get allowed fields $allowedFields = array_merge(array('publisherId', 'name', 'type', 'icon', 'file'), Streams::getExtendFieldNames($type, $asOwner)); $fields = Q::take($req, $allowedFields); // Prevent setting restricted fields if (is_array($create)) { $restrictedFields = array_diff($allowedFields, $create); foreach ($restrictedFields as $fieldName) { if (in_array($fieldName, array('publisherId', 'type'))) { continue; } if (isset($req[$fieldName])) { throw new Users_Exception_NotAuthorized(); } } } // Create the stream $stream = Streams::create($user->id, $publisherId, $type, $fields, $relate, $result); $messageTo = false; if (isset($result['messagesTo'])) { $messageTo = reset($result['messagesTo']); $messageTo = reset($messageTo); if (is_array($messageTo)) { $messageTo = reset($messageTo); } $messageTo = $messageTo->exportArray(); } Q_Response::setSlot('messageTo', $messageTo); // Process any icon that was posted if ($icon === true) { $icon = array(); } if (is_array($icon)) { if (empty($icon['path'])) { $icon['path'] = 'uploads/Streams'; } if (empty($icon['subpath'])) { $icon['subpath'] = "{$splitId}/{$stream->name}/icon/" . time(); } Q_Response::setSlot('icon', Q::event("Q/image/post", $icon)); // the Streams/after/Q_image_save hook saves some attributes } // Process any file that was posted if ($file === true) { $file = array(); } if (is_array($file)) { if (empty($file['path'])) { $file['path'] = 'uploads/Streams'; } if (empty($file['subpath'])) { $file['subpath'] = "{$splitId}/{$stream->name}/file/" . time(); } Q_Response::setSlot('file', Q::event("Q/file/post", $file)); // the Streams/after/Q_file_save hook saves some attributes } // Re-fetch the stream object from the Streams::fetch cache, // since it might have been retrieved and modified to be different // from what is currently in $stream. // This also calculates the access levels on the stream. $stream = Streams::fetchOne($user->id, $publisherId, $stream->name); if (empty($req['dontSubscribe'])) { // autosubscribe to streams you yourself create, using templates $stream->subscribe(); } Streams::$cache['stream'] = $stream; }
/** * This tool is meant to be wrapped in a <form> tag * @param {array} $options An associative array of parameters, containing: * @param {array} $options.fields an associative array of fieldname => fieldinfo pairs, * where fieldinfo contains the following: * "type" => the type of the field (@see Q_Html::smartTag()) * "attributes" => additional attributes for the field input * "value" => the initial value of the field input * "options" => options for the field input (if type is "select", "checkboxes" or "radios") * "message" => initial message, if any to put in the field's message space * "label" => the label for the field * "extra" => if set, this is html to replace the first cell, displacing the label * "placeholder" => if set, this is the placeholder text for the input * "fillFromRequest" => Defaults to true. * If true, uses $_REQUEST to fill any fields with same name. * Currently doesn't work for names which specify arrays, such as a[b]. * @param {string} [$options.onSubmit] Optional. Name of the javascript function or url to pass to Q.handle on submit * @param {string} [$options.onResponse] Name of the javascript function or url to pass to Q.handle on response * @param {string} [$options.onSuccess] Name of javascript function or url to pass to Q.handle on success * @param {string} [$options.loader] Optional. Name of a javascript function which takes (action, method, params, slots, callback) as arguments. * It should call the callback and pass it an object with the response info. Can be used to implement caching, etc. * instead of the default HTTP request. * If "loader" is Q.getter and request should be done bypasing cache, assign true to .ignoreCache property of the tool * @param {array|string} [$options.slotsToRequest] Optional. A string or array of slot names to request in response. Should include "form". * @param {array|string} [$options.contentElements] Optional. Array of $slotName => $cssSelector pairs for child element of the form to fill with HTML returned from the slot. */ function Q_form_tool($options) { if (empty($options['fields'])) { $options['fields'] = array(); } if (!array_key_exists('fillFromRequest', $options)) { $options['fillFromRequest'] = true; } if (empty($options['contentElements'])) { $options['contentElements'] = array(); } $field_defaults = array('type' => 'text', 'attributes' => array(), 'value' => null, 'options' => array(), 'message' => '', 'placeholder' => null); $tr_array = array(); $messages_td = false; $colspan = ''; foreach ($options['fields'] as $name => $field) { if (isset($field['message'])) { $messages_td = true; $colspan = "colspan='2'"; } } foreach ($options['fields'] as $name => $field) { if (!is_array($field)) { $name2 = '"' . addslashes($name) . '"'; throw new Q_Exception_WrongType(array('field' => "\$options[{$name2}]", 'type' => 'array')); } $field2 = array_merge($field_defaults, $field); $type = $field2['type']; if ($type === 'hidden') { continue; } $attributes = array('name' => $name, 'id' => $name); $value = $field2['value']; $o = $field2['options']; $message = $field2['message']; if (!empty($options['fillFromRequest']) and !in_array($type, array('button', 'submit'))) { if (isset($_REQUEST[$name])) { $value = $_REQUEST[$name]; } else { if ($type === 'static' or $type === 'date') { $parts = array($name . '_hour' => 0, $name . '_minute' => 0, $name . '_second' => 0, $name . '_month' => date('m'), $name . '_day' => date('d'), $name . '_year' => date('Y')); $provided = Q::ifset($_REQUEST, array_keys($parts), null); if (isset($provided)) { $mktime = Q::take($_REQUEST, $parts); $value = call_user_func_array('mktime', $mktime); } } } } if (isset($field2['placeholder'])) { $attributes['placeholder'] = $field2['placeholder']; } if ($field2['attributes']) { $attributes = array_merge($attributes, $field2['attributes']); } if (ctype_alnum($type)) { if (isset($attributes['class'])) { if (is_array($attributes['class'])) { foreach ($attributes['class'] as $k => $v) { $attributes['class'][$k] .= " {$type}"; } } else { $attributes['class'] .= " {$type}"; } } else { $attributes['class'] = " {$type}"; } } $label = isset($field['label']) ? $field['label'] : Q_Html::text($name); $label = Q_Html::tag('label', array('for' => $attributes['id']), $label); $name_text = Q_Html::text($name); $extra = isset($field['extra']) ? $field['extra'] : null; switch ($type) { case 'textarea': $tr_rest = "<td class='Q_form_fieldinput' data-fieldname=\"{$name_text}\" {$colspan}>" . ($extra ? "<div class='Q_form_label'>{$label}</div>" : '') . Q_Html::smartTag($type, $attributes, $value, $o) . "</td></tr><tr><td class='Q_form_placeholder'>" . "</td><td class='Q_form_undermessage Q_form_textarea_undermessage' {$colspan}>" . "<div class='Q_form_undermessagebubble'>{$message}</div></td>"; break; default: $tr_rest = "<td class='Q_form_fieldinput' data-fieldname=\"{$name_text}\">" . ($extra ? "<div class='Q_form_label'>{$label}</div>" : '') . Q_Html::smartTag($type, $attributes, $value, $o) . "</td>" . ($messages_td ? "<td class='Q_form_fieldmessage Q_form_{$type}_message'>{$message}</td>" : '') . "</tr><tr><td class='Q_form_placeholder'>" . "</td><td class='Q_form_undermessage Q_form_{$type}_undermessage' {$colspan}>" . "<div class='Q_form_undermessagebubble'></div></td>"; break; } $leftside = $extra ? $extra : $label; $tr_array[] = "<tr><td class='Q_form_fieldname'>{$leftside}</td>{$tr_rest}</tr>"; } $result = "<table class='Q_form_tool_table'>\n" . implode("\n\t", $tr_array) . "\n</table>"; foreach ($options['fields'] as $name => $field) { if (isset($field['type']) and $field['type'] === 'hidden') { $result .= Q_Html::hidden($field['value'], $name); } } $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; }
/** * Used to set the user's location from geolocation data. * @class HTTP Places geolocation * @method post * @param $_REQUEST * @param [$_REQUEST.latitude] The new latitude. If set, must also specify longitude. * @param [$_REQUEST.longitude] The new longitude. If set, must also specify latitude. * @param [$_REQUEST.zipcode] The new zip code. Can be set instead of latitude, longitude. * @param [$_REQUEST.miles] The distance around their location around that the user is interested in * @param [$_REQUEST.subscribe] Whether to subscribe to all the local interests at the new location. * @param [$_REQUEST.unsubscribe] Whether to unsubscribe from all the local interests at the old location. * @param [$_REQUEST.accuracy] * @param [$_REQUEST.altitude] * @param [$_REQUEST.altitudeAccuracy] * @param [$_REQUEST.heading] * @param [$_REQUEST.speed] * @param [$_REQUEST.timezone] * @param [$_REQUEST.placeName] optional * @param [$_REQUEST.state] optional * @param [$_REQUEST.country] optional */ function Places_geolocation_post() { $user = Users::loggedInUser(true); $stream = Places_Location::userStream(); $oldLatitude = $stream->getAttribute('latitude'); $oldLongitude = $stream->getAttribute('longitude'); $oldMiles = $stream->getAttribute('miles'); $fields = array('accuracy', 'altitude', 'altitudeAccuracy', 'heading', 'latitude', 'longitude', 'speed', 'miles', 'zipcode', 'timezone', 'placeName', 'state', 'country'); $attributes = Q::take($_REQUEST, $fields); if (isset($attributes['latitude']) xor isset($attributes['longitude'])) { throw new Q_Exception("When specifying latitude,longitude you must specify both", array('latitude', 'longitude')); } if (!empty($attributes['zipcode']) and !isset($attributes['latitude'])) { $z = new Places_Zipcode(); $z->countryCode = 'US'; $z->zipcode = $attributes['zipcode']; if ($z->retrieve()) { $attributes['latitude'] = $z->latitude; $attributes['longitude'] = $z->longitude; $attributes['country'] = $z->countryCode; } else { throw new Q_Exception_MissingRow(array('table' => 'zipcode', 'criteria' => $attributes['zipcode']), 'zipcode'); } } $attributes['miles'] = Q::ifset($attributes, 'miles', $stream->getAttribute('miles', Q_Config::expect('Places', 'nearby', 'defaultMiles'))); if (empty($attributes['zipcode']) and isset($attributes['latitude'])) { $zipcodes = Places_Zipcode::nearby($attributes['latitude'], $attributes['longitude'], $attributes['miles'], 1); if ($zipcode = $zipcodes ? reset($zipcodes) : null) { $attributes['zipcode'] = $zipcode->zipcode; $attributes['placeName'] = $zipcode->placeName; $attributes['state'] = $zipcode->state; $attributes['country'] = $zipcode->countryCode; } } $stream->setAttribute($attributes); $stream->save(); $stream->post($user->id, array('type' => 'Places/location/updated', 'content' => '', 'instructions' => $stream->getAllAttributes()), true); $shouldUnsubscribe = !empty($_REQUEST['unsubscribe']) && isset($oldMiles); $shouldSubscribe = !empty($_REQUEST['subscribe']); $noChange = false; $latitude = $stream->getAttribute('latitude'); $longitude = $stream->getAttribute('longitude'); $miles = $stream->getAttribute('miles'); if ($shouldUnsubscribe and $shouldSubscribe and abs($latitude - $oldLatitude) < 0.0001 and abs($longitude - $oldLongitude) < 0.0001 and abs($miles - $oldMiles) < 0.001) { $noChange = true; } $attributes['stream'] = $stream; Q_Response::setSlot('attributes', $attributes); if (!$noChange) { // Send the response and keep going. // WARN: this potentially ties up the PHP thread for a long time $timeLimit = Q_Config::get('Places', 'geolocation', 'timeLimit', 100000); ignore_user_abort(true); set_time_limit($timeLimit); Q_Dispatcher::response(true); session_write_close(); if ($shouldUnsubscribe or $shouldSubscribe) { $myInterests = Streams_Category::getRelatedTo($user->id, 'Streams/user/interests', 'Streams/interests'); if (!isset($myInterests)) { $myInterests = array(); } } if ($shouldUnsubscribe and $oldLatitude and $oldLongitude and $oldMiles) { $results = array(); foreach ($myInterests as $weight => $info) { $publisherId = $info[0]; if (!isset($results[$publisherId])) { $results[$publisherId] = array(); } $results[$publisherId] = array_merge($results[$publisherId], Places_Interest::streams($publisherId, $oldLatitude, $oldLongitude, $info[2], array('miles' => $oldMiles, 'skipAccess' => true, 'forSubscribers' => true))); } foreach ($results as $publisherId => $streams) { Streams::unsubscribe($user->id, $publisherId, $streams, array('skipAccess' => true)); } $attributes['unsubscribed'] = Places_Nearby::unsubscribe($oldLatitude, $oldLongitude, $oldMiles); } if ($shouldSubscribe) { $results = array(); foreach ($myInterests as $weight => $info) { $publisherId = $info[0]; if (!isset($results[$publisherId])) { $results[$publisherId] = array(); } $results[$publisherId] = array_merge($results[$publisherId], Places_Interest::streams($publisherId, $latitude, $longitude, $info[2], array('miles' => $miles, 'skipAccess' => true, 'forSubscribers' => true))); } foreach ($results as $publisherId => $streams) { Streams::subscribe($user->id, $publisherId, $streams, array('skipAccess' => true)); } $attributes['subscribed'] = Places_Nearby::subscribe($latitude, $longitude, $miles); } } Q::event("Places/geolocation", $attributes, 'after'); }
/** * Used by HTTP clients to fetch relations and related streams * @class HTTP Streams related * @method get * @param {array} [$_REQUEST] Parameters that can come from the request * @param {string} $_REQUEST.publisherId Required. The user id of the publisher of the stream. * @param {string} $_REQUEST.streamName Required streamName or name. The name of the stream * @param {boolean} [$_REQUEST.isCategory=false] Whether to fetch streams related TO the stream with this publisherId and streamName. * @param {boolean} [$_REQUEST.relationsOnly=false] Return only the relations, not the streams * @param {boolean} [$_REQUEST.ascending=false] Whether to sort by ascending instead of descending weight * @param {boolean} [$_REQUEST.omitRedundantInfo=false] Whether to omit redundant publisherId and streamName fields in the output * @param {integer} [$_REQUEST.messages=0] Whether to also return this many latest messages per stream * @param {integer} [$_REQUEST.participants=0] Whether to also return this many participants per stream */ function Streams_related_response() { if (!Q_Request::slotName('relations') and !Q_Request::slotName('streams')) { return; } $user = Users::loggedInUser(); $asUserId = $user ? $user->id : ''; $publisherId = Streams::requestedPublisherId(true); $streamName = Streams::requestedName(true, 'original'); $isCategory = !(empty($_REQUEST['isCategory']) or strtolower($_REQUEST['isCategory']) === 'false'); $slotNames = Q_Request::slotNames(); $streams_requested = in_array('relatedStreams', $slotNames); $options = Q::take($_REQUEST, array('limit', 'offset', 'min', 'max', 'type', 'prefix', 'filter')); $options['relationsOnly'] = !$streams_requested; $options['orderBy'] = !empty($_REQUEST['ascending']); $options['fetchOptions'] = array('withParticipant' => true); $result = Streams::related($asUserId, $publisherId, $streamName, $isCategory, $options); $fields = Q::ifset($_REQUEST, 'fields', null); $exportOptions = array('numeric' => true); if (isset($fields)) { if (is_string($fields)) { $fields = array_map('trim', explode(',', $fields)); } $exportOptions['fields'] = $fields; } if ($streams_requested) { $rel = Db::exportArray($result[0], $exportOptions); } else { $rel = Db::exportArray($result, $exportOptions); } if (!empty($_REQUEST['omitRedundantInfo'])) { if ($isCategory) { foreach ($rel as &$r) { unset($r['toPublisherId']); unset($r['toStreamName']); } } else { foreach ($rel as &$r) { unset($r['fromPublisherId']); unset($r['fromStreamName']); } } } Q_Response::setSlot('relations', $rel); if (!$streams_requested) { return; } $streams = $result[1]; $arr = Db::exportArray($streams, array('numeric' => true)); foreach ($arr as $k => $stream) { if (!$stream) { continue; } $s = $streams[$stream['name']]; $arr[$k]['access'] = array('readLevel' => $s->get('readLevel', $s->readLevel), 'writeLevel' => $s->get('writeLevel', $s->writeLevel), 'adminLevel' => $s->get('adminLevel', $s->adminLevel)); } Q_Response::setSlot('relatedStreams', $arr); $stream = $result[2]; if (is_array($stream)) { Q_Response::setSlot('streams', Db::exportArray($stream)); return; } else { if (is_object($stream)) { Q_Response::setSlot('stream', $stream->exportArray()); } else { Q_Response::setSlot('stream', false); } } if (!empty($_REQUEST['messages'])) { $max = -1; $limit = $_REQUEST['messages']; $messages = false; $type = isset($_REQUEST['messageType']) ? $_REQUEST['messageType'] : null; if ($stream->testReadLevel('messages')) { $messages = Db::exportArray($stream->getMessages(compact('type', 'max', 'limit'))); } Q_Response::setSlot('messages', $messages); } if (!empty($_REQUEST['participants'])) { $limit = $_REQUEST['participants']; $offset = -1; $participants = false; if ($stream->testReadLevel('participants')) { $participants = Db::exportArray($stream->getParticipants(compact('limit', 'offset'))); } Q_Response::setSlot('participants', $participants); } }
function Places_autocomplete_response_results() { $r = Q::take($_REQUEST, array('input' => '', 'types' => null, 'latitude' => null, 'longitude' => null, 'miles' => 25)); return Places::autocomplete($r['input'], true, $r['types'], $r['latitude'], $r['longitude'], $r['miles']); }
/** * Used to set the user's location from geolocation data. * @param $_REQUEST * @param [$_REQUEST.latitude] The new latitude. If set, must also specify longitude. * @param [$_REQUEST.longitude] The new longitude. If set, must also specify latitude. * @param [$_REQUEST.zipcode] The new zip code. Can be set instead of latitude, longitude. * @param [$_REQUEST.miles] The distance around their location around that the user is interested in * @param [$_REQUEST.subscribe] Whether to subscribe to all the local interests at the new location. * @param [$_REQUEST.unsubscribe] Whether to unsubscribe from all the local interests at the old location. * @param [$_REQUEST.accuracy] * @param [$_REQUEST.altitude] * @param [$_REQUEST.altitudeAccuracy] * @param [$_REQUEST.heading] * @param [$_REQUEST.speed] * @param [$_REQUEST.timezone] * @param [$_REQUEST.placeName] optional * @param [$_REQUEST.state] optional * @param [$_REQUEST.country] optional */ function Places_geolocation_post() { $user = Users::loggedInUser(true); $stream = Places_Location::userStream(); $oldLatitude = $stream->getAttribute('latitude'); $oldLongitude = $stream->getAttribute('longitude'); $oldMiles = $stream->getAttribute('miles'); $fields = array('accuracy', 'altitude', 'altitudeAccuracy', 'heading', 'latitude', 'longitude', 'speed', 'miles', 'zipcode', 'timezone', 'placeName', 'state', 'country'); $attributes = Q::take($_REQUEST, $fields); if (isset($attributes['latitude']) xor isset($attributes['longitude'])) { throw new Q_Exception("When specifying latitude,longitude you must specify both", array('latitude', 'longitude')); } if (!empty($attributes['zipcode']) and !isset($attributes['latitude'])) { $z = new Places_Zipcode(); $z->countryCode = 'US'; $z->zipcode = $attributes['zipcode']; if ($z->retrieve()) { $attributes['latitude'] = $z->latitude; $attributes['longitude'] = $z->longitude; $attributes['country'] = $z->countryCode; } else { throw new Q_Exception_MissingRow(array('table' => 'zipcode', 'criteria' => $attributes['zipcode']), 'zipcode'); } } $attributes['miles'] = Q::ifset($attributes, 'miles', $stream->getAttribute('miles', Q_Config::expect('Places', 'nearby', 'defaultMiles'))); if (empty($attributes['zipcode']) and isset($attributes['latitude'])) { $zipcodes = Places_Zipcode::nearby($attributes['latitude'], $attributes['longitude'], $attributes['miles'], 1); if ($zipcode = $zipcodes ? reset($zipcodes) : null) { $attributes['zipcode'] = $zipcode->zipcode; $attributes['placeName'] = $zipcode->placeName; $attributes['state'] = $zipcode->state; $attributes['country'] = $zipcode->countryCode; } } $stream->setAttribute($attributes); $stream->save(); $stream->post($user->id, array('type' => 'Places/location/updated', 'content' => '', 'instructions' => $stream->getAllAttributes()), true); $shouldUnsubscribe = !empty($_REQUEST['unsubscribe']) && isset($oldMiles); $shouldSubscribe = !empty($_REQUEST['subscribe']); $noChange = false; $latitude = $stream->getAttribute('latitude'); $longitude = $stream->getAttribute('longitude'); $miles = $stream->getAttribute('miles'); if ($shouldUnsubscribe and $shouldSubscribe and abs($latitude - $oldLatitude) < 0.0001 and abs($longitude - $oldLongitude) < 0.0001 and abs($miles - $oldMiles) < 0.001) { $noChange = true; } if (!$noChange) { if ($shouldUnsubscribe or $shouldSubscribe) { $myInterests = Streams_Category::getRelatedTo($user->id, 'Streams/user/interests', 'Streams/interest'); if (!isset($myInterests)) { $myInterests = array(); } } if ($shouldUnsubscribe) { // TODO: implement mass unsubscribe foreach ($myInterests as $weight => $info) { Places_Nearby::unsubscribe($oldLatitude, $oldLongitude, $oldMiles, $info[0], array('transform' => array('Places_Interest', '_transform'), 'title' => $info[2], 'skipAccess' => true)); } $attributes['unsubscribed'] = Places_Nearby::unsubscribe($oldLatitude, $oldLongitude, $oldMiles); } if ($shouldSubscribe) { // TODO: implement mass subscribe foreach ($myInterests as $weight => $info) { Places_Nearby::subscribe($latitude, $longitude, $miles, $info[0], array('transform' => array('Places_Interest', '_transform'), 'create' => array('Places_Interest', '_create'), 'title' => $info[2], 'skipAccess' => true)); } $attributes['subscribed'] = Places_Nearby::subscribe($latitude, $longitude, $miles); } } $attributes['stream'] = $stream; Q_Response::setSlot('attributes', $attributes); Q::event("Places/geolocation", $attributes, 'after'); }
/** * Used by HTTP clients to create a new stream in the system. * @class Streams-stream * @method post * @param {array} [$params] Parameters that can come from the request * @param {string} $params.publisherId Required. The id of the user to publish the stream. * @param {string} $params.type Required. The type of the stream. * @param {string} [$params.Q_Streams_related_publisherId] Optionally indicate the publisher of the stream to relate the newly created to. Used together with the related.streamName option. * @param {string} [$params.Q_Streams_related_streamName] Optionally indicate the name of a stream to relate the newly crated stream to. This is often necessary in order to obtain permissions to create the stream. * @param {bool} [$params.dontSubscribe=false] Pass 1 or true here in order to skip auto-subscribing to the newly created stream. * @param {array} [$params.icon] This is used to upload a custom icon for the stream which will then be saved in different sizes. See fields for Q/image/post method * @param {string} [$params.icon.data] Required if $_FILES is empty. Base64-encoded data URI - see RFC 2397 * @param {string} [$params.icon.path="uploads"] parent path under web dir (see subpath) * @param {string} [$params.icon.subpath=""] subpath that should follow the path, to save the image under * @param {string} [$params.icon.merge=""] path under web dir for an optional image to use as a background * @param {string} [$params.icon.crop] array with keys "x", "y", "w", "h" to crop the original image * @param {string} [$params.icon.save=array("x" => "")] array of $size => $basename pairs * where the size is of the format "WxH", and either W or H can be empty. * @param {array} [$params.file] This is used to upload a custom icon for the stream which will then be saved in different sizes. See fields for Q/image/post method * @param {string} [$params.file.data] Required if $_FILES is empty. Base64-encoded data URI - see RFC 2397 * @param {string} [$params.file.path="uploads"] parent path under web dir (see subpath) * @param {string} [$params.file.subpath=""] subpath that should follow the path, to save the file under * @param {string} [$params.file.name] override name of the file, after the subpath */ function Streams_stream_post($params = array()) { $user = Users::loggedInUser(true); $publisherId = Streams::requestedPublisherId(); if (empty($publisherId)) { $publisherId = $_REQUEST['publisherId'] = $user->id; } $req = array_merge($_REQUEST, $params); $type = Streams::requestedType(true); $types = Q_Config::expect('Streams', 'types'); if (!array_key_exists($type, $types)) { throw new Q_Exception("This app doesn't support streams of type {$type}", 'type'); } if (empty($types[$type]['create'])) { throw new Q_Exception("This app doesn't support directly creating streams of type {$type}", 'type'); } // Should this stream be related to another stream? $relate = array(); $relate['streamName'] = Q_Request::special("Streams.related.streamName", null, $req); if (isset($relate['streamName'])) { $relate['publisherId'] = Q_Request::special("Streams.related.publisherId", $publisherId, $req); $relate['type'] = Q_Request::special("Streams.related.type", "", $req); $relate['weight'] = "+1"; // TODO: introduce ways to have "1" and "+1" for some admins etc. } // Hold on to any icon that was posted $icon = null; if (!empty($req['icon']) and is_array($req['icon'])) { $icon = $req['icon']; unset($req['icon']); } // Hold on to any file that was posted $file = null; if (!empty($req['file']) and is_array($req['file'])) { $file = $req['file']; unset($req['file']); } // Check if client can set the name of this stream if (!empty($req['name'])) { if ($user->id !== $publisherId or !Q_Config::get('Streams', 'possibleUserStreams', $req['name'], false)) { throw new Users_Exception_NotAuthorized(); } } // Create the stream $allowedFields = array_merge(array('publisherId', 'type', 'icon', 'file'), Streams::getExtendFieldNames($type, $user->id === $publisherId)); $fields = Q::take($req, $allowedFields); $stream = Streams::create($user->id, $publisherId, $type, $fields, $relate, $result); Q_Response::setSlot('messageTo', $result['messageTo']->exportArray()); // Process any icon that was posted if ($icon === true) { $icon = array(); } if (is_array($icon)) { if (empty($icon['path'])) { $icon['path'] = 'uploads/Streams'; } if (empty($icon['subpath'])) { $icon['subpath'] = "{$publisherId}/{$stream->name}/icon/" . time(); } Q_Response::setSlot('icon', Q::event("Q/image/post", $icon)); } // Process any file that was posted if ($file === true) { $file = array(); } if ($file) { if (empty($file['path'])) { $file['path'] = 'uploads/Streams'; } if (empty($file['subpath'])) { $file['subpath'] = "{$publisherId}/{$stream->name}/file/" . time(); } Q_Response::setSlot('file', Q::event("Q/file/post", $file)); } $file = Q::ifset($fieldNames, 'file', null); if (is_array($file)) { unset($fieldNames['file']); Q_Response::setSlot('file', Q::event("Q/file/post", $icon)); } // Re-fetch the stream object from the Streams::fetch cache, // since it might have been retrieved and modified to be different // from what is currently in $stream. // This also calculates the access levels on the stream. $stream = Streams::fetchOne($user->id, $publisherId, $stream->name); if (empty($req['dontSubscribe'])) { // autosubscribe to streams you yourself create, using templates $stream->subscribe(); } Streams::$cache['stream'] = $stream; }
/** * Some standard info to be stored in sessions, devices, etc. * @return {array} */ static function userAgentInfo() { $info = array('formFactor' => Q_Request::formFactor(), 'platform' => Q_Request::platform(), 'version' => Q_Request::OSVersion()); $fields = Q_Config::get('Q', 'session', 'userAgentInfo', array()); return Q::take($info, $fields); }