/** * @method afterRemoveExcecute * @param {Db_Result} $result * @param {Db_Query} $query * @return {Db_Result} */ function afterRemoveExecute($result, $query) { $stream = $this; // if the above call threw an exception, then we will not be doing the following. Q_Utils::sendToNode(array("Q/method" => "Streams/Stream/remove", "stream" => Q::json_encode($stream->toArray()))); /** * @event Streams/remove/$streamType {after} * @param {Streams_Stream} stream * @param {string} asUserId */ Q::event("Streams/remove/{$stream->type}", compact('stream', 'result'), 'after'); if ($this->name !== 'Streams/user/firstName' and $this->name !== 'Streams/user/lastName') { return $result; } // 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; }
/** * Access tool * @class Streams access * @constructor * @param {array} $options Options for the tool * @param {string} [$options.publisherId] the id of the user who is publishing the stream * @param {string} [$options.streamName] the name of the stream for which to edit access levels * @param {array} [$options.tabs] array of tab name => title. Defaults to read, write, admin tabs. * @param {array} [$options.ranges] associative array with keys "read", "write", "admin" and values as associative arrays of ($min, $max) for the displayed levels. * @param {boolean} [$options.controls] optionally set this to true to render only the controls */ function Streams_access_tool($options) { $tabs = array('read' => 'visible to', 'write' => 'editable by', 'admin' => 'members'); extract($options); $user = Users::loggedInUser(true); /** * @var string $streamName */ if (empty($streamName)) { $streamName = Streams::requestedName(true); } if (empty($publisherId)) { $publisherId = Streams::requestedPublisherId(); if (empty($publisherId)) { $publisherId = $user->id; } } reset($tabs); $tab = Q::ifset($_REQUEST, 'tab', key($tabs)); $stream = Streams::fetchOne($user->id, $publisherId, $streamName); if (!$stream) { throw new Q_Exception_MissingRow(array('table' => 'stream', 'criteria' => 'with that name')); } $stream->addPreloaded($user->id); if (!$stream->testAdminLevel('own')) { throw new Users_Exception_NotAuthorized(); } $access_array = Streams_Access::select('*')->where(array('publisherId' => $stream->publisherId, 'streamName' => $stream->name))->andWhere("{$tab}Level != -1")->fetchDbRows(); $labelRows = Users_Label::fetch($stream->publisherId, '', true); $labels = array(); $icons = array(); foreach ($labelRows as $label => $row) { $labels[$label] = $row->title; $icons[$label] = "labels/{$label}"; } $userId_list = array(); foreach ($access_array as $a) { if ($a->ofUserId) { $userId_list[] = $a->ofUserId; } } $avatar_array = empty($userId_list) ? array() : Streams_Avatar::fetch($user->id, $userId_list); switch ($tab) { case 'read': $levels = Q_Config::get('Streams', 'readLevelOptions', array()); break; case 'write': $levels = Q_Config::get('Streams', 'writeLevelOptions', array()); break; case 'admin': $levels = Q_Config::get('Streams', 'adminLevelOptions', array()); break; } if (isset($ranges[$tab])) { $range_min = reset($ranges[$tab]); $range_max = end($ranges[$tab]); foreach ($levels as $k => $v) { if ($k < $range_min) { unset($levels[$k]); } if ($k > $range_max) { unset($levels[$k]); } } } $accessActionUrl = Q_Uri::url("Streams/access?publisherId={$publisherId}&streamName={$streamName}"); $dir = Q_Config::get('Users', 'paths', 'icons', 'files/Users/icons'); $accessArray = Db::exportArray($access_array); $avatarArray = Db::exportArray($avatar_array); if (empty($controls)) { Q_Response::addScript("plugins/Streams/js/Streams.js"); Q_Response::addScript("plugins/Streams/js/tools/access.js"); Q_Response::setToolOptions(compact('accessArray', 'avatarArray', 'labels', 'icons', 'tab', 'publisherId', 'streamName')); } else { Q_Response::setSlot('extra', array('stream' => $stream->exportArray(), 'accessArray' => $accessArray, 'avatarArray' => $avatarArray, 'labels' => $labels, 'icons' => $icons)); } return Q::view('Streams/tool/access.php', compact('stream', 'tabs', 'tab', 'labels', 'icons', 'levels', 'dir', 'publisherId', 'streamName', 'accessActionUrl', 'controls')); }
/** * Calculates the access for one or more streams by querying the database * Modifies the objects in the $streams array, setting their access levels. * After the function returns, you will be able to call the methods * testReadLevel(), testWriteLevel() and testAdminLevel() * on these streams before using them on the user's behalf. * @method fetch * @static * @param {string} $asUserId * Set this to the user relative to whom access is calculated. * If this matches the publisherId, just sets full access and calls publishedByFetcher(true). * If this is '', only returns the streams anybody can see. * If this is null, the logged-in user's id is used, or '' if no one is logged in * @param {string} $publisherId * The id of the user publishing these streams * @param {array} $streams * An array of streams, obtained for example by Streams::fetch * @param {boolean} $recalculate=false * Pass true here to force recalculating access to streams for which access was already calculated * @return {integer} * The number of streams that were recalculated */ static function calculateAccess($asUserId, $publisherId, $streams, $recalculate = false) { if (!isset($asUserId)) { $asUserId = Users::loggedInUser(); if (!$asUserId) { $asUserId = ""; } } if ($asUserId instanceof Users_User) { $asUserId = $asUserId->id; } if ($publisherId instanceof Users_User) { $publisherId = $publisherId->id; } if ($recalculate) { $streams2 = $streams; } else { $streams2 = array(); foreach ($streams as $k => $s) { if ($s->get('readLevel', null) === null) { $streams2[$k] = $s; } } } if (empty($streams2)) { return 0; } $public_source = Streams::$ACCESS_SOURCES['public']; $contact_source = Streams::$ACCESS_SOURCES['contact']; $direct_source = Streams::$ACCESS_SOURCES['direct']; $streams3 = array(); $names = array(); foreach ($streams2 as $s) { if ($s->get('asUserId', null) === $asUserId) { continue; } $s->set('asUserId', $asUserId); if ($asUserId and $asUserId == $publisherId) { // The publisher should have full access to every one of their streams. // Streams which are "required", though, won't be deleted by the system. $required = Q_Config::get('Streams', 'requiredUserStreams', $s->name, false); $s->set('isRequired', $required); $s->set('readLevel', Streams::$READ_LEVEL['max']); $s->set('writeLevel', Streams::$WRITE_LEVEL['max']); $s->set('adminLevel', Streams::$ADMIN_LEVEL['max']); $s->set('readLevel_source', $direct_source); $s->set('writeLevel_source', $direct_source); $s->set('adminLevel_source', $direct_source); $s->publishedByFetcher(true); continue; } $s->set('readLevel', $s->readLevel); $s->set('writeLevel', $s->writeLevel); $s->set('adminLevel', $s->adminLevel); $s->set('readLevel_source', $public_source); $s->set('writeLevel_source', $public_source); $s->set('adminLevel_source', $public_source); if (empty($asUserId)) { continue; // No need to fetch further access info. } $names[] = $s->name; $names[] = $s->type . "*"; $streams3[] = $s; } if (empty($names)) { return count($streams2); } // Get the per-label access data // Avoid making a join to allow more flexibility for sharding $accesses = Streams_Access::select('*')->where(array('publisherId' => $publisherId, 'streamName' => $names, 'ofUserId' => array('', $asUserId)))->fetchDbRows(); $labels = array(); foreach ($accesses as $access) { if ($access->ofContactLabel) { $labels[] = $access->ofContactLabel; } } if (!empty($labels)) { $labels = array_unique($labels); $contacts = Users_Contact::select('*')->where(array('userId' => $publisherId, 'label' => $labels, 'contactUserId' => $asUserId))->fetchDbRows(); foreach ($contacts as $contact) { foreach ($accesses as $access) { if ($access->ofContactLabel !== $contact->label) { continue; } foreach ($streams3 as $stream) { $tail = substr($access->streamName, -1); $head = substr($access->streamName, 0, -1); if ($stream->name !== $access->streamName and ($tail !== '*' or $head !== $stream->type)) { continue; } $readLevel = $stream->get('readLevel', 0); $writeLevel = $stream->get('writeLevel', 0); $adminLevel = $stream->get('adminLevel', 0); if ($access->readLevel >= 0 and $access->readLevel > $readLevel) { $stream->set('readLevel', $access->readLevel); $stream->set('readLevel_source', $contact_source); } if ($access->writeLevel >= 0 and $access->writeLevel > $writeLevel) { $stream->set('writeLevel', $access->writeLevel); $stream->set('writeLevel_source', $contact_source); } if ($access->adminLevel >= 0 and $access->adminLevel > $adminLevel) { $stream->set('adminLevel', $access->adminLevel); $stream->set('adminLevel_source', $contact_source); } } } } } // Override with per-user access data foreach ($accesses as $access) { foreach ($streams3 as $stream) { $tail = substr($access->streamName, -1); $head = substr($access->streamName, 0, -1); if ($stream->name !== $access->streamName and ($tail !== '*' or $head !== $stream->type)) { continue; } if ($access->ofUserId === $asUserId) { if ($access->readLevel >= 0) { $stream->set('readLevel', $access->readLevel); $stream->set('readLevel_source', $direct_source); } if ($access->writeLevel >= 0) { $stream->set('writeLevel', $access->writeLevel); $stream->set('writeLevel_source', $direct_source); } if ($access->adminLevel >= 0) { $stream->set('adminLevel', $access->adminLevel); $stream->set('adminLevel_source', $direct_source); } } } } return count($streams2); }
/** * Keeps Access table consistent on row deletion and update avatar * @method afterRemoveExecute * @param {Db_Result} $result * Query result * @param {array} $query * The query which has been excecuted * @return {Db_Result} */ function afterRemoveExecute($result, $query) { if (!empty($this->ofUserId)) { // Removed an access for a specific user Streams::updateAvatar($this->ofUserId, $this->publisherId); return $result; } if (empty($this->ofContactLabel)) { return $result; } // Update all avatars corresponding to access rows for this stream $tainted_access = Streams_Access::select('*')->where(array('publisherId' => $this->publisherId, 'streamName' => $this->streamName))->fetchDbRows(); $found = false; foreach ($tainted_access as $ca) { if ($ca->ofContactLabel === $this->ofContactLabel) { // this should never really happen, since the row was just deleted $found = true; $ca->set('removed', true); } } if (!$found) { $this->set('removed', true); $tainted_access[] = $this; } Streams::updateAvatars($this->publisherId, $tainted_access, $this->streamName); return $result; }