function Streams_0_8_1_Streams_mysql() { $app = Q_Config::expect('Q', 'app'); // template for community stream $stream = new Streams_Stream(); $stream->publisherId = ''; $stream->name = 'Streams/community/'; $stream->type = 'Streams/template'; $stream->title = "Community"; $stream->content = ''; $readLevel = Streams::$READ_LEVEL['content']; $writeLevel = Streams::$WRITE_LEVEL['join']; $adminLevel = Streams::$ADMIN_LEVEL['invite']; $stream->save(); // app community stream, for announcements $stream = new Streams_Stream(); $stream->publisherId = $app; $stream->name = 'Streams/community/main'; $stream->type = 'Streams/community'; $stream->title = "{$app} Community"; $stream->save(); // symlink the labels folder $cwd = getcwd(); chdir(USERS_PLUGIN_FILES_DIR . DS . 'Users' . DS . 'icons'); if (!file_exists('Streams')) { symlink(STREAMS_PLUGIN_FILES_DIR . DS . 'Streams' . DS . 'icons' . DS . 'labels' . DS . 'Streams', 'Streams'); } chdir($cwd); }
function Websites_seo_post() { if (empty($_REQUEST['streamName'])) { throw new Q_Exception_RequiredField(array('field' => 'streamName')); } $prefix = "Websites/seo/"; if (substr($_REQUEST['streamName'], 0, strlen($prefix)) !== $prefix) { throw new Q_Exception_WrongValue(array('field' => 'streamName', 'range' => "string beginning with {$prefix}")); } $user = Users::loggedInUser(true); $publisherId = Users::communityId(); $type = "Websites/seo"; if (!Streams::isAuthorizedToCreate($user->id, $publisherId, $type)) { throw new Users_Exception_NotAuthorized(); } $stream = new Streams_Stream($publisherId); $stream->publisherId = $publisherId; $stream->name = $_REQUEST['streamName']; $stream->type = $type; if (isset($_REQUEST['uri'])) { $stream->setAttribute('uri', $_REQUEST['uri']); } $stream->save(); $stream->post($user->id, array('type' => 'Streams/created', 'content' => '', 'instructions' => Q::json_encode($stream->toArray())), true); $stream->subscribe(); // autosubscribe to streams you yourself create, using templates Q_Response::setSlot('stream', $stream->exportArray()); }
function Streams_0_8_4_Streams_mysql() { $app = Q_Config::expect('Q', 'app'); $communityId = Users::communityId(); $user = Users_User::fetch($communityId); // avatar for the App user $avatar = new Streams_Avatar(); $avatar->toUserId = $communityId; $avatar->publisherId = $communityId; $avatar->username = $user->username; $avatar->firstName = Users::communityName(); $avatar->lastName = Users::communitySuffix(); $avatar->icon = $user->icon; $avatar->save(); $avatar2 = new Streams_Avatar(); $avatar2->copyFrom($avatar, null, false, true); $avatar->toUserId = ''; $avatar->save(); // access stream for managing app roles $stream = new Streams_Stream(); $stream->publisherId = Users::communityId(); $stream->name = 'Streams/contacts'; $stream->type = 'Streams/resource'; $stream->title = "Contacts"; $stream->setAttribute('prefixes', array("Users/", "{$app}/")); $stream->save(); // access stream for managing app roles $stream = new Streams_Stream(); $stream->publisherId = $app; $stream->name = 'Streams/labels'; $stream->type = 'Streams/resource'; $stream->title = "Labels"; $stream->setAttribute('prefixes', array("Users/", "{$app}/")); $stream->save(); // access for managing app contacts $access = new Streams_Access(); $access->publisherId = $communityId; $access->streamName = 'Streams/contacts'; $access->ofUserId = ''; $access->ofContactLabel = "{$app}/admins"; $access->readLevel = Streams::$READ_LEVEL['messages']; $access->writeLevel = Streams::$WRITE_LEVEL['edit']; $access->adminLevel = Streams::$ADMIN_LEVEL['manage']; $access->save(); // access for managing app roles $access = new Streams_Access(); $access->publisherId = $communityId; $access->streamName = 'Streams/labels'; $access->ofUserId = ''; $access->ofContactLabel = "{$app}/admins"; $access->readLevel = Streams::$READ_LEVEL['messages']; $access->writeLevel = Streams::$WRITE_LEVEL['edit']; $access->adminLevel = Streams::$ADMIN_LEVEL['manage']; $access->save(); }
/** * Create award stream for logged-in user * @method createStream * @static * @param {Users_User} $user The user for which stream is created * @return {Streams_Stream} */ static function createStream($user) { $stream = new Streams_Stream(); $stream->publisherId = $user->id; $stream->name = 'Awards/credits'; $stream->type = 'Awards/credits'; $stream->icon = 'plugins/Awards/img/credits.png'; $app = Q_Config::expect('Q', 'app'); $stream->title = 'Credits'; $stream->content = ''; $stream->setAttribute('amount', Q_Config::get('Awards', 'credits', 'amounts', 'Users/insertUser', 20)); $stream->save(); return $stream; }
function Broadcast_main_response_content() { Q_Response::addScript('plugins/Broadcast/js/Broadcast.js'); $user = Users::loggedInUser(true); $stream = new Streams_Stream(); $stream->publisherId = $user->id; $stream->name = 'Broadcast/main'; if (!$stream->retrieve()) { $stream->type = 'Broadcast'; $stream->title = "Main broadcast stream"; $stream->content = "Whatever you post to this stream will be syndicated by everyone who has opted in."; $stream->save(); } Q_Response::redirect('Broadcast/stream publisherId=' . $stream->publisherId . ' name=Broadcast/main'); }
function Streams_0_8_1_Streams_mysql() { $app = Q_Config::expect('Q', 'app'); // template for community stream $stream = new Streams_Stream(); $stream->publisherId = ''; $stream->name = 'Streams/community/'; $stream->type = 'Streams/template'; $stream->title = "Community"; $stream->content = ''; $readLevel = Streams::$READ_LEVEL['content']; $writeLevel = Streams::$WRITE_LEVEL['join']; $adminLevel = Streams::$ADMIN_LEVEL['invite']; $stream->save(); // app community stream, for announcements Streams::create($app, $app, 'Streams/community', array('skipAccess' => true, 'name' => 'Streams/community/main', 'title' => "{$app} Community")); // symlink the labels folder if (!file_exists('Streams')) { Q_Utils::symlink(STREAMS_PLUGIN_FILES_DIR . DS . 'Streams' . DS . 'icons' . DS . 'labels' . DS . 'Streams', USERS_PLUGIN_FILES_DIR . DS . 'Users' . DS . 'icons' . DS . 'Streams'); } }
function Streams_after_Users_User_saveExecute($params) { // If the username or icon was somehow modified, // update all the avatars for this publisher $modifiedFields = $params['modifiedFields']; $user = $params['row']; $updates = array(); if (isset($modifiedFields['username'])) { $updates['username'] = $modifiedFields['username']; } if (isset($modifiedFields['icon'])) { $updates['icon'] = $modifiedFields['icon']; } if ($user->id === Users::communityId()) { $firstName = Users::communityName(); $lastName = Users::communitySuffix(); $firstName = $firstName ? $firstName : ""; $lastName = $lastName ? $lastName : ""; } else { $firstName = Q::ifset(Streams::$cache, 'register', 'first', ''); $lastName = Q::ifset(Streams::$cache, 'register', 'last', ''); } if ($params['inserted']) { // create some standard streams for them $onInsert = Q_Config::get('Streams', 'onInsert', 'Users_User', array()); if (!$onInsert) { return; } $p = new Q_Tree(); $p->load(STREAMS_PLUGIN_CONFIG_DIR . DS . 'streams.json'); $p->load(APP_CONFIG_DIR . DS . 'streams.json'); $values = array('Streams/user/firstName' => $firstName, 'Streams/user/lastName' => $lastName); // Check for user data from facebook if (!empty(Users::$cache['facebookUserData'])) { $userData = Users::$cache['facebookUserData']; foreach ($userData as $name_fb => $value) { foreach ($p->getAll() as $name => $info) { if (isset($info['name_fb']) and $info['name_fb'] === $name_fb) { $onInsert[] = $name; $values[$name] = $value; } } } } foreach ($onInsert as $name) { $stream = Streams::fetchOne($user->id, $user->id, $name); if (!$stream) { // it shouldn't really be in the db yet $stream = new Streams_Stream(); $stream->publisherId = $user->id; $stream->name = $name; } $stream->type = $p->expect($name, "type"); $stream->title = $p->expect($name, "title"); $stream->content = $p->get($name, "content", ''); // usually empty $stream->readLevel = $p->get($name, 'readLevel', Streams_Stream::$DEFAULTS['readLevel']); $stream->writeLevel = $p->get($name, 'writeLevel', Streams_Stream::$DEFAULTS['writeLevel']); $stream->adminLevel = $p->get($name, 'adminLevel', Streams_Stream::$DEFAULTS['adminLevel']); if ($name === "Streams/user/icon") { $sizes = Q_Config::expect('Users', 'icon', 'sizes'); sort($sizes); $stream->setAttribute('sizes', $sizes); $stream->icon = $user->iconUrl(); } if (isset($values[$name])) { $stream->content = $values[$name]; } $stream->save(); // this also inserts avatars $o = array('userId' => $user->id, 'skipAccess' => true); $so = $p->get($name, "subscribe", array()); if ($so === false) { $stream->join($o); } else { $stream->subscribe(array_merge($o, $so)); } } // Save a greeting stream, to be edited $communityId = Users::communityId(); Streams::create($user->id, $user->id, "Streams/greeting", array('name' => "Streams/greeting/{$communityId}")); // Create some standard labels $label = new Users_Label(); $label->userId = $user->id; $label->label = 'Streams/invited'; $label->icon = 'labels/Streams/invited'; $label->title = 'People I invited'; $label->save(true); $label2 = new Users_Label(); $label2->userId = $user->id; $label2->label = 'Streams/invitedMe'; $label2->icon = 'labels/Streams/invitedMe'; $label2->title = 'Who invited me'; $label2->save(true); // By default, users they invite should see their full name $access = new Streams_Access(); $access->publisherId = $user->id; $access->streamName = 'Streams/user/firstName'; $access->ofUserId = ''; $access->ofContactLabel = 'Streams/invited'; $access->grantedByUserId = $user->id; $access->readLevel = Streams::$READ_LEVEL['content']; $access->writeLevel = -1; $access->adminLevel = -1; $access->save(); $access = new Streams_Access(); $access->publisherId = $user->id; $access->streamName = 'Streams/user/lastName'; $access->ofUserId = ''; $access->ofContactLabel = 'Streams/invited'; $access->grantedByUserId = $user->id; $access->readLevel = Streams::$READ_LEVEL['content']; $access->writeLevel = -1; $access->adminLevel = -1; $access->save(); // NOTE: the above saving of access caused Streams::updateAvatar to run, // insert a Streams_Avatar row for the new user, and properly configure it. } else { if ($modifiedFields) { if ($updates) { Streams_Avatar::update()->set($updates)->where(array('publisherId' => $user->id))->execute(); } foreach ($modifiedFields as $field => $value) { $name = Q_Config::get('Streams', 'onUpdate', 'Users_User', $field, null); if (!$name) { continue; } $stream = isset(Streams::$beingSaved[$field]) ? Streams::$beingSaved[$field] : Streams::fetchOne($user->id, $user->id, $name); if (!$stream) { // it should probably already be in the db continue; } $stream->content = $value; if ($name === "Streams/user/icon") { $sizes = Q_Config::expect('Users', 'icon', 'sizes'); sort($sizes); $attributes = $stream->attributes; $stream->setAttribute('sizes', $sizes); $stream->icon = $changes['icon'] = $user->iconUrl(); } Streams::$beingSavedQuery = $stream->changed($user->id); } } } }
/** * 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. * @param {String} [$_REQUEST.subscribe] Optional. Defauls to false. Whether to subscribe rather than just join the interest stream. * @return {void} */ function Streams_interest_post() { $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) { $stream = Streams::create($publisherId, $publisherId, 'Streams/interest', array('name' => $name, 'title' => $title)); $parts = explode(': ', $title, 2); $keywords = implode(' ', $parts); try { $data = Q_Image::pixabay($keywords, array('orientation' => 'horizontal', 'min_width' => '500', 'safesearch' => 'true', 'image_type' => 'photo'), true); } catch (Exception $e) { Q::log("Exception during Streams/interest post: " . $e->getMessage()); $data = null; } if (!empty($data)) { $sizes = Q_Config::expect('Streams', 'icons', 'sizes'); ksort($sizes); $params = array('data' => $data, 'path' => "plugins/Streams/img/icons", 'subpath' => $name, 'save' => $sizes, 'skipAccess' => true); Q_Image::save($params); $stream->icon = $name; } $stream->save(); } $subscribe = !!Q::ifset($_REQUEST, 'subscribe', false); if ($subscribe) { if (!$stream->subscription($user->id)) { $stream->subscribe(); } } else { $stream->join(); } $myInterestsName = 'Streams/user/interests'; $myInterests = Streams::fetchOne($user->id, $user->id, $myInterestsName); if (!$myInterests) { $myInterests = new Streams_Stream(); $myInterests->publisherId = $user->id; $myInterests->name = $myInterestsName; $myInterests->type = 'Streams/category'; $myInterests->title = 'My Interests'; $myInterests->save(); } Streams::relate($user->id, $user->id, 'Streams/user/interests', 'Streams/interest', $publisherId, $name, array('weight' => '+1')); Q_Response::setSlot('publisherId', $publisherId); Q_Response::setSlot('streamName', $name); /** * Occurs when the logged-in user has successfully added an interest via HTTP * @event Streams/interest/post {after} * @param {string} publisherId The publisher of the interest stream * @param {string} title The title of the interest * @param {boolean} subscribe Whether the user subscribed to the interest stream * @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/add", compact('publisherId', 'title', 'subscribe', 'user', 'stream', 'myInterests'), 'after'); }
/** * Retrieve the user's stream needed to post invite messages * If stream does not exists - create it. May return null if save failed. * @method getInvitedStream * @static * @param $asUserId {string} * The user id of inviting user * @param $forUserId {string} * User id for which stream is created * @return {Streams_Stream|null} */ static function getInvitedStream($asUserId, $forUserId) { $invited = Streams::fetch($asUserId, $forUserId, 'Streams/invited'); if (!empty($invited)) { return $invited['Streams/invited']; } $invited = new Streams_Stream(); $invited->publisherId = $forUserId; $invited->name = 'Streams/invited'; $invited->type = 'Streams/invited'; $invited->title = 'Streams/invited'; $invited->content = 'Post message here when user is invited to some stream'; $invited->readLevel = Streams::$READ_LEVEL['none']; $invited->writeLevel = Streams::$WRITE_LEVEL['post']; // anyone can post messages $invited->adminLevel = Streams::$ADMIN_LEVEL['none']; $result = $invited->save(true); //Streams::calculateAccess($asUserId, $forUserId, array('Streams/invited' => $invited), false); return $result ? $invited : null; }
/** * Closes a stream, which prevents anyone from posting messages to it * unless they have WRITE_LEVEL >= "close", as well as attempting to remove * all relations to other streams. A "cron job" can later go and delete * closed streams. The reason you should avoid deleting streams right away * is that other subscribers may still want to receive the last messages * posted to the stream. * @method close * @param {string} $asUserId The id of the user who would be closing the stream * @param {string} $publisherId The id of the user publishing the stream * @param {string} $streamName The name of the stream * @param {array} [$options=array()] Can include "skipAccess" * @static */ static function close($asUserId, $publisherId, $streamName, $options = array()) { $stream = new Streams_Stream(); $stream->publisherId = $publisherId; $stream->name = $streamName; if (!$stream->retrieve()) { throw new Q_Exception_MissingRow(array('table' => 'stream', 'criteria' => "{publisherId: '{$publisherId}', name: '{$streamName}'}")); } // Authorization check if (empty($options['skipAccess'])) { if ($asUserId !== $publisherId) { $stream->calculateAccess($asUserId); if (!$stream->testWriteLevel('close')) { throw new Users_Exception_NotAuthorized(); } } } // Clean up relations from other streams to this category list($relations, $related) = Streams::related($asUserId, $stream->publisherId, $stream->name, true); foreach ($relations as $r) { try { Streams::unrelate($asUserId, $r->fromPublisherId, $r->fromStreamName, $r->type, $stream->publisherId, $stream->name); } catch (Exception $e) { } } // Clean up relations from this stream to categories list($relations, $related) = Streams::related($asUserId, $stream->publisherId, $stream->name, false); foreach ($relations as $r) { try { Streams::unrelate($asUserId, $r->toPublisherId, $r->toStreamName, $r->type, $stream->publisherId, $stream->name); } catch (Exception $e) { } } $result = false; try { $db = $stream->db(); $stream->closedTime = $closedTime = $db->toDateTime($db->getCurrentTimestamp()); if ($stream->save()) { $stream->post($asUserId, array('type' => 'Streams/closed', 'content' => '', 'instructions' => compact('closedTime')), true); $result = true; } } catch (Exception $e) { throw $e; } return $result; }
/** * Fetch a stream on which messages are posted relating to things happening * a given number of $miles around the given location. * If it doesn't exist, create it. * @method stream * @static * @param {double} $latitude The latitude of the coordinates to search around * @param {double} $longitude The longitude of the coordinates to search around * @param {double} $miles The radius, in miles, around this location. * Should be one of the array values in the Places/nearby/miles config. * @param {string} $publisherId The id of the publisher to publish this stream * Defaults to the app name in Q/app config. * @param {string} $streamName The name of the stream to create. * Defaults to Places_Nearby::streamName($latitude, $longitude, $miles). * @return {Streams_Stream} Returns the stream object that was created or fetched. */ static function stream($latitude, $longitude, $miles, $publisherId = null, $streamName = null) { list($latitude, $longGrid) = Places::quantize($latitude, $longitude, $miles); $zipcodes = Places_Zipcode::nearby($latitude, $longitude, $miles, 1); if (!isset($publisherId)) { $publisherId = Q_Config::expect('Q', 'app'); } if (!isset($streamName)) { $streamName = self::streamName($latitude, $longitude, $miles); } if ($stream = Streams::fetchOne(null, $publisherId, $streamName)) { return $stream; } $zipcode = $zipcodes ? reset($zipcodes) : null; $stream = new Streams_Stream(); $stream->publisherId = $publisherId; $stream->name = $streamName; $stream->type = "Places/nearby"; $stream->title = $zipcode ? "Nearby ({$latitude}, {$longitude}): {$zipcode->placeName}, zipcode {$zipcode->zipcode}" : "Nearby ({$latitude}, {$longitude})"; $stream->setAttribute('latitude', $latitude); $stream->setAttribute('longitude', $longitude); if ($zipcode) { $stream->setAttribute('zipcode', $zipcode->zipcode); $stream->setAttribute('placeName', $zipcode->placeName); $stream->setAttribute('state', $zipcode->state); } $stream->save(); return $stream; }
/** * Get a Places/location stream published by a publisher for a given placeId. * This is used to cache information from the Google Places API. * @method stream * @static * @param {string} $publisherId * @param {string} $placeId The id of the place in Google Places * @param {boolean} $throwIfBadValue * Whether to throw Q_Exception if the result contains a bad value * @return {Streams_Stream|null} * @throws {Q_Exception} if a bad value is encountered and $throwIfBadValue is true */ static function stream($publisherId, $placeId, $throwIfBadValue = false) { if (empty($placeId)) { if ($throwIfBadValue) { throw new Q_Exception_RequiredField(array('field' => 'id')); } return null; } // sanitize the ID $characters = '/[^A-Za-z0-9]+/'; $result = preg_replace($characters, '_', $placeId); // see if it's already in the system $location = new Streams_Stream(); $location->publisherId = $publisherId; $location->name = "Places/location/{$placeId}"; if ($location->retrieve()) { $ut = $location->updatedTime; if (isset($ut)) { $db = $location->db(); $ut = $db->fromDateTime($ut); $ct = $db->getCurrentTimestamp(); $cd = Q_Config::get('Places', 'cache', 'duration', 60 * 60 * 24 * 30); if ($ct - $ut < $cd) { // there is a cached location stream that is still viable return $location; } } } $key = Q_Config::expect('Places', 'google', 'keys', 'server'); $query = http_build_query(array('key' => $key, 'placeid' => $placeId)); $url = "https://maps.googleapis.com/maps/api/place/details/json?{$query}"; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $json = curl_exec($ch); curl_close($ch); $response = json_decode($json, true); if (empty($response['result'])) { throw new Q_Exception("Places_Location::stream: Couldn't obtain place information for {$placeId}"); } if (!empty($response['error_message'])) { throw new Q_Exception("Places_Location::stream: " . $response['error_message']); } $result = $response['result']; $attributes = array('title' => $result['name'], 'latitude' => $result['geometry']['location']['lat'], 'longitude' => $result['geometry']['location']['lng'], 'viewport' => $result['geometry']['viewport'], 'phoneNumber' => Q::ifset($result, 'international_phone_number', null), 'phoneFormatted' => Q::ifset($result, 'formatted_phone_number', null), 'rating' => Q::ifset($result, 'rating', null), 'address' => Q::ifset($result, 'formatted_address', null)); $location->title = $result['name']; $location->setAttribute($attributes); $location->type = 'Places/location'; $location->save(); return $location; }