Пример #1
0
/**
 * 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;
}
Пример #2
0
/**
 * 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);
}
Пример #3
0
 /**
  * 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;
 }
Пример #4
0
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);
}
Пример #5
0
/**
 * 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);
    }
}
Пример #8
0
/**
 * 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;
}
Пример #9
0
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");
}
Пример #10
0
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'));
}
Пример #11
0
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);
    }
}
Пример #12
0
 /**
  * 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;
 }
Пример #13
0
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'));
}
Пример #14
0
/**
 * 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;
}
Пример #15
0
/**
 * 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;
}
Пример #16
0
/**
 * 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');
}
Пример #17
0
/**
 * 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);
    }
}
Пример #18
0
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']);
}
Пример #19
0
/**
 * 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');
}
Пример #20
0
/**
 * 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;
}
Пример #21
0
 /**
  * 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);
 }