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 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'); }
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 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(); }
/** * Post one or more fields here to change the corresponding basic streams for the logged-in user. Fields can include: * "firstName": specify the first name directly * "lastName": specify the last name directly * "fullName": the user's full name, which if provided will be split into first and last name and override them * "gender": the user's gender * "birthday_year": the year the user was born * "birthday_month": the month the user was born * "birthday_day": the day the user was born */ function Streams_basic_post() { Q_Valid::nonce(true); $user = Users::loggedInUser(true); $request = $_REQUEST; $fields = array(); if (!empty($request['birthday_year']) && !empty($request['birthday_month']) && !empty($request['birthday_day'])) { $request['birthday'] = sprintf("%04d-%02d-%02d", $_REQUEST['birthday_year'], $_REQUEST['birthday_month'], $_REQUEST['birthday_day']); } // $request['icon'] = $user->icon; if (isset($request['fullName'])) { $name = Streams::splitFullName($request['fullName']); $request['firstName'] = $name['first']; $request['lastName'] = $name['last']; } foreach (array('firstName', 'lastName', 'birthday', 'gender') as $field) { if (isset($request[$field])) { $fields[] = $field; } } $p = new Q_Tree(); $p->load(STREAMS_PLUGIN_CONFIG_DIR . DS . 'streams.json'); $p->load(APP_CONFIG_DIR . DS . 'streams.json'); $names = array(); foreach ($fields as $field) { $names[] = "Streams/user/{$field}"; } $streams = Streams::fetch($user, $user->id, $names); foreach ($fields as $field) { $name = "Streams/user/{$field}"; $type = $p->get($name, "type", null); if (!$type) { throw new Q_Exception("Missing {$name} type", $field); } $title = $p->get($name, "title", null); if (!$title) { throw new Q_Exception("Missing {$name} title", $field); } $stream = $streams[$name]; if (isset($stream) and $stream->content === (string) $request[$field]) { continue; } if (!isset($stream)) { $stream = new Streams_Stream(); $stream->publisherId = $user->id; $stream->name = $name; } $messageType = $stream->wasRetrieved() ? 'Streams/changed' : 'Streams/created'; $stream->content = (string) $request[$field]; $stream->type = $type; $stream->title = $title; $stream->changed($user->id, $messageType); } }
function Streams_before_Streams_Participant_save($params) { $row = $params['row']; if (substr($row->streamName, 0, 18) === 'Streams/community/' and !$row->wasRetrieved()) { $communityId = $row->publisherId; $stream = new Streams_Stream(); $stream->publisherId = $row->userId; $stream->name = "Streams/greeting/{$communityId}"; if ($stream->retrieve()) { $row->setExtra('Streams/greeting', $stream->content); } } }
/** * 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; }
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_invite_response_content() { $user = Users::loggedInUser(true); $publisherId = Streams::requestedPublisherId(); if (empty($publisherId)) { $publisherId = $user->id; } $streamName = Streams::requestedName(true); $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}'}")); } return Q::tool('Streams/invite', compact('stream')); }
function Websites_0_9_1_Streams_mysql() { $userId = Users::communityId(); $ofUserId = ''; $ofContactLabel = 'Websites/admins'; $grantedByUserId = null; $streams = array("Websites/presentation/" => array('type' => "Streams/template", "title" => "Untitled Presentation", "icon" => "{{baseUrl}}/plugins/Websites/img/icons/Websites/presentation", "content" => "", "deletable" => true), "Websites/slide/" => array('type' => "Streams/template", "title" => "Untitled Slide", "icon" => "{{baseUrl}}/plugins/Websites/img/icons/Websites/presentation", "content" => "", "deletable" => true)); $readLevel = Streams::$READ_LEVEL['messages']; $adminLevel = Streams::$ADMIN_LEVEL['own']; $rows = array(); foreach ($streams as $streamName => $stream) { $publisherId = substr($streamName, -1) == '/' ? '' : $userId; $level = !empty($stream['deletable']) ? 'close' : 'edit'; $writeLevel = Streams::$WRITE_LEVEL[!empty($stream['deletable']) ? 'close' : 'edit']; $rows[] = compact('publisherId', 'streamName', 'ofUserId', 'ofContactLabel', 'grantedByUserId', 'readLevel', 'writeLevel', 'adminLevel'); } Streams_Access::insertManyAndExecute($rows); $attributes = null; $closedTime = null; $readLevel = Streams::$READ_LEVEL['messages']; $writeLevel = Streams::$WRITE_LEVEL['join']; $adminLevel = Streams::$ADMIN_LEVEL['invite']; $inheritAccess = null; $rows = array(); foreach ($streams as $name => $s) { extract($s); $publisherId = substr($name, -1) == '/' ? '' : $userId; $rows[] = compact('publisherId', 'name', 'type', 'title', 'icon', 'content', 'attributes', 'readLevel', 'writeLevel', 'adminLevel', 'inheritAccess'); } Streams_Stream::insertManyAndExecute($rows); Streams_RelatedTo::insert(array('toPublisherId' => '', 'toStreamName' => 'Websites/presentation/', 'type' => 'slides', 'fromPublisherId' => '', 'fromStreamName' => 'Websites/slide/'))->execute(); }
function Streams_0_8_6_Streams_mysql() { $app = Q_Config::expect('Q', 'app'); // access for managing communities $access = new Streams_Access(); $access->publisherId = $app; $access->streamName = 'Streams/community*'; $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 categories $access = new Streams_Access(); $access->publisherId = $app; $access->streamName = 'Streams/category/'; $access->ofUserId = ''; $access->ofContactLabel = "{$app}/admins"; $access->readLevel = Streams::$READ_LEVEL['messages']; $access->writeLevel = Streams::$WRITE_LEVEL['close']; $access->adminLevel = Streams::$ADMIN_LEVEL['manage']; $access->save(); // template to help users relate things to Streams/category streams Streams_Stream::insert(array('publisherId' => '', 'name' => 'Streams/category/', 'type' => 'Streams/template', 'title' => 'Untitled Category', 'icon' => 'Streams/category', 'content' => '', 'attributes' => null, 'readLevel' => Streams::$READ_LEVEL['messages'], 'writeLevel' => Streams::$WRITE_LEVEL['relate'], 'adminLevel' => Streams::$ADMIN_LEVEL['invite']))->execute(); // template to help users create subcategories for things Streams_RelatedTo::insert(array('toPublisherId' => '', 'toStreamName' => 'Streams/category/', 'type' => 'subcategories', 'fromPublisherId' => '', 'fromStreamName' => 'Streams/category/'))->execute(); }
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); }
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_access_response_content($options) { $ajax = true; $user = Users::loggedInUser(true); $streamName = Streams::requestedName(true); $publisherId = Streams::requestedPublisherId(); if (empty($publisherId)) { $publisherId = $user->id; } $stream = new Streams_Stream(); $stream->publisherId = $publisherId; $stream->name = $streamName; if (!$stream->retrieve()) { throw new Q_Exception_MissingRow(array('table' => 'stream', 'criteria' => 'with that name'), 'name'); } $controls = !empty($options['controls']); Q_Response::setSlot('title', "Access to: " . $stream->title); return Q::tool('Streams/access', compact('publisherId', 'streamName', 'ajax', 'controls'), $controls ? array('tag' => null) : array()); }
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'); } }
/** * Check if user "owns" a stream template for a publisher * @method isOwner * @static * @param {string} $publisherId * @param {string} $type * @param {string|Users_User} [$user=null] * @return {boolean} */ static function isOwner($publisherId, $type, $user = null) { if (!isset($user)) { $user = Users::loggedInUser(); } else { if (is_string($user)) { $user = Users_User::fetch($user); } } if (!isset($user)) { return false; } // check if user is owner of stream template $stream = new Streams_Stream(); $stream->publisherId = $publisherId; $stream->name = $type . '/'; if (!$stream->retrieve()) { return false; } $stream->calculateAccess($user->id); return $stream->testAdminLevel('own'); }
function Streams_0_9_3_Streams() { $results = Streams_Participant::select("publisherId, streamName, COUNT(IF(state='invited', 1, NULL)) invitedCount, COUNT(IF(state='participating', 1, NULL)) participatingCount, COUNT(IF(state='left', 1, NULL)) leftCount")->groupBy('publisherId, streamName')->fetchAll(PDO::FETCH_ASSOC); echo "Updating streams..."; $c = count($results); $i = 0; foreach ($results as $r) { Streams_Stream::update()->set(array('invitedCount' => intval($r['invitedCount']), 'participatingCount' => intval($r['participatingCount']), 'leftCount' => intval($r['leftCount'])))->where(array('publisherId' => $r['publisherId'], 'name' => $r['streamName']))->execute(); ++$i; echo "[100D"; echo "Updated {$i} of {$c} streams"; } echo "\n"; }
function Streams_after_Q_file_save($params) { $path = $subpath = $name = $writePath = $data = $tailUrl = $size = $audio = null; extract($params, EXTR_OVERWRITE); if (!empty(Streams::$cache['canWriteToStream'])) { // some stream's associated file was being changed $stream = Streams::$cache['canWriteToStream']; } if (empty($stream)) { return; } $url = Q_Valid::url($tailUrl) ? $tailUrl : '{{baseUrl}}/' . $tailUrl; $stream->setAttribute('Q.file.url', $url); $stream->setAttribute('Q.file.size', $size); if ($audio) { include_once Q_CLASSES_DIR . DS . 'Audio' . DS . 'getid3' . DS . 'getid3.php'; $getID3 = new getID3(); $meta = $getID3->analyze($writePath . $name); $bitrate = $meta['audio']['bitrate']; $bits = $size * 8; $duration = $bits / $bitrate; $stream->setAttribute('Q.audio.bitrate', $bitrate); $stream->setAttribute('Q.audio.duration', $duration); } if (Streams_Stream::getConfigField($stream->type, 'updateTitle', false)) { // set the title every time a new file is uploaded $stream->title = $name; } if (Streams_Stream::getConfigField($stream->type, 'updateIcon', false)) { // set the icon every time a new file is uploaded $parts = explode('.', $name); $urlPrefix = Q_Request::baseUrl() . '/plugins/Streams/img/icons/files'; $dirname = STREAMS_PLUGIN_FILES_DIR . DS . 'Streams' . DS . 'icons' . DS . 'files'; $extension = end($parts); $stream->icon = file_exists($dirname . DS . $extension) ? "{$urlPrefix}/{$extension}" : "{$urlPrefix}/_blank"; } if (empty(Streams::$beingSavedQuery)) { $stream->changed(); } else { $stream->save(); } }
function Websites_0_8_Streams_mysql() { $userId = Q_Config::get("Websites", "user", "id", null); if (!$userId) { throw new Q_Exception('Websites: Please fill in the config field "Websites"/"user"/"id"'); } // $now = Streams::db()->toDateTime(Streams::db()->getCurrentTimestamp()); $ofUserId = ''; $ofContactLabel = 'Websites/admins'; $grantedByUserId = null; $streams = array("Streams/images/" => array('type' => "Streams/template", "title" => "Images", "icon" => "default", "content" => "", "deletable" => true), "Streams/image/" => array('type' => "Streams/template", "title" => "Untitled Image", "icon" => "Streams/image", "content" => "", "deletable" => true), "Streams/file/" => array('type' => "Streams/template", "title" => "Untitled File", "icon" => "files/_blank", "content" => "", "deletable" => true), "Websites/article/" => array('type' => "Streams/template", "title" => "Untitled Article", "icon" => "default", "content" => "", "deletable" => true), "Websites/seo/" => array('type' => "Streams/template", "title" => "Website SEO", "icon" => Q_Html::themedUrl("plugins/Websites/img/seo"), "content" => "", "deletable" => true), "Websites/header" => array('type' => "Streams/image/icon", "title" => "Header image", "icon" => Q_Html::themedUrl("plugins/Websites/img/header"), "content" => ""), "Websites/slogan" => array('type' => "Streams/text/small", "title" => "Website slogan", "icon" => "default", "content" => "The coolest website"), "Websites/title" => array('type' => "Streams/text/small", "title" => "Website title", "icon" => "default", "content" => "Website Title"), "Websites/menu" => array('type' => "Streams/category", "title" => "Website Menu", "icon" => "default", "content" => ""), "Websites/articles" => array('type' => "Streams/category", "title" => "Articles", "icon" => "default", "content" => "Articles"), "Websites/images" => array('type' => "Streams/category", "title" => "Images", "icon" => "default", "content" => "Articles")); $readLevel = Streams::$READ_LEVEL['messages']; $writeLevel = Streams::$WRITE_LEVEL['edit']; $adminLevel = Streams::$ADMIN_LEVEL['own']; $rows = array(); foreach ($streams as $streamName => $stream) { $publisherId = substr($streamName, -1) == '/' ? '' : $userId; $writeLevel = !empty($stream['deletable']) ? 40 : 30; $rows[] = compact('publisherId', 'streamName', 'ofUserId', 'ofContactLabel', 'grantedByUserId', 'readLevel', 'writeLevel', 'adminLevel'); } Streams_Access::insertManyAndExecute($rows); $attributes = null; $closedTime = null; $readLevel = Streams::$READ_LEVEL['messages']; $writeLevel = Streams::$WRITE_LEVEL['join']; $adminLevel = Streams::$ADMIN_LEVEL['invite']; $inheritAccess = null; $rows = array(); foreach ($streams as $name => $s) { extract($s); if (substr($name, 0, 9) != 'Websites/') { continue; // this tempate was already added by Streams install script } $publisherId = substr($name, -1) == '/' ? '' : $userId; $rows[] = compact('publisherId', 'name', 'type', 'title', 'icon', 'content', 'attributes', 'readLevel', 'writeLevel', 'adminLevel', 'inheritAccess'); } Streams_Stream::insertManyAndExecute($rows); Streams_RelatedTo::insert(array('toPublisherId' => '', 'toStreamName' => 'Streams/images/', 'type' => 'images', 'fromPublisherId' => '', 'fromStreamName' => 'Streams/image/'))->execute(); Streams_RelatedTo::insert(array('toPublisherId' => '', 'toStreamName' => 'Streams/category/', 'type' => 'articles', 'fromPublisherId' => '', 'fromStreamName' => 'Websites/article/'))->execute(); Streams_RelatedTo::insert(array('toPublisherId' => '', 'toStreamName' => 'Streams/category/', 'type' => 'announcements', 'fromPublisherId' => '', 'fromStreamName' => 'Websites/article/'))->execute(); }
function Streams_0_8_7_Streams_mysql() { $app = Q_Config::expect('Q', 'app'); $user = Users_User::fetch($app, true); $simulated = array('row' => $user, 'inserted' => true, 'modifiedFields' => $user->fields); Q::event('Db/Row/Users_User/saveExecute', $simulated, 'after'); $stream = array('publisherId' => '', 'name' => "Streams/images/", 'type' => 'Streams/template', 'title' => 'Image Gallery', 'icon' => 'default', 'content' => '', 'attributes' => null, 'readLevel' => Streams::$READ_LEVEL['messages'], 'writeLevel' => Streams::$WRITE_LEVEL['close'], 'adminLevel' => Streams::$ADMIN_LEVEL['invite']); $access = array('publisherId' => '', 'streamName' => "Streams/images/", 'ofUserId' => '', 'grantedByUserId' => null, 'ofContactLabel' => "{$app}/admins", 'readLevel' => Streams::$READ_LEVEL['messages'], 'writeLevel' => Streams::$WRITE_LEVEL['close'], 'adminLevel' => Streams::$ADMIN_LEVEL['invite']); Streams_Stream::insert($stream)->execute(); Streams_Access::insert($access)->execute(); $stream['name'] = $access['streamName'] = 'Streams/image/'; $stream['icon'] = 'Streams/image'; $stream['title'] = 'Untitled Image'; Streams_Stream::insert($stream)->execute(); Streams_Access::insert($access)->execute(); $stream['name'] = $access['streamName'] = 'Streams/file/'; $stream['icon'] = 'files/_blank'; $stream['title'] = 'Untitled File'; Streams_Stream::insert($stream)->execute(); Streams_Access::insert($access)->execute(); }
/** * Provide player content to view the stream content * Uses Streams/$type/get.php view and Streams::get to retrieve stream data * **/ function Streams_get_response_player() { $user = Users::loggedInUser(); $userId = $user ? $user->id : 0; $publisherId = Streams::requestedPublisherId(true); $name = Streams::requestedName(true); if (substr($name, -1) === '/') { throw new Q_Exception("Player cannot show multiple streams", compact('publisherId', 'name')); } /* * Get shall return only streams which user is authorized to see. */ if (!($stream = Streams::get($userId, $publisherId, $name, null, true))) { throw new Q_Exception_MissingRow(array('table' => 'stream', 'criteria' => compact('publisherId', 'name'))); } // join the stream if ($userId !== 0 && $stream->testWriteLevel('join')) { Streams_Stream::join($userId, $stream->publisherId, $stream->name); } // Let's be nice to poor Windows users $type = join(DS, explode('/', $stream->type)); return Q::view("Streams/{$type}/get.php", compact('stream', 'userId')); }
/** * Does necessary preparations for saving a stream in the database. * @method beforeSave * @param {array} $modifiedFields * The array of fields * @return {array} * @throws {Exception} * If mandatory field is not set */ function beforeSave($modifiedFields) { if (empty($this->attributes)) { $this->attributes = '{}'; } if (!$this->retrieved) { // Generate a unique name for the stream if (!isset($modifiedFields['name'])) { $this->name = $modifiedFields['name'] = Streams::db()->uniqueId(Streams_Stream::table(), 'name', array('publisherId' => $this->publisherId), array('prefix' => $this->type . '/Q')); } // we don't want user to update private fields but will set initial values to them $privateFieldNames = self::getConfigField($this->type, 'private', array()); // magic fields are handled by parent method $magicFieldNames = array('insertedTime', 'updatedTime'); $privateFieldNames = array_diff($privateFieldNames, $magicFieldNames); $streamTemplate = $this->getStreamTemplate('Streams_Stream'); $fieldNames = Streams_Stream::fieldNames(); if ($streamTemplate) { // if template exists copy all non-PK and non-magic fields from template foreach (array_diff($fieldNames, $this->getPrimaryKey(), $magicFieldNames) as $field) { if (in_array($field, $privateFieldNames) || !array_key_exists($field, $modifiedFields)) { $this->{$field} = $modifiedFields[$field] = $streamTemplate->{$field}; } } } else { // otherwise (no template) set all private fields to defaults foreach ($privateFieldNames as $field) { $defaults = self::getConfigField($this->type, 'defaults', Streams_Stream::$DEFAULTS); $this->{$field} = $modifiedFields[$field] = Q::ifset($defaults, $field, null); } } // Assign default values to fields that haven't been set yet foreach (array_diff($fieldNames, $magicFieldNames) as $field) { if (!array_key_exists($field, $this->fields) and !array_key_exists($field, $modifiedFields)) { $defaults = self::getConfigField($this->type, 'defaults', Streams_Stream::$DEFAULTS); $this->{$field} = $modifiedFields[$field] = Q::ifset($defaults, $field, null); } } // Get all access templates and save corresponding access $type = true; $accessTemplates = $this->getStreamTemplate('Streams_Access', $type); for ($i = 1; $i <= 3; ++$i) { foreach ($accessTemplates[$i] as $template) { $access = new Streams_Access(); $access->copyFrom($template->toArray()); $access->publisherId = $this->publisherId; $access->streamName = $this->name; if (!$access->save(true)) { return false; // JUNK: this leaves junk in the database, but preserves consistency } } } } /** * @event Streams/Stream/save/$streamType {before} * @param {Streams_Stream} stream * @return {false} To cancel further processing */ $params = array('stream' => $this, 'modifiedFields' => $modifiedFields); if (false === Q::event("Streams/Stream/save/{$this->type}", $params, 'before')) { return false; } foreach ($this->fields as $name => $value) { if (!empty($this->fieldsModified[$name])) { $modifiedFields[$name] = $value; } } $this->beforeSaveExtended($modifiedFields); $result = parent::beforeSave($modifiedFields); // Assume that the stream's name is not being changed $fields = array('Streams/user/firstName' => false, 'Streams/user/lastName' => false, 'Streams/user/username' => 'username', 'Streams/user/icon' => 'icon'); if (!isset($fields[$this->name])) { return $result; } $field = $this->name === 'Streams/user/icon' ? 'icon' : 'content'; $wasModified = !empty($this->fieldsModified[$field]) or !empty($this->fieldsModified['readLevel']); if (!$wasModified) { return $result; } if ($publicField = $fields[$this->name] and !Q::eventStack('Db/Row/Users_User/saveExecute')) { Streams::$beingSaved[$publicField] = $this; try { $user = Users_User::fetch($this->publisherId, true); $user->{$publicField} = $modifiedFields[$field]; $user->save(); } catch (Exception $e) { Streams::$beingSaved[$publicField] = array(); throw $e; } Streams::$beingSaved[$publicField] = array(); return Streams::$beingSavedQuery; } if ($this->retrieved and !$publicField) { // Update all avatars corresponding to access rows for this stream $taintedAccess = Streams_Access::select('*')->where(array('publisherId' => $this->publisherId, 'streamName' => $this->name))->fetchDbRows(); Streams::updateAvatars($this->publisherId, $taintedAccess, $this, true); } return $result; }
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); } } } }
/** * Send credits, as the logged-in user, to another user * @method send * @static * @param {integer} $amount The amount of credits to send. * @param {string} $toUserId The id of the user to whom you will send the credits * @param {array} $more An array supplying more info, including * "reason" => Identifies the reason for sending, if any */ static function send($amount, $toUserId, $more = array()) { if (!is_int($amount) or $amount <= 0) { throw new Q_Exception_WrongType(array('field' => 'amount', 'type' => 'integer')); } $user = Users::loggedInUser(true); $from_stream = new Streams_Stream(); $from_stream->publisherId = $user->id; $from_stream->name = 'Awards/credits'; if (!$from_stream->retrieve()) { $from_stream = self::createStream($user); } $existing_amount = $from_stream->getAttribute('amount'); if ($existing_amount < $amount) { throw new Awards_Exception_NotEnoughCredits(array('missing' => $amount - $existing_amount)); } $to_user = Users_User::fetch($toUserId, true); $to_stream = new Streams_Stream(); $to_stream->publisherId = $toUserId; $to_stream->name = 'Awards/credits'; if (!$to_stream->retrieve()) { $to_stream = self::createStream($to_user); } $to_stream->setAttribute('amount', $to_stream->getAttribute('amount') - $amount); $to_stream->save(); // NOTE: we are not doing transactions here mainly because of sharding. // If if we reached this point without exceptions, that means everything worked. // But if the following statement fails, then someone will get free credits. $from_stream->setAttribute('amount', $from_stream->getAttribute('amount') - $amount); $from_stream->save(); $instructions_json = Q::json_encode(array_merge(array('app' => Q_Config::expect('Q', 'app')), $more)); Streams_Message::post($user->id, $userId, array('type' => 'Awards/credits/sent', 'content' => $amount, 'instructions' => $instructions_json)); Streams_Message::post($user->id, $toUserId, array('type' => 'Awards/credits/received', 'content' => $amount, 'instructions' => $instructions_json)); }
/** * Post (potentially) multiple messages to multiple streams. * With one call to this function you can post at most one message per stream. * @static * @param {string} $asUserId * The user to post the message as * @param {string} $messages * Array indexed as follows: * array($publisherId => array($streamName => $message)) * where $message are either Streams_Message objects, * or arrays containing all the fields of messages that will need to be posted. * @param {booleam} $skipAccess=false * If true, skips the access checks and just posts the message. * @return {array} * Returns an array(array(Streams_Message), array(Streams_Stream)) */ static function postMessages($asUserId, $messages, $skipAccess = false) { if (!isset($asUserId)) { $asUserId = Users::loggedInUser(); if (!$asUserId) { $asUserId = ""; } } if ($asUserId instanceof Users_User) { $asUserId = $asUserId->id; } // Build arrays we will need foreach ($messages as $publisherId => $arr) { if (!is_array($arr)) { throw new Q_Exception_WrongType(array('field' => "messages", 'type' => 'array of publisherId => streamName => message')); } foreach ($arr as $streamName => &$message) { if (!is_array($message)) { if (!$message instanceof Streams_Message) { throw new Q_Exception_WrongType(array('field' => "message under {$publisherId} => {$streamName}", 'type' => 'array or Streams_Message')); } $message = $message->fields; } } } // Start posting messages, publisher by publisher $eventParams = array(); $posted = array(); $streams = array(); $messages2 = array(); $totals2 = array(); $clientId = Q_Request::special('clientId', ''); $sendToNode = true; foreach ($messages as $publisherId => $arr) { $streamNames = array_keys($messages[$publisherId]); $streams[$publisherId] = $fetched = Streams::fetch($asUserId, $publisherId, $streamNames, '*', array('refetch' => true, 'begin' => true)); foreach ($arr as $streamName => $message) { $p =& $posted[$publisherId][$streamName]; $p = false; $type = isset($message['type']) ? $message['type'] : 'text/small'; $content = isset($message['content']) ? $message['content'] : ''; $instructions = isset($message['instructions']) ? $message['instructions'] : ''; $weight = isset($message['weight']) ? $message['weight'] : 1; if (!isset($message['byClientId'])) { $message['byClientId'] = $clientId ? substr($clientId, 0, 255) : ''; } if (is_array($instructions)) { $instructions = Q::json_encode($instructions); } $byClientId = $message['byClientId']; // Get the Streams_Stream object if (!isset($fetched[$streamName])) { $p = new Q_Exception_MissingRow(array('table' => 'stream', 'criteria' => "publisherId {$publisherId} and name {$streamName}")); continue; } $stream = $fetched[$streamName]; // Make a Streams_Message object $message = new Streams_Message(); $message->publisherId = $publisherId; $message->streamName = $streamName; $message->insertedTime = new Db_Expression("CURRENT_TIMESTAMP"); $message->sentTime = new Db_Expression("CURRENT_TIMESTAMP"); $message->byUserId = $asUserId; $message->byClientId = $byClientId ? substr($byClientId, 0, 31) : ''; $message->type = $type; $message->content = $content; $message->instructions = $instructions; $message->weight = $weight; $message->ordinal = $stream->messageCount + 1; // thanks to transaction // Set up some parameters for the event hooks $eventParams[$publisherId][$streamName] = array('publisherId' => $publisherId, 'message' => $message, 'skipAccess' => $skipAccess, 'sendToNode' => &$sendToNode, 'stream' => $stream); $params = $eventParams[$publisherId][$streamName]; /** * @event Streams/post/$streamType {before} * @param {string} publisherId * @param {Streams_Stream} stream * @param {string} message * @return {false} To cancel further processing */ if (Q::event("Streams/post/{$stream->type}", $params, 'before') === false) { $results[$stream->name] = false; continue; } /** * @event Streams/message/$messageType {before} * @param {string} publisherId * @param {Streams_Stream} stream * @param {string} message * @return {false} To cancel further processing */ if (Q::event("Streams/message/{$type}", $params, 'before') === false) { $results[$stream->name] = false; continue; } if (!$skipAccess && !$stream->testWriteLevel('post')) { $p = new Users_Exception_NotAuthorized(); /** * @event Streams/notAuthorized {before} * @param {string} publisherId * @param {Streams_Stream} stream * @param {string} message */ Q::event("Streams/notAuthorized", $params, 'after'); continue; } // if we are still here, mark the message as "in the database" $message->wasRetrieved(true); $posted[$publisherId][$streamName] = $message; // build the arrays of rows to insert $messages2[] = $mf = $message->fields; $totals2[] = array('publisherId' => $mf['publisherId'], 'streamName' => $mf['streamName'], 'messageType' => $mf['type'], 'messageCount' => 1); } } if ($totals2) { Streams_Total::insertManyAndExecute($totals2, array('onDuplicateKeyUpdate' => array('messageCount' => new Db_Expression('messageCount + 1')))); } if ($messages2) { Streams_Message::insertManyAndExecute($messages2); } // time to update the stream rows and commit the transaction // on all the shards where the streams were fetched. Streams_Stream::update()->set(array('messageCount' => new Db_Expression("messageCount+1")))->where(array('publisherId' => $publisherId, 'name' => $streamNames))->commit()->execute(); // handle all the events for successfully posting foreach ($posted as $publisherId => $arr) { foreach ($arr as $streamName => $m) { $message = $posted[$publisherId][$streamName]; $params =& $eventParams[$publisherId][$streamName]; /** * @event Streams/message/$messageType {after} * @param {string} publisherId * @param {Streams_Stream} stream * @param {string} message */ Q::event("Streams/message/{$message->type}", $params, 'after', false); /** * @event Streams/post/$streamType {after} * @param {string} publisherId * @param {Streams_Stream} stream * @param {string} message */ Q::event("Streams/post/{$stream->type}", $params, 'after', false); } } /** * @event Streams/postMessages {after} * @param {string} publisherId * @param {Streams_Stream} stream * @param {string} posted */ Q::event("Streams/postMessages", array('streams' => $streams, 'messages' => $messages, 'skipAccess' => $skipAccess, 'posted' => $posted), 'after', false); if ($sendToNode) { Q_Utils::sendToNode(array("Q/method" => "Streams/Message/postMessages", "posted" => Q::json_encode($messages2), "streams" => Q::json_encode($streams))); } return array($posted, $streams); }
/** * Used to update an existing stream * * @param string $params Must include "publisherId" as well as "name" or "streamName". * Can also include 'type', 'title', 'icon', 'content', 'attributes', 'readLevel', * 'writeLevel', 'adminLevel', as well as any fields named in the * 'Streams'/'types'/$type/'fields' config field for this $type of stream. * @param {string} [$params.publisherId] The id of the user publishing the stream * @param {string} [$params.name] The name of the stream * @param {string} [$params.streamName] Alternatively, the name of the stream * @param {array} [$params.attributes] Array of attributeName => value to set in stream. * @param {array} [$params.icon] Optional array of icon data (see Q_Image::save params) * @return {} */ function Streams_stream_put($params) { // only logged in user can edit stream $user = Users::loggedInUser(true); $publisherId = Streams::requestedPublisherId(); if (empty($publisherId)) { $publisherId = $_REQUEST['publisherId'] = $user->id; } $name = Streams::requestedName(true); $req = array_merge($_REQUEST, $params); if (array_key_exists('closedTime', $req)) { $closedTime = $req['closedTime']; if (in_array($closedTime, array(false, 'false', 'null'))) { $req['closedTime'] = null; } } // do not set stream name $stream = Streams::fetchOne($user->id, $publisherId, $name); if (!$stream) { throw new Q_Exception_MissingRow(array('table' => 'stream', 'criteria' => "{publisherId: '{$publisherId}', name: '{$name}'}")); } // valid stream types should be defined in config by 'Streams/type' array $range = Q_Config::expect('Streams', 'types'); if (!array_key_exists($stream->type, $range)) { throw new Q_Exception("This app doesn't support streams of type " . $stream->type); } // check if editing directly from client is allowed $edit = Streams_Stream::getConfigField($stream->type, 'edit', false); if (!$edit) { throw new Q_Exception("This app doesn't let clients directly edit streams of type '{$stream->type}'"); } $suggest = false; if ($stream->publisherId != $user->id) { $stream->calculateAccess($user->id); if (!$stream->testWriteLevel('edit')) { if ($stream->testWriteLevel('suggest')) { $suggest = true; } else { throw new Users_Exception_NotAuthorized(); } } } $restricted = array('readLevel', 'writeLevel', 'adminLevel', 'permissions', 'inheritAccess', 'closedTime'); $owned = $stream->testAdminLevel('own'); // owners can reopen streams foreach ($restricted as $r) { if (isset($req[$r]) and !$owned) { throw new Users_Exception_NotAuthorized(); } } // handle setting of attributes if (isset($req['attributes']) and is_array($req['attributes'])) { foreach ($req['attributes'] as $k => $v) { $stream->setAttribute($k, $v); } unset($req['attributes']); } // Get all the extended field names for this stream type $fieldNames = Streams::getExtendFieldNames($stream->type); // Prevent editing restricted fields if (is_array($edit)) { $restrictedFields = array_diff($fieldNames, $edit); foreach ($restrictedFields as $fieldName) { if (in_array($fieldName, array('publisherId', 'name', 'streamName'))) { continue; } if (isset($req[$fieldName])) { throw new Users_Exception_NotAuthorized(); } } } // Process any icon that was posted $icon = Q::ifset($fieldNames, 'icon', null); if (is_array($icon)) { unset($fieldNames['icon']); Q_Response::setSlot('icon', Q::event("Q/image/post", $icon)); } // Process any file that was posted $file = Q::ifset($fieldNames, 'file', null); if (is_array($file)) { unset($fieldNames['file']); $data = Q::event("Q/file/post", $file); Q_Response::setSlot('file', $data); } if (!empty($fieldNames)) { foreach ($fieldNames as $f) { if (array_key_exists($f, $req)) { $stream->{$f} = $req[$f]; } } $stream->changed($user->id, $suggest ? 'Streams/suggest' : 'Streams/changed'); } if (!empty($req['join'])) { $stream->join(); } Streams::$cache['stream'] = $stream; }
/** * 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; }
/** * 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; }
/** * Look up stream by types and title filter * @method lookup * @static * @param {string} $publisherId * The id of the publisher whose streams to look through * @param {string|array} $types * The possible stream type, or an array of types * @param {string} $title * A string to compare titles by using SQL's "LIKE" statement */ static function lookup($publisherId, $types, $title) { $fc = $title[0]; if ($fc === '%' and strlen($title) > 1 and Q_Config::get('Streams', 'lookup', 'requireTitleIndex', true)) { throw new Q_Exception_WrongValue(array('field' => 'title', 'range' => "something that doesn't start with %")); } $limit = Q_Config::get('Streams', 'lookup', 'limit', 10); return Streams_Stream::select('*')->where(array('publisherId' => $publisherId, 'type' => $types, 'title LIKE ' => $title))->limit($limit)->fetchDbRows(); }