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();
}
示例#2
0
/**
 * This tool renders a user avatar
 *
 * @param {array} $options An associative array of parameters, containing:
 * @param {string} [$options.userId]
 *   The user's id. Defaults to id of the logged-in user, if any.
 *   Can be '' for a blank-looking avatar.
 * @param {boolean} [options.short]
 *   Optional. Renders the short version of the display name.
 * @param {boolean|integer} [options.icon=false]
 *   Optional. Pass the size in pixels of the (square) icon to render
 *   before the username. Or pass true to render the default size.
 * @param {array} [options.iconAttributes]
 *   Optional. Array of attributes to render for the icon.
 * @param {boolean|array} [options.editable=false]
 *   Optional. Whether to provide an interface for editing the user's info. Can be array containing one or more of "icon", "name".
 * @param {boolean} [$options.show] The parts of the name to show. Can have the letters "f", "l", "u" in any order.
 * @param {boolean} [options.cacheBust=null]
 *   Number of milliseconds to use for Q_Uri::cacheBust for combating unintended caching on some environments.
 * @param {boolean} [options.renderOnClient]
 *   If true, only the html container is rendered, so the client will do the rest.
 */
function Users_avatar_tool($options)
{
    $defaults = array('icon' => false, 'short' => false, 'cacheBust' => null, 'editable' => false);
    $options = array_merge($defaults, $options);
    Q_Response::addStylesheet('plugins/Users/css/Users.css');
    $loggedInUser = Users::loggedInUser();
    $loggedInUserId = $loggedInUser ? $loggedInUser->id : "";
    if (empty($options['userId'])) {
        $options['userId'] = $loggedInUserId;
    }
    unset($options['iconAttributes']);
    if (empty($options['editable'])) {
        $options['editable'] = array();
    } else {
        if (is_string($options['editable'])) {
            $options['editable'] = array($options['editable']);
        } else {
            if ($options['editable'] === true) {
                $options['editable'] = array('icon', 'name');
            }
        }
    }
    Q_Response::setToolOptions($options);
    if (!empty($options['renderOnClient'])) {
        return '';
    }
    $avatar = Streams_Avatar::fetch($loggedInUserId, $options['userId']);
    if (!$avatar) {
        return '';
    }
    $result = '';
    if ($icon = $options['icon']) {
        if ($icon === true) {
            $icon = Q_Config::get('Users', 'icon', 'defaultSize', 40);
        }
        $attributes = isset($options['iconAttributes']) ? $options['iconAttributes'] : array();
        $class = "Users_avatar_icon Users_avatar_icon_{$icon}";
        $attributes['class'] = isset($attributes['class']) ? $attributes['class'] . ' ' . $class : $class;
        if (isset($options['cacheBust'])) {
            $attributes['cacheBust'] = $options['cacheBust'];
        }
        $result .= Q_Html::img(Users::iconUrl($avatar->icon, "{$icon}.png"), 'user icon', $attributes);
    }
    $o = $options['short'] ? array('short' => true) : array();
    $o['html'] = true;
    if (in_array('name', $options['editable'])) {
        $o['show'] = 'fl';
        $streams = Streams::fetch(null, $options['userId'], array('Streams/user/firstName', 'Streams/user/lastName', 'Streams/user/username'));
        foreach ($streams as $s) {
            $s->addPreloaded();
        }
    }
    if (!empty($options['show'])) {
        $o['show'] = $options['show'];
    }
    $displayName = $avatar->displayName($o, 'Someone');
    $result .= "<span class='Users_avatar_name'>{$displayName}</span>";
    return $result;
}
示例#3
0
function Streams_avatar_response()
{
    $prefix = $limit = $userIds = $batch = $public = null;
    extract($_REQUEST, EXTR_IF_EXISTS);
    $user = Users::loggedInUser();
    $asUserId = $user ? $user->id : "";
    if (isset($prefix)) {
        $avatars = Streams_Avatar::fetchByPrefix($asUserId, $prefix, compact('limit', 'public'));
    } else {
        if (isset($batch)) {
            $batch = json_decode($batch, true);
            if (!isset($batch)) {
                throw new Q_Exception_WrongValue(array('field' => 'batch', 'range' => '{userIds: [userId1, userId2, ...]}'));
            }
            if (!isset($batch['userIds'])) {
                throw new Q_Exception_RequiredField(array('field' => 'userIds'));
            }
            $userIds = $batch['userIds'];
        }
        if (!isset($userIds)) {
            throw new Q_Exception_RequiredField(array('field' => 'userIds'));
        }
        if (is_string($userIds)) {
            $userIds = explode(",", $userIds);
        }
        $avatars = Streams_Avatar::fetch($asUserId, $userIds);
    }
    $avatars = Db::exportArray($avatars);
    if (isset($batch)) {
        $result = array();
        foreach ($userIds as $userId) {
            $result[] = array('slots' => array('avatar' => isset($avatars[$userId]) ? $avatars[$userId] : null));
        }
        Q_Response::setSlot('batch', $result);
    } else {
        Q_Response::setSlot('avatars', $avatars);
    }
    return $avatars;
}
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);
            }
        }
    }
}
示例#5
0
 /**
  * Retrieve avatars for one or more publishers as displayed to a particular user.
  * 
  * @method fetchByPrefix
  * @static
  * @param $toUserId {User_User|string} The id of the user to which this would be displayed
  * @param $prefix {string} The prefix for the firstName
  * @param {array} $options=array()
  *	'limit' => number of records to fetch
  *  'fields' => defaults to array('username', 'firstName', 'lastName') 
  *  'public' => defaults to false. If true, also gets publicly accessible names.
  * @return {array}
  */
 static function fetchByPrefix($toUserId, $prefix, $options = array())
 {
     if ($toUserId instanceof Users_User) {
         $toUserId = $toUserId->id;
     }
     $toUserId = empty($options['public']) ? $toUserId : array($toUserId, '');
     $fields = isset($options['fields']) ? $options['fields'] : array('firstName', 'lastName', 'username');
     $limit = isset($options['limit']) ? $options['limit'] : Q_Config::get('Users', 'Avatar', 'fetchByPrefix', 'limit', 100);
     $max = $limit;
     $avatars = array();
     $prefixes = preg_split("/\\s+/", $prefix);
     $prefix = reset($prefixes);
     $criteria = array();
     if (count($prefixes) < 2) {
         foreach ($fields as $field) {
             $criteria[] = array($field => new Db_Range($prefix, true, false, true));
         }
     } else {
         $criteria = array(array('firstName' => new Db_Range($prefixes[0], true, false, true), 'lastName' => new Db_Range($prefixes[1], true, false, true)), array('firstName' => new Db_Range($prefixes[0], true, false, true), 'username' => new Db_Range($prefixes[1], true, false, true)), array('username' => new Db_Range($prefixes[0], true, false, true), 'lastName' => new Db_Range($prefixes[1], true, false, true)));
     }
     $count = count($criteria);
     for ($i = 0; $i < $count; ++$i) {
         // NOTE: sharding should be done on toUserId only, not publisherId
         $q = Streams_Avatar::select('*')->where(array('toUserId' => $toUserId))->andWhere($criteria[$i])->orderBy('firstName');
         $rows = $q->limit($max)->fetchDbRows();
         foreach ($rows as $r) {
             if (!isset($avatars[$r->publisherId]) or $r->toUserId !== '') {
                 $avatars[$r->publisherId] = $r;
             }
         }
         $max = $limit - count($avatars);
         if ($max <= 0) {
             break;
         }
     }
     return $avatars;
 }
示例#6
0
 /**
  * Updates the publisher's avatars, which may have changed with the taintedAccess.
  * This function should be called during rare events that may cause the
  * publisher's avatar to change appearance for certain users viewing it.<br/>
  *
  * You should rarely have to call this function. It is used internally by the model,
  * in two main situations:
  *
  * 1)  adding, removing or modifying a Streams_Access row for Streams/user/firstName or Streams/user/lastName
  *	In this case, the function is able to update exactly the avatars that need updating.
  * 
  * 2) adding, removing or modifying a Stream row for Streams/user/firstName or Streams/user/lastName
  *	In this case, there may be some avatars which this function will miss.
  *	These correspond to users which are reachable by the access array for one stream,
  *	but not the other. For example, if Streams/user/firstName is being updated, but
  *	a particular user is reachable only by the access array for Streams/user/lastName, then
  *	their avatar will not be updated and contain a stale value for firstName.
  *	To fix this, the Streams_Stream model passes true in the 4th parameter to this function.
  * @method updateAvatars
  * @static
  * @param {string} $publisherId
  *  id of the publisher whose avatar to update
  * @param {array} $taintedAccess
  *  array of Streams_Access objects representing access information that is either
  *  about to be saved, are about to be overwritten, or will be deleted
  * @param {string|Streams_Stream} $streamName
  *  pass the stream name here. You can also pass a Stream_Stream object here,
  *  in which case it will be used, instead of selecting that stream from the database.
  * @param {boolean} $updateToPublicValue=false
  *  if you want to first update all the avatars for this stream
  *  to the what the public would see, to avoid the situation described in 2).
  */
 static function updateAvatars($publisherId, $taintedAccess, $streamName, $updateToPublicValue = false)
 {
     if (!isset($streamName)) {
         $streamAccesses = array();
         foreach ($taintedAccess as $access) {
             $streamAccesses[$access->streamName][] = $access;
         }
         if (count($streamAccesses) > 1) {
             foreach ($streamAccesses as $k => $v) {
                 self::updateAvatars($publisherId, $v, $k);
             }
             return false;
         }
     }
     if ($streamName instanceof Streams_Stream) {
         $stream = $streamName;
         $streamName = $stream->name;
     }
     // If we are here, all the Stream_Access objects have the same streamName
     if ($streamName !== 'Streams/user/firstName' and $streamName !== 'Streams/user/lastName' and $streamName !== 'Streams/user/username') {
         // we don't care about access to other streams being updated
         return false;
     }
     $showToUserIds = array();
     // Select the user corresponding to this publisher
     $user = new Users_User();
     $user->id = $publisherId;
     if (!$user->retrieve(null, null, array('ignoreCache' => true))) {
         throw new Q_Exception_MissingRow(array('table' => 'user', 'criteria' => 'id = ' . $user->id));
     }
     // Obtain the stream object to use
     if (isset($stream)) {
         if (!isset($stream->content)) {
             $stream->content = '';
         }
     } else {
         // If the $stream isn't already defined, select it
         $stream = new Streams_Stream();
         $stream->publisherId = $publisherId;
         $stream->name = $streamName;
         if (!$stream->retrieve()) {
             // Strange, this stream doesn't exist.
             // Well, we will just silently set the content to '' then
             $stream->content = '';
         }
     }
     $content_readLevel = Streams::$READ_LEVEL['content'];
     $readLevels = array();
     $label_readLevels = array();
     $contact_label_list = array();
     $removed_labels = array();
     // First, assign all the readLevels that are directly set for specific users,
     // and aggregate the contact_labels from the other accesses, for an upcoming select.
     foreach ($taintedAccess as $access) {
         if ($userId = $access->ofUserId) {
             $readLevel = $access->readLevel;
             $readLevels[$userId] = $readLevel;
             if ($readLevel < 0) {
                 $showToUserIds[$userId] = null;
                 // not determined yet
             } else {
                 if ($readLevel >= $content_readLevel) {
                     $showToUserIds[$userId] = true;
                 } else {
                     $showToUserIds[$userId] = false;
                 }
             }
         } else {
             if ($access->ofContactLabel) {
                 $ofContactLabel = $access->ofContactLabel;
                 $contact_label_list[] = $ofContactLabel;
                 if ($access->get('removed', false)) {
                     $removed_labels[$ofContactLabel] = true;
                 } else {
                     $label_readLevels[$ofContactLabel] = $access->readLevel;
                 }
             }
         }
     }
     // Now, get all the people affected by this change, and their readLevels
     $readLevels2 = array();
     if ($contact_label_list) {
         $contact_label_list = array_unique($contact_label_list);
         $contacts = Users_Contact::select('*')->where(array('userId' => $publisherId, 'label' => $contact_label_list))->fetchDbRows(null, '', 'contactUserId');
         foreach ($contacts as $contact) {
             $contactUserId = $contact->contactUserId;
             if (isset($showToUserIds[$contactUserId])) {
                 // this user had their read level set directly by the access,
                 // which overrides read levels set by access using ofContactLabel
                 continue;
             }
             if (isset($removed_labels[$ofContactLabel])) {
                 // this label doesn't affect readLevels anymore, since it was deleted
                 // but put this contact's id on a list whose readLevels need to be determined
                 $showToUserIds[$contactUserId] = null;
                 continue;
             }
             if (!isset($label_readLevels[$contact->label])) {
                 continue;
             }
             $readLevel = $label_readLevels[$contact->label];
             if (!isset($readLevels2[$contactUserId])) {
                 $readLevels2[$contactUserId] = $readLevel;
             } else {
                 $readLevels2[$contactUserId] = max($readLevels2[$contactUserId], $readLevel);
             }
         }
     }
     // Now step through all the users we found who were found through ofContactLabel
     // and make sure we update the avatar rows that were meant for them.
     foreach ($readLevels2 as $userId => $rl) {
         if ($rl >= $content_readLevel) {
             $showToUserIds[$userId] = true;
         } else {
             // in order for this to happen, two things had to be true:
             // 1) there was no access that directly set a readLevel >= $content_readLevel
             // 2) there was no access that set a readLevel >= $content_readLevel for any label containing this user
             // therefore, their view should be the public view
             $showToUserIds[$userId] = 'public';
         }
     }
     // Resolve all the undetermined readLevels
     foreach ($showToUserIds as $userId => $v) {
         if (!isset($v)) {
             // if the readLevel hasn't been determined by now, it's the same as the public one
             $showToUserIds[$userId] = 'public';
         }
     }
     // Set up the self avatar:
     $showToUserIds[$publisherId] = true;
     // Finally, set up the public avatar:
     if (!isset($stream->readLevel)) {
         $stream->readLevel = Streams_Stream::$DEFAULTS['readLevel'];
     }
     $showToUserIds[""] = $stream->readLevel >= $content_readLevel;
     // Now, we update the avatars:
     $parts = explode('/', $streamName);
     $field = end($parts);
     $rows_that_show = array();
     $rows_that_hide = array();
     foreach ($showToUserIds as $userId => $show) {
         if ($show === 'public') {
             // If no show is explicitly specified, use the value used for the rest of the public
             $show = $showToUserIds[""];
         }
         if ($show === true) {
             $rows_that_show[] = array('publisherId' => $publisherId, 'toUserId' => $userId, 'username' => $user->username, 'icon' => $user->icon, 'updatedTime' => new Db_Expression("CURRENT_TIMESTAMP"), $field => $stream->content);
         } else {
             if ($show === false) {
                 $rows_that_hide[] = array('publisherId' => $publisherId, 'toUserId' => $userId, 'username' => $user->username, 'icon' => $user->icon, 'updatedTime' => new Db_Expression("CURRENT_TIMESTAMP"), $field => '');
             }
         }
     }
     $updates_that_show = array('username' => $user->username, 'icon' => $user->icon, 'updatedTime' => new Db_Expression("CURRENT_TIMESTAMP"), $field => $stream->content);
     $updates_that_hide = array('username' => $user->username, 'icon' => $user->icon, 'updatedTime' => new Db_Expression("CURRENT_TIMESTAMP"), $field => '');
     // We are now ready to make changes to the database.
     if ($updateToPublicValue) {
         Streams_Avatar::update()->set(array($field => $showToUserIds[""] ? $stream->content : ''))->where(compact('publisherId'))->execute();
     }
     Streams_Avatar::insertManyAndExecute($rows_that_show, array('onDuplicateKeyUpdate' => $updates_that_show));
     Streams_Avatar::insertManyAndExecute($rows_that_hide, array('onDuplicateKeyUpdate' => $updates_that_hide));
 }
示例#7
0
文件: tool.php 项目: dmitriz/Platform
/**
 * 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'));
}