/** * Displays an HTML document that can be printed, ideally with line breaks. * Uses a particular view for the layout. * @param {array} $_REQUEST * @param {string} $_REQUEST.invitingUserId Required. The id of the user that generated the invitations with a call to Streams::invite. * @param {string} $_REQUEST.batch Required. The name of the batch under which invitations were saved during a call to Streams::invite. * @param {string} [$_REQUEST.limit=100] The maximum number of invitations to show on the page * @param {string} [$_REQUEST.offset=0] Used for paging * @param {string} [$_REQUEST.title='Invitations'] Override the title of the document * @param {string} [$_REQUEST.layout='default'] The name of the layout to use for the HTML document * @see Users::addLink() */ function Streams_invitations_response() { Q_Request::requireFields(array('batch', 'invitingUserId'), true); $invitingUserId = $_REQUEST['invitingUserId']; $batch = $_REQUEST['batch']; $title = Q::ifset($_REQUEST, 'layout', 'title'); $layoutKey = Q::ifset($_REQUEST, 'layout', 'default'); $limit = min(1000, Q::ifset($_REQUEST, 'limit', 100)); $offset = Q::ifset($_REQUEST, 'offset', 0); $layout = Q_Config::expect('Streams', 'invites', 'layout', $layoutKey); $app = Q_Config::expect('Q', 'app'); $pattern = Streams::invitationsPath($invitingUserId) . DS . $batch . DS . "*.html"; $filenames = glob($pattern); $parts = array(); foreach ($filenames as $f) { if (--$offset > 0) { continue; } $parts[] = file_get_contents($f); if (--$limit == 0) { break; } } $content = implode("\n\n<div class='Q_pagebreak Streams_invitations_separator'></div>\n\n", $parts); echo Q::view($layout, compact('content', 'parts')); return false; }
/** * This tool implements expandable containers that work on most modern browsers, * including ones on touchscreens. * @class Q expandable * @constructor * @param {array} $options Options for the tool * @param {string} $options.title Required. The title for the expandable. * @param {string} $options.content The content. Required unless you pass "items" instead. * @param {array} [$options.items] An array of strings to wrap in <span> elements and render in the content * @param {string} [$options.class] If you use "items", optionally specify the class of the container elements for each item * @param {integer} [$options.title] A number, if any, to display when collapsed * @param {boolean} [$options.autoCollapseSiblings] Whether, when expanding an expandable, its siblings should be automatically collapsed. */ function Q_expandable_tool($options) { if (isset($options['items'])) { $classString = isset($options['class']) ? "class='{$options['class']}'" : ''; $lines = array(); foreach ($options['items'] as $key => $value) { $lines[] = "<span {$classString}>{$key}</span>"; } $between = Q::ifset($options, 'between', ''); $options['content'] = implode($between, $lines); } foreach (array('title', 'content') as $field) { if (!isset($options[$field])) { throw new Q_Exception_RequiredField(compact('field')); } } Q_Response::addScript('plugins/Q/js/tools/expandable.js'); Q_Response::addStylesheet('plugins/Q/css/expandable.css'); $count = Q::ifset($options, 'count', ''); $style = empty($options['expanded']) ? '' : 'style="display:block"'; $h2 = "<h2>\n\t<span class='Q_expandable_count'>{$count}</span>\n\t{$options['title']}\n</h2>"; $div = "<div class='Q_expandable_container' {$style}><div class='Q_expandable_content'>\n\t{$options['content']}\n</div></div>"; Q_Response::setToolOptions($options); return $h2 . $div; }
/** * Tool for admins to edit the url, title, keywords, description of the current page * @class Websites seo * @constructor * @param {Object} [$options] Options for the tool * @param {String} [$options.skipIfNotAuthorized=true] Whether to skip rendering the contents of the tool if the logged-in user is not authorized to edit the SEO information for this page. */ function Websites_seo_tool($options) { $skipIfNotAuthorized = Q::ifset($options, 'skipIfNotAuthorized', true); if ($skipIfNotAuthorized) { $websitesUserId = Users::communityId(); $sha1 = sha1(Q_Dispatcher::uri()); $seoStreamName = "Websites/seo/{$sha1}"; $stream = Streams::fetchOne(null, $websitesUserId, $seoStreamName); $user = Users::loggedInUser(); if (!$user or $stream and !$stream->testWriteLevel('suggest')) { $options['skip'] = true; } if (!$stream and !Streams::isAuthorizedToCreate($user->id, $websitesUserId, 'Websites/seo')) { $options['skip'] = true; } } unset($options['skipIfNotAuthorized']); Q_Response::addStylesheet('plugins/Websites/css/Websites.css'); Q_Response::addScript("plugins/Websites/js/Websites.js"); Q_Response::setToolOptions($options); $user = Users::loggedInUser(false, false); $userId = $user ? $user->id : ""; $communityId = Users::communityId(); $sha1 = sha1(Q_Dispatcher::uri()); $seoStreamName = "Websites/seo/{$sha1}"; $streams = Streams::fetch($userId, $communityId, array("Websites/header", "Websites/title", "Websites/slogan", $seoStreamName)); foreach ($streams as $name => $s) { if ($s) { $s->addPreloaded($userId); } } }
function Streams_interests_response() { // serve a javascript file and tell client to cache it $app = Q_Config::expect('Q', 'app'); $communityId = Q::ifset($_REQUEST, 'communityId', $app); $tree = new Q_Tree(); $tree->load("files/Streams/interests/{$communityId}.json"); $categories = $tree->getAll(); foreach ($categories as $category => &$v1) { foreach ($v1 as $k2 => &$v2) { if (!Q::isAssociative($v2)) { ksort($v1); break; } ksort($v2); } } header('Content-Type: text/javascript'); header("Pragma: ", true); // 1 day header("Cache-Control: public, max-age=86400"); // 1 day $expires = date("D, d M Y H:i:s T", time() + 86400); header("Expires: {$expires}"); // 1 day $json = Q::json_encode($categories, true); echo "Q.setObject(['Q', 'Streams', 'Interests', 'all', '{$communityId}'], {$json});"; return false; }
function Streams_message_response_messages() { if (isset(Streams::$cache['message'])) { $message = Streams::$cache['message']; return Db::exportArray(array($message->ordinal => $message)); } if (isset(Streams::$cache['messages'])) { return Db::exportArray(Streams::$cache['messages']); } $publisherId = Streams::requestedPublisherId(true); $streamName = Streams::requestedName(true); $type = Streams::requestedMessageType(); $stream = Q::ifset(Streams::$cache, 'stream', Streams::fetchOne(null, $publisherId, $streamName, true)); $maxLimit = Streams_Stream::getConfigField($type, 'getMessagesLimit', 100); $limit = min($maxLimit, Q::ifset($_REQUEST, 'limit', $maxLimit)); if (isset($_REQUEST['ordinal'])) { $min = $_REQUEST['ordinal']; $limit = 1; } if (isset($_REQUEST['min'])) { $min = $_REQUEST['min']; } $max = isset($_REQUEST['max']) ? $_REQUEST['max'] : -1; if (isset($_REQUEST['ascending'])) { $ascending = $_REQUEST['ascending']; } if (!$stream->testReadLevel('messages')) { throw new Users_Exception_NotAuthorized(); } $messages = $stream->getMessages(compact('type', 'min', 'max', 'limit', 'ascending')); return Db::exportArray($messages); }
/** * Edits a label in the system. Fills the "label" (and possibly "icon") slot. * @param {array} $_REQUEST * @param {string} $_REQUEST.label The label * @param {string} [$_REQUEST.title] The title of the label * @param {string} [$_REQUEST.icon] Optional path to an icon * @param {string} [$_REQUEST.userId=Users::loggedInUser(true)->id] You can override the user id, if another plugin adds a hook that allows you to do this */ function Users_label_put($params = array()) { $req = array_merge($_REQUEST, $params); Q_Request::requireFields(array('label'), $req, true); $loggedInUserId = Users::loggedInUser(true)->id; $userId = Q::ifset($req, 'userId', $loggedInUserId); $l = $req['label']; $icon = Q::ifset($req, 'icon', null); $title = Q::ifset($req, 'title', null); Users::canManageLabels($loggedInUserId, $userId, $l, true); $label = new Users_Label(); $label->userId = $userId; $label->label = $l; if (!$label->retrieve()) { throw new Q_Exception_MissingRow(array('table' => 'Label', 'criteria' => json_encode($label->fields))); } if (isset($title)) { $label->title = $title; } if (is_array($icon)) { // Process any icon data $icon['path'] = 'uploads/Users'; $icon['subpath'] = "{$userId}/label/{$label}/icon"; $data = Q::event("Q/image/post", $icon); Q_Response::setSlot('icon', $data); $label->icon = Q_Request::baseUrl() . '/' . $data['']; } $label->save(); Q_Response::setSlot('label', $label->exportArray()); }
/** * This tool generates an HTML article viewer that lets authorized users edit the article. * @class Websites article * @constructor * @param {Object} [$options] parameters for the tool * @param {String} $options.publisherId The article publisher's user id * @param {String} $options.streamName The article's stream name * @param {String} $options.stream The article's stream, if it is already fetched * @param {String} [$options.html=array()] Any additional for the Streams/html editor * @param {String} [$options.getintouch=array()] Additional options for the Users/getintouch tool, in case it's rendered */ function Websites_article_tool($options) { $publisherId = $options['publisherId']; $streamName = $options['streamName']; $article = Q::ifset($options, 'stream', Streams::fetchOne(null, $publisherId, $streamName)); if (!$article) { throw new Q_Exception_MissingRow(array('table' => 'article', 'criteria' => $streamName)); } $getintouch = array_merge(array('user' => $article->userId, 'email' => true, 'sms' => true, 'call' => true, 'between' => "", 'emailSubject' => 'Reaching out from your website', 'class' => 'Q_button Q_clickable'), Q::ifset($options, 'getintouch', array())); $canView = $article->testReadLevel('content'); $canEdit = $article->testWriteLevel('edit'); if ($article->getintouch) { if (is_array($git = json_decode($article->getintouch, true))) { $getintouch = array_merge($getintouch, $git); } } $getintouch['class'] = 'Q_button'; if (!$canView) { throw new Users_Exception_NotAuthorized(); } $html = Q::ifset($options, 'html', array()); $article->addPreloaded(); Q_Response::addStylesheet('plugins/Websites/css/Websites.css'); Q_Response::addScript("plugins/Websites/js/Websites.js"); Q_Response::setToolOptions($options); return Q::view("Websites/tool/article.php", compact('article', 'getintouch', 'canEdit', 'canView', 'html')); }
function Streams_invite_response_data() { if (isset(Streams::$cache['invited'])) { return Streams::$cache['invited']; } $user = Users::loggedInUser(true); $publisherId = Streams::requestedPublisherId(); $streamType = Streams::requestedType(); $invitingUserId = Streams::requestedField('invitingUserId'); $limit = Q::ifset($_REQUEST, 'limit', Q_Config::get('Streams', 'invites', 'limit', 100)); $invited = Streams_Invited::select('*')->where(array('userId' => $user->id, 'state' => 'pending', 'expireTime <' => new Db_Expression('CURRENT_TIMESTAMP')))->limit($limit)->fetchDbRows(null, null, 'token'); $query = Streams_Invite::select('*')->where(array('token' => array_keys($invited))); if (isset($publisherId)) { $query = $query->where(array('publisherId' => $publisherId)); } if (isset($streamType)) { $query = $query->where(array('streamName' => new Db_Range($streamType . '/', true, false, true))); } if (isset($invitingUserId)) { $query = $query->where(array('invitingUserId' => $invitingUserId)); } $invites = $query->fetchDbRows(); $streams = array(); foreach ($invites as $invite) { $stream = new Streams_Stream(); $stream->publisherId = $invite->publisherId; $stream->name = $invite->streamName; if ($stream->retrieve()) { $streams[$invite->token] = $stream->exportArray(); $streams[$invite->token]['displayName'] = $invite->displayName; } } return compact('streams', 'invites'); }
/** * Displays an HTML document that can be printed, ideally with line breaks. * Uses a particular view for the layout. * @param {array} $_REQUEST * @param {string} $_REQUEST.invitingUserId Required. The id of the user that generated the invitations with a call to Streams::invite. * @param {string} $_REQUEST.batch Required. The name of the batch under which invitations were saved during a call to Streams::invite. * @param {string} [$_REQUEST.limit=100] The maximum number of invitations to show on the page * @param {string} [$_REQUEST.offset=0] Used for paging * @param {string} [$_REQUEST.title='Invitations'] Override the title of the document * @param {string} [$_REQUEST.layout='default'] The name of the layout to use for the HTML document * @see Users::addLink() */ function Streams_invitations_response() { Q_Request::requireFields(array('batch', 'invitingUserId'), true); $invitingUserId = $_REQUEST['invitingUserId']; $batch = $_REQUEST['batch']; $user = Users::loggedInUser(true); $stream = Streams::fetchOne(null, $invitingUserId, 'Streams/invitations', true); if (!$stream->testReadLevel('content')) { throw new Users_Exception_NotAuthorized(); } $title = Q::ifset($_REQUEST, 'layout', 'title'); $layoutKey = Q::ifset($_REQUEST, 'layout', 'default'); $limit = min(1000, Q::ifset($_REQUEST, 'limit', 100)); $offset = Q::ifset($_REQUEST, 'offset', 0); $layout = Q_Config::expect('Streams', 'invites', 'layout', $layoutKey); $pattern = Streams::invitationsPath($invitingUserId) . DS . $batch . DS . "*.html"; $filenames = glob($pattern); $parts = array(); foreach ($filenames as $f) { if (--$offset > 0) { continue; } $parts[] = file_get_contents($f); if (--$limit == 0) { break; } } $content = implode("\n\n<div class='Q_pagebreak Streams_invitations_separator'></div>\n\n", $parts); echo Q::view($layout, compact('title', 'content', 'parts')); return false; }
function Users_identifier_post() { $userId = Q::ifset($_REQUEST, 'userId', null); if (isset($userId)) { $user = Users_User::fetch($userId, true); if ($user->emailAddress or $user->mobileNumber) { throw new Q_Exception("This user is already able to log in and set their own email and mobile number."); } } else { $user = Users::loggedInUser(true); } $app = Q_Config::expect('Q', 'app'); $fields = array(); $identifier = Users::requestedIdentifier($type); if (!$type) { throw new Q_Exception("a valid email address or mobile number is required", array('identifier', 'mobileNumber', 'emailAddress')); } if ($type === 'email') { $subject = Q_Config::get('Users', 'transactional', 'identifier', 'subject', "Welcome! Verify your email address."); $view = Q_Config::get('Users', 'transactional', 'identifier', 'body', 'Users/email/addEmail.php'); $user->addEmail($identifier, $subject, $view, array(), array('html' => true)); } else { if ($type === 'mobile') { $view = Q_Config::get('Users', 'transactional', 'identifier', 'sms', 'Users/sms/addMobile.php'); $user->addMobile($identifier, $view); } } }
function Streams_stream_response_Q_inplace() { $stream = isset(Streams::$cache['stream']) ? Streams::$cache['stream'] : null; if (!$stream) { throw new Exception("No stream"); } if (isset($_REQUEST['title'])) { $result = $stream->title; } else { if (isset($_REQUEST['attributes'])) { if (is_array($_REQUEST['attributes'])) { reset($_REQUEST['attributes']); $result = $stream->getAttribute(key($_REQUEST['attributes'])); } else { $result = $stream->attributes; } } else { $fieldNames = array_diff(Streams::getExtendFieldNames($stream->type), array('insertedTime', 'updatedTime')); $field = 'content'; foreach ($fieldNames as $f) { if (isset($_REQUEST[$f])) { $field = $f; break; } } $result = $stream->{$field}; } } $convert = Q::ifset($_REQUEST, 'convert', '["\\n"]'); return Q_Html::text($result, json_decode($convert, true)); }
/** * Adds a label to the system. Fills the "label" (and possibly "icon") slot. * @param {array} $_REQUEST * @param {string} $_REQUEST.title The title of the label * @param {string} [$_REQUEST.label] You can override the label to use * @param {string} [$_REQUEST.icon] Optional path to an icon * @param {string} [$_REQUEST.userId=Users::loggedInUser(true)->id] You can override the user id, if another plugin adds a hook that allows you to do this */ function Users_label_post($params = array()) { $req = array_merge($_REQUEST, $params); Q_Request::requireFields(array('title'), $req, true); $loggedInUserId = Users::loggedInUser(true)->id; $userId = Q::ifset($req, 'userId', $loggedInUserId); $icon = Q::ifset($req, 'icon', null); $title = $req['title']; $l = Q::ifset($req, 'label', 'Users/' . Q_Utils::normalize($title)); Users::canManageLabels($loggedInUserId, $userId, $l, true); $label = new Users_Label(); $label->userId = $userId; $label->label = $l; if ($label->retrieve()) { throw new Users_Exception_LabelExists(); } $label->title = $title; if (is_array($icon)) { // Process any icon that was posted $icon['path'] = 'uploads/Users'; $icon['subpath'] = "{$userId}/label/{$label}/icon"; $data = Q::event("Q/image/post", $icon); Q_Response::setSlot('icon', $data); $label->icon = Q_Request::baseUrl() . '/' . $data['']; } else { $label->icon = 'default'; } $label->save(); Q_Response::setSlot('label', $label->exportArray()); }
/** * Standard tool for starting or managing subscriptions. * @class Assets subscription * @constructor * @param {array} $options Override various options for this tool * @param {string} $options.payments can be "authnet" or "stripe" * @param {string} $options.planStreamName the name of the subscription plan's stream * @param {string} [$options.publisherId=Q.Users.communityId] the publisher of the subscription plan's stream * @param {string} [$options.subscribeButton] Can override the title of the subscribe button * @param {array} [$options=array()] Any additional options * @param {string} [$options.token=null] required unless the user is an existing customer */ function Assets_subscription_tool($options) { if (empty($options['payments'])) { throw new Q_Exception_RequiredField(array('field' => 'payments'), 'payments'); } $payments = ucfirst($options['payments']); $lcpayments = strtolower($payments); $currency = strtolower(Q::ifset($options, 'currency', 'usd')); if ($payments === 'Authnet' and $currency !== 'usd') { throw new Q_Exception("Authnet doesn't support currencies other than USD", 'currency'); } $className = "Assets_Payments_{$payments}"; switch ($payments) { case 'Authnet': $adapter = new $className($options); $token = $options['token'] = $adapter->authToken(); $testing = $options['testing'] = Q_Config::expect('Assets', 'payments', $lcpayments, 'testing'); $action = $options['action'] = $testing ? "https://test.authorize.net/profile/manage" : "https://secure.authorize.net/profile/manage"; break; case 'Stripe': $publishableKey = Q_Config::expect('Assets', 'payments', 'stripe', 'publishableKey'); break; } $titles = array('Authnet' => 'Authorize.net', 'Stripe' => 'Stripe'); $subscribeButton = Q::ifset($options, 'subscribeButton', "Subscribe with " . $titles[$payments]); Q_Response::setToolOptions($options); return Q::view("Assets/tool/subscription/{$payments}.php", compact('token', 'publishableKey', 'action', 'paymentButton', 'subscribeButton', 'planStreamName')); }
function Streams_after_Q_objects() { $user = Users::loggedInUser(); if (!$user) { return; } $invite = Streams::$followedInvite; if (!$invite) { return; } $displayName = $user->displayName(); if ($displayName) { return; } $stream = new Streams_Stream(); $stream->publisherId = $invite->publisherId; $stream->name = $invite->streamName; if (!$stream->retrieve()) { throw new Q_Exception_MissingRow(array('table' => 'stream', 'criteria' => 'with that name'), 'streamName'); } // Prepare the complete invite dialog $invitingUser = Users_User::fetch($invite->invitingUserId); list($relations, $related) = Streams::related($user->id, $stream->publisherId, $stream->name, false); $params = array('displayName' => null, 'action' => 'Streams/basic', 'icon' => $user->iconUrl(), 'token' => $invite->token, 'user' => array('icon' => $invitingUser->iconUrl(), 'displayName' => $invitingUser->displayName(array('fullAccess' => true))), 'stream' => $stream->exportArray(), 'relations' => Db::exportArray($relations), 'related' => Db::exportArray($related)); $config = Streams_Stream::getConfigField($stream->type, 'invite', array()); $defaults = Q::ifset($config, 'dialog', array()); $tree = new Q_Tree($defaults); if ($tree->merge($params)) { $dialogData = $tree->getAll(); if ($dialogData) { Q_Response::setScriptData('Q.plugins.Streams.invite.dialog', $dialogData); Q_Response::addTemplate('Streams/invite/complete'); } } }
function Users_activate_validate() { $uri = Q_Dispatcher::uri(); $emailAddress = Q::ifset($_REQUEST, 'e', $uri->emailAddress); $mobileNumber = Q::ifset($_REQUEST, 'm', $uri->mobileNumber); if ($emailAddress && !Q_Valid::email($emailAddress, $e_normalized, array('no_ip' => 'false'))) { throw new Q_Exception_WrongValue(array('field' => 'email', 'range' => 'a valid email address'), 'emailAddress'); } if ($mobileNumber && !Q_Valid::phone($mobileNumber, $m_normalized)) { throw new Q_Exception_WrongValue(array('field' => 'mobile phone', 'range' => 'a valid phone number'), 'mobileNumber'); } if ($emailAddress or $mobileNumber) { if (empty($_REQUEST['code'])) { throw new Q_Exception("The activation code is missing"); } } else { throw new Q_Exception("The contact information is missing"); } if (!empty($e_normalized)) { Users::$cache['emailAddress'] = $e_normalized; } if (!empty($m_normalized)) { Users::$cache['mobileNumber'] = $m_normalized; } }
/** * Used to create a new stream * * @param {array} $_REQUEST * @param {String} [$_REQUEST.title] Required. The title of the interest. * @param {String} [$_REQUEST.publisherId] Optional. Defaults to the app name. * @return {void} */ function Streams_interest_delete() { $user = Users::loggedInUser(true); $title = Q::ifset($_REQUEST, 'title', null); if (!isset($title)) { throw new Q_Exception_RequiredField(array('field' => 'title')); } $app = Q_Config::expect('Q', 'app'); $publisherId = Q::ifset($_REQUEST, 'publisherId', $app); $name = 'Streams/interest/' . Q_Utils::normalize($title); $stream = Streams::fetchOne(null, $publisherId, $name); if (!$stream) { throw new Q_Exception_MissingRow(array('table' => 'stream', 'criteria' => Q::json_encode(compact('publisherId', 'name')))); } $miPublisherId = $user->id; $miName = 'Streams/user/interests'; $myInterests = Streams::fetchOne($user->id, $miPublisherId, $miName); if (!$myInterests) { throw new Q_Exception_MissingRow(array('table' => 'stream', 'criteria' => Q::json_encode(array('publisherId' => $miPublisherId, 'name' => $miName)))); } $stream->leave(); Streams::unrelate($user->id, $user->id, 'Streams/user/interests', 'Streams/interest', $publisherId, $name, array('adjustWeights' => true)); Q_Response::setSlot('publisherId', $publisherId); Q_Response::setSlot('streamName', $name); /** * Occurs when the logged-in user has successfully removed an interest via HTTP * @event Streams/interest/delete {after} * @param {string} publisherId The publisher of the interest stream * @param {string} title The title of the interest * @param {Users_User} user The logged-in user * @param {Streams_Stream} stream The interest stream * @param {Streams_Stream} myInterests The user's "Streams/user/interests" stream */ Q::event("Streams/interest/remove", compact('publisherId', 'title', 'subscribe', 'user', 'stream', 'myInterests'), 'after'); }
/** * Standard tool for making payments. * @class Assets payment * @constructor * @param {array} $options Override various options for this tool * @param {string} $options.payments can be "authnet" or "stripe" * @param {string} $options.amount the amount to pay. * @param {double} [$options.currency="usd"] the currency to pay in. (authnet supports only "usd") * @param {string} [$options.payButton] Can override the title of the pay button * @param {String} [$options.publisherId=Users::communityId()] The publisherId of the Assets/product or Assets/service stream * @param {String} [$options.streamName] The name of the Assets/product or Assets/service stream * @param {string} [$options.name=Users::communityName()] The name of the organization the user will be paying * @param {string} [$options.image] The url pointing to a square image of your brand or product. The recommended minimum size is 128x128px. * @param {string} [$options.description=null] A short name or description of the product or service being purchased. * @param {string} [$options.panelLabel] The label of the payment button in the Stripe Checkout form (e.g. "Pay {{amount}}", etc.). If you include {{amount}}, it will be replaced by the provided amount. Otherwise, the amount will be appended to the end of your label. * @param {string} [$options.zipCode] Specify whether Stripe Checkout should validate the billing ZIP code (true or false). The default is false. * @param {boolean} [$options.billingAddress] Specify whether Stripe Checkout should collect the user's billing address (true or false). The default is false. * @param {boolean} [$options.shippingAddress] Specify whether Checkout should collect the user's shipping address (true or false). The default is false. * @param {string} [$options.email=Users::loggedInUser(true)->emailAddress] You can use this to override the email address, if any, provided to Stripe Checkout to be pre-filled. * @param {boolean} [$options.allowRememberMe=true] Specify whether to include the option to "Remember Me" for future purchases (true or false). * @param {boolean} [$options.bitcoin=false] Specify whether to accept Bitcoin (true or false). * @param {boolean} [$options.alipay=false] Specify whether to accept Alipay ('auto', true, or false). * @param {boolean} [$options.alipayReusable=false] Specify if you need reusable access to the customer's Alipay account (true or false). */ function Assets_payment_tool($options) { Q_Valid::requireFields(array('payments', 'amount'), $options, true); if (empty($options['name'])) { $options['name'] = Users::communityName(); } if (!empty($options['image'])) { $options['image'] = Q_Html::themedUrl($options['image']); } $options['payments'] = strtolower($options['payments']); if (empty($options['email'])) { $options['email'] = Users::loggedInUser(true)->emailAddress; } $payments = ucfirst($options['payments']); $currency = strtolower(Q::ifset($options, 'currency', 'usd')); if ($payments === 'Authnet' and $currency !== 'usd') { throw new Q_Exception("Authnet doesn't support currencies other than USD", 'currency'); } $className = "Assets_Payments_{$payments}"; switch ($payments) { case 'Authnet': $adapter = new $className($options); $token = $options['token'] = $adapter->authToken(); $testing = $options['testing'] = Q_Config::expect('Assets', 'payments', $lcpayments, 'testing'); $action = $options['action'] = $testing ? "https://test.authorize.net/profile/manage" : "https://secure.authorize.net/profile/manage"; break; case 'Stripe': $publishableKey = Q_Config::expect('Assets', 'payments', 'stripe', 'publishableKey'); break; } $titles = array('Authnet' => 'Authorize.net', 'Stripe' => 'Stripe'); Q_Response::setToolOptions($options); $payButton = Q::ifset($options, 'payButton', "Pay with " . $titles[$payments]); return Q::view("Assets/tool/payment/{$payments}.php", compact('token', 'publishableKey', 'action', 'payButton')); }
function Streams_player_Websites_article($options) { $stream = $options['stream']; $emailSubject = Q::ifset($options, 'emailSubject', "Reaching out from your website"); $result = Q::tool('Websites/article', array('publisherId' => $stream->publisherId, 'streamName' => $stream->name, 'html' => array('placeholder' => 'Start editing the article here. Select some text to use the HTML editor.', 'froala' => array('key' => Q_Config::get('Streams', 'froala', 'key', null), 'pasteImage' => true)), 'getintouch' => array('email' => true, 'emailSubject' => $emailSubject, 'sms' => 'Text', 'call' => 'Call', 'class' => 'Q_button clickable'))); $result .= Q::tool("Websites/seo", array('skipIfNotAuthorized' => true, 'publisherId' => $stream->publisherId, 'streamName' => $stream->name)); return $result; }
/** * 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; }
/** * Removes a contact from the system. * @param {array} $_REQUEST * @param {string} $_REQUEST.label The label of the contact * @param {string} $_REQUEST.contactUserId The contactUserId of the contact * @param {string} [$_REQUEST.userId=Users::loggedInUser(true)->id] You can override the user id, if another plugin adds a hook that allows you to do this */ function Users_contact_delete($params = array()) { $req = array_merge($_REQUEST, $params); Q_Request::requireFields(array('label', 'contactUserId'), $req, true); $loggedInUserId = Users::loggedInUser(true)->id; $userId = Q::ifset($req, 'userId', $loggedInUserId); $label = $req['label']; $contactUserId = $req['contactUserId']; return !!Users_Contact::removeContact($userId, $label, $contactUserId); }
function Streams_register_response_data() { $user = Q::ifset(Users::$cache['user']); if (!$user) { return array('user' => null); } $u = $user->exportArray(); $u['displayName'] = Streams::displayName($user); return array('user' => $u); }
/** * 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; }
/** * We are going to implement a subset of the OAuth 1.0a functionality for now, * and later we can expand it to match the full OAuth specification. */ function Users_authorize_response() { if (Q_Response::getErrors()) { Q_Dispatcher::showErrors(); } $response_type = 'token'; $token_type = 'bearer'; $client_id = $_REQUEST['client_id']; $state = $_REQUEST['state']; $skip = Q::ifset($_REQUEST, 'skip', false); $scope = Users_OAuth::requestedScope(true, $scopes); $client = Users_User::fetch($client_id, true); if (!$client) { throw new Q_Exception_MissingRow(array('table' => 'client user', 'criteria' => "id = '{$client_id}'"), 'client_id'); } if (empty($client->url)) { throw new Q_Exception("Client app needs to register url", 'client_id'); } $redirect_uri = Q::ifset($_REQUEST, 'redirect_uri', $client->url); $user = Users::loggedInUser(); $oa = null; if (isset(Users::$cache['oAuth'])) { $oa = Users::$cache['oAuth']; } else { if ($user) { $oa = new Users_OAuth(); $oa->client_id = $client_id; $oa->userId = $user->id; $oa->state = $state; $oa = $oa->retrieve(); } } $remaining = $scope; if ($oa and $oa->wasRetrieved()) { // User is logged in and already has a token for this client_id and state $paths = Q_Config::get('Users', 'authorize', 'clients', Q::app(), 'redirectPaths', false); $path = substr($redirect_uri, strlen($client->url) + 1); $p = array('response_type' => $response_type, 'token_type' => $token_type, 'access_token' => $oa->access_token, 'expires_in' => $oa->token_expires_seconds, 'scope' => implode(' ', $scope), 'state' => $oa->state); $p = Q_Utils::sign($p, 'Q.Users.oAuth'); // the redirect uri could be a native app url scheme $s = strpos($redirect_uri, '#') === false ? '#' : '&'; $redirect_uri = Q_Uri::from($redirect_uri . $s . http_build_query($p), false)->toUrl(); if (!Q::startsWith($redirect_uri, $client->url) or is_array($paths) and !in_array($path, $paths)) { throw new Users_Exception_Redirect(array('uri' => $redirect_uri)); } Q_Response::redirect($redirect_uri); return false; } $terms_label = Users::termsLabel('authorize'); Q_Response::setScriptData('Q.Users.authorize', compact('client_id', 'redirect_uri', 'scope', 'scopes', 'remaining', 'state', 'response_type', 'skip')); $content = Q::view('Users/content/authorize.php', compact('client', 'user', 'redirect_uri', 'scope', 'scopes', 'remaining', 'state', 'terms_label', 'response_type', 'skip')); Q_Response::setSlot('content', $content); Q_Response::setSlot('column0', $content); return true; }
/** * Implements an input that filters an associated list (like an autocomplete) * @class Q filter * @constructor * @param {array} [$options] Override various options for this tool * @param {String} [$options.name=filter] The name of the text input * @param {String} [$options.value=''] The initial value of the text input * @param {String} [$options.placeholder] Any placeholder text * @param {array} [$options.placeholders={}] Options for Q/placeholders, or null to omit it * @param {String} [$options.results=''] HTML to display in the results initially. If setting them later, remember to call stateChanged('results') * @param {Q.Event} [$options.onFilter] Name of a JS event handler that is meant to fetch and update results by editing the contents of the element pointed to by the second argument. The first argument is the content of the text input. * @return {string} */ function Q_filter_tool($options) { Q_Response::setToolOptions($options); $name = Q::ifset($options, 'name', 'filter'); $value = Q::ifset($options, 'value', ''); $placeholder = Q::ifset($options, 'placeholder', 'filter'); $class = 'Q_filter_input'; Q_Response::addScript('plugins/Q/js/tools/filter.js'); Q_Response::addStylesheet('plugins/Q/css/filter.css'); return Q_Html::input($name, $value, compact('placeholder', 'class')) . '<div class="Q_filter_results"></div>'; }
/** * Adds contacts to the system. Fills the "contacts" slot. * @param {array} $_REQUEST * @param {string} $_REQUEST.label The label of the contact * @param {string} $_REQUEST.contactUserId The contactUserId of the contact * @param {string} [$_REQUEST.nickname] The nickname of the contact * @param {string} [$_REQUEST.userId=Users::loggedInUser(true)->id] You can override the user id, if another plugin adds a hook that allows you to do this */ function Users_contact_post($params = array()) { $req = array_merge($_REQUEST, $params); Q_Request::requireFields(array('label', 'contactUserId'), $req, true); $loggedInUserId = Users::loggedInUser(true)->id; $userId = Q::ifset($req, 'userId', $loggedInUserId); $contactUserId = $req['contactUserId']; $nickname = Q::ifset($req, 'nickname', null); $contacts = Users_Contact::addContact($userId, $req['label'], $contactUserId, $nickname); Q_Response::setSlot('contacts', Db::exportArray($contacts)); }
function Broadcast_widget_response_widget() { if (!empty($_REQUEST['css'])) { Q_Response::addStylesheet($_REQUEST['css']); } $explanation = Q::ifset($_REQUEST, 'explanation', Q_Config::get('Broadcast', 'text', 'explanation', '')); $button = Q::ifset($_REQUEST, 'button', Q_Config::get('Broadcast', 'text', 'button', '')); $checkbox = Q::ifset($_REQUEST, 'checkbox', Q_Config::get('Broadcast', 'text', 'checkbox', '')); Q_Response::addScript('plugins/Broadcast/js/Broadcast.js'); return Q::view('Broadcast/widget/widget.php', compact('explanation', 'button', 'checkbox')); }
function Streams_access_response_data() { $user = Users::loggedInUser(true); $publisherId = Streams::requestedPublisherId(true); $streamName = Streams::requestedName(true); $stream = Streams::fetchOne($user->id, $publisherId, $streamName); if (!$stream->testAdminLevel('own')) { throw new Users_Exception_NotAuthorized(); } return array('access' => Q::ifset(Streams::$cache, 'access', null)); }
static function helperTool($template, $context, $args, $source) { if (empty($args[0])) { return "{{tool missing name}}"; } $name = $args[0]; $id = count($args) > 1 && is_string($args[1]) ? $args[1] : null; $options = Q::ifset($args, 'hash', array()); $fields = $context->fields(); $o = array_merge($options, Q::ifset($fields, $name, array()), Q::ifset($fields, "id:{$id}", array())); return Q::tool($name, $o, compact('id')); }
/** * Edits a label in the system. Fills the "label" (and possibly "icon") slot. * @param {array} $_REQUEST * @param {string} $_REQUEST.label The label * @param {string} [$_REQUEST.title] The title of the label * @param {string} [$_REQUEST.icon] Optional path to an icon * @param {string} [$_REQUEST.userId=Users::loggedInUser(true)->id] You can override the user id, if another plugin adds a hook that allows you to do this */ function Users_label_put($params = array()) { $req = array_merge($_REQUEST, $params); Q_Request::requireFields(array('label'), $req, true); $loggedInUserId = Users::loggedInUser(true)->id; $userId = Q::ifset($req, 'userId', $loggedInUserId); $l = $req['label']; $icon = Q::ifset($req, 'icon', null); $title = Q::ifset($req, 'title', null); $label = Users_Label::updateLabel($userId, $l, compact('icon', 'title')); Q_Response::setSlot('label', $label->exportArray()); }