function Streams_before_Q_Utils_canWriteToPath($params, &$result)
{
    extract($params);
    /**
     * @var $path
     * @var $throwIfNotWritable
     * @var $mkdirIfMissing
     */
    // Assume that Users/before/Q/Utils/canWriteToPath already executed
    $user = Users::loggedInUser();
    $userId = $user ? $user->id : "";
    $app = Q_Config::expect('Q', 'app');
    $len = strlen(APP_DIR);
    if (substr($path, 0, $len) === APP_DIR) {
        $sp = str_replace(DS, '/', substr($path, $len + 1));
        if (substr($sp, -1) === '/') {
            $sp = substr($sp, 0, strlen($sp) - 1);
        }
        $prefix = "files/{$app}/uploads/Streams/";
        $len = strlen($prefix);
        if (substr($sp, 0, $len) === $prefix) {
            $splitId = Q_Utils::splitId($userId);
            $prefix2 = "files/{$app}/uploads/Streams/invitations/{$splitId}/";
            if ($userId and substr($sp, 0, strlen($prefix2)) === $prefix2) {
                $result = true;
                // user can write any invitations here
                return;
            }
            $parts = explode('/', substr($sp, $len));
            $c = count($parts);
            if ($c >= 3) {
                $result = false;
                for ($j = 0; $j < $c - 3; ++$j) {
                    $publisherId = implode('', array_slice($parts, 0, $j + 1));
                    $l = $j;
                    for ($i = $c - 1; $i > $j; --$i) {
                        $l = $i;
                        if (in_array($parts[$i], array('icon', 'file'))) {
                            break;
                        }
                    }
                    $name = implode('/', array_slice($parts, $j + 1, $l - $j - 1));
                    if ($name and $stream = Streams::fetchOne($userId, $publisherId, $name)) {
                        $result = $stream->testWriteLevel('edit');
                        Streams::$cache['canWriteToStream'] = $stream;
                        break;
                    }
                }
            }
        }
    }
    if (!$result and $throwIfNotWritable) {
        throw new Q_Exception_CantWriteToPath();
    }
}
예제 #2
0
function Users_after_Q_image_save($params, &$return)
{
    extract($params);
    /**
     * @var string $path
     * @var string $subpath
     * @var Users_User $user
     */
    $user = Users::loggedInUser(true);
    $fullpath = $path . ($subpath ? DS . $subpath : '');
    $splitId = Q_Utils::splitId($user->id);
    $prefix = "uploads/Users/{$splitId}/icon";
    if (substr($fullpath, 0, strlen($prefix)) === $prefix) {
        if ($user->icon != $subpath) {
            $user->icon = Q_Html::themedUrl("{$path}/{$subpath}");
            $user->save();
            // triggers any registered hooks
            Users::$cache['iconUrlWasChanged'] = true;
        } else {
            Users::$cache['iconUrlWasChanged'] = false;
        }
    }
}
예제 #3
0
 /**
  * Imports an icon and sets $user->icon to the url.
  * @method importIcon
  * @static
  * @param {array} $user The user for whom the icon should be downloaded
  * @param {array} [$urls=array()] Array of urls
  * @param {string} [$directory=null] Defaults to APP/files/APP/uploads/Users/USERID/icon/imported
  * @return {string} the path to the icon directory
  */
 static function importIcon($user, $urls = array(), $directory = null)
 {
     if (empty($directory)) {
         $app = Q_Config::expect('Q', 'app');
         $directory = APP_FILES_DIR . DS . $app . DS . 'uploads' . DS . 'Users' . DS . Q_Utils::splitId($user->id) . DS . 'icon' . DS . 'imported';
     }
     if (empty($urls)) {
         return $directory;
     }
     Q_Utils::canWriteToPath($directory, false, true);
     $type = Q_Config::get('Users', 'login', 'iconType', 'wavatar');
     $largestSize = 0;
     $largestUrl = null;
     $largestImage = null;
     foreach ($urls as $basename => $url) {
         if (!is_string($url)) {
             continue;
         }
         $filename = $directory . DS . $basename;
         $info = pathinfo($filename);
         $size = $info['filename'];
         if ((string) (int) $size !== $size) {
             continue;
         }
         if ($largestSize < (int) $size) {
             $largestSize = (int) $size;
             $largestUrl = $url;
         }
     }
     if ($largestSize) {
         $largestImage = imagecreatefromstring(file_get_contents($largestUrl));
     }
     foreach ($urls as $basename => $url) {
         if (is_string($url)) {
             $filename = $directory . DS . $basename;
             $info = pathinfo($filename);
             $size = $info['filename'];
             $success = false;
             if ($largestImage and (string) (int) $size === $size) {
                 if ($size == $largestSize) {
                     $image = $largestImage;
                     $success = true;
                 } else {
                     $image = imagecreatetruecolor($size, $size);
                     imagealphablending($image, false);
                     $success = imagecopyresampled($image, $largestImage, 0, 0, 0, 0, $size, $size, $largestSize, $largestSize);
                 }
             }
             if (!$success) {
                 $ch = curl_init();
                 curl_setopt($ch, CURLOPT_URL, $url);
                 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
                 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
                 $data = curl_exec($ch);
                 curl_close($ch);
                 $image = imagecreatefromstring($data);
             }
             $info = pathinfo($filename);
             switch ($info['extension']) {
                 case 'png':
                     $func = 'imagepng';
                     imagesavealpha($image, true);
                     imagealphablending($image, true);
                     break;
                 case 'jpeg':
                 case 'jpeg':
                     $func = 'imagejpeg';
                     break;
                 case 'gif':
                     $func = 'imagegif';
                     break;
             }
             call_user_func($func, $image, $directory . DS . $info['filename'] . '.png');
         } else {
             Q_Image::put($directory . DS . $basename, $url['hash'], $url['size'], $type, Q_Config::get('Users', 'login', 'gravatar', false));
         }
     }
     $head = APP_FILES_DIR . DS . $app . DS . 'uploads';
     $tail = str_replace(DS, '/', substr($directory, strlen($head)));
     $user->icon = '{{baseUrl}}/uploads' . $tail;
     return $directory;
 }
function Users_before_Q_Utils_canWriteToPath($params, &$result)
{
    extract($params);
    /**
     * @var $path
     * @var $throwIfNotWritable
     * @var $mkdirIfMissing
     */
    // The Users plugin requires that a user be logged in before uploading a file,
    // and only in the proper directories.
    $user = Users::loggedInUser($throwIfNotWritable);
    if (!$user) {
        return false;
    }
    $app = Q_Config::expect('Q', 'app');
    $subpaths = Q_Config::get('Users', 'paths', 'uploads', array('files/{{app}}/uploads/Users/{{userId}}' => true));
    $paths = array();
    $path = str_replace(array("/", "\\"), DS, $path);
    foreach ($subpaths as $subpath => $can_write) {
        if (!$can_write) {
            continue;
        }
        $subpath = Q::interpolate($subpath, array('userId' => Q_Utils::splitId($user->id), 'app' => $app));
        if ($subpath and ($subpath[0] !== '/' or $subpath[0] !== DS)) {
            $subpath = DS . $subpath;
        }
        $last_char = substr($subpath, -1);
        if ($subpath and $last_char !== '/' and $last_char !== DS) {
            $subpath .= DS;
        }
        $paths[] = APP_DIR . $subpath;
        foreach (Q_Config::get('Q', 'plugins', array()) as $plugin) {
            $c = strtoupper($plugin) . '_PLUGIN_DIR';
            if (defined($c)) {
                $paths[] = constant($c) . $subpath;
            }
        }
        $paths[] = Q_DIR . $subpath;
    }
    if (strpos($path, "../") === false and strpos($path, ".." . DS) === false) {
        foreach ($paths as $p) {
            $p = str_replace(array("/", "\\"), DS, $p);
            $len = strlen($p);
            if (strncmp($path, $p, $len) === 0) {
                // we can write to this path
                if ($mkdirIfMissing and !file_exists($path)) {
                    $mode = is_integer($mkdirIfMissing) ? $mkdirIfMissing : 0777;
                    $mask = umask(Q_Config::get('Q', 'internal', 'umask', 00));
                    if (!@mkdir($path, $mode, true)) {
                        throw new Q_Exception_FilePermissions(array('action' => 'create', 'filename' => $path, 'recommendation' => ' Please set your files directory to be writable.'));
                    }
                    umask($mask);
                    $dir3 = $path;
                    do {
                        chmod($dir3, $mode);
                        $dir3 = dirname($dir3);
                    } while ($dir3 and $dir3 != $p and $dir3 . DS != $p);
                }
                $result = true;
                return;
            }
        }
    }
    if ($throwIfNotWritable) {
        throw new Q_Exception_CantWriteToPath();
    }
    $result = false;
}
예제 #5
0
 static function invitationsPath($invitingUserId)
 {
     $app = Q_Config::expect('Q', 'app');
     $subpath = Q_Config::get('Streams', 'invites', 'subpath', '{{app}}/uploads/Streams/invitations');
     return APP_FILES_DIR . DS . Q::interpolate($subpath, compact('app')) . DS . Q_Utils::splitId($invitingUserId);
 }
예제 #6
0
/**
 * Used by HTTP clients to create a new stream in the system.
 * @class HTTP Streams stream
 * @method post
 * @param {array} [$params] Parameters that can come from the request
 *   @param {string} $params.publisherId  Required. The id of the user to publish the stream.
 *   @param {string} $params.type Required. The type of the stream.
 *   @param {string} [$params.Q_Streams_related_publisherId] Optionally indicate the publisher of the stream to relate the newly created to. Used together with the related.streamName option.
 *   @param {string} [$params.Q_Streams_related_streamName] Optionally indicate the name of a stream to relate the newly crated stream to. This is often necessary in order to obtain permissions to create the stream.
 *   @param {bool} [$params.dontSubscribe=false] Pass 1 or true here in order to skip auto-subscribing to the newly created stream.
 *   @param {array} [$params.icon] This is used to upload a custom icon for the stream which will then be saved in different sizes. See fields for Q/image/post method
 *     @param {string} [$params.icon.data]  Required if $_FILES is empty. Base64-encoded  data URI - see RFC 2397
 *     @param {string} [$params.icon.path="uploads"] parent path under web dir (see subpath)
 *     @param {string} [$params.icon.subpath=""] subpath that should follow the path, to save the image under
 *     @param {string} [$params.icon.merge=""] path under web dir for an optional image to use as a background
 *     @param {string} [$params.icon.crop] array with keys "x", "y", "w", "h" to crop the original image
 *     @param {string} [$params.icon.save=array("x" => "")] array of $size => $basename pairs
 *      where the size is of the format "WxH", and either W or H can be empty.
 *   @param {array} [$params.file] This is used to upload a custom icon for the stream which will then be saved in different sizes. See fields for Q/image/post method
 *     @param {string} [$params.file.data]  Required if $_FILES is empty. Base64-encoded  data URI - see RFC 2397
 *     @param {string} [$params.file.path="uploads"] parent path under web dir (see subpath)
 *     @param {string} [$params.file.subpath=""] subpath that should follow the path, to save the file under
 *     @param {string} [$params.file.name] override name of the file, after the subpath
 */
function Streams_stream_post($params = array())
{
    $user = Users::loggedInUser(true);
    $publisherId = Streams::requestedPublisherId();
    if (empty($publisherId)) {
        $publisherId = $_REQUEST['publisherId'] = $user->id;
    }
    $req = array_merge($_REQUEST, $params);
    $type = Streams::requestedType(true);
    $types = Q_Config::expect('Streams', 'types');
    if (!array_key_exists($type, $types)) {
        throw new Q_Exception("This app doesn't support streams of type {$type}", 'type');
    }
    $create = Streams_Stream::getConfigField($type, 'create', false);
    if (!$create) {
        throw new Q_Exception("This app doesn't let clients directly create streams of type {$type}", 'type');
    }
    // Should this stream be related to another stream?
    $relate = array();
    $relate['streamName'] = Q_Request::special("Streams.related.streamName", null, $req);
    if (isset($relate['streamName'])) {
        $relate['publisherId'] = Q_Request::special("Streams.related.publisherId", $publisherId, $req);
        $relate['type'] = Q_Request::special("Streams.related.type", "", $req);
        $relate['weight'] = "+1";
        // TODO: introduce ways to have "1" and "+1" for some admins etc.
    }
    // Split the id for saving files in the filesystem
    $splitId = Q_Utils::splitId($publisherId);
    // Hold on to any icon that was posted
    $icon = null;
    if (!empty($req['icon']) and is_array($req['icon'])) {
        $icon = $req['icon'];
        unset($req['icon']);
    }
    // Hold on to any file that was posted
    $file = null;
    if (!empty($req['file']) and is_array($req['file'])) {
        $file = $req['file'];
        unset($req['file']);
    }
    // Check if the user owns the stream
    if ($user->id === $publisherId) {
        $asOwner = true;
    } else {
        $streamTemplate = Streams_Stream::getStreamTemplate($publisherId, $type, 'Streams_Stream');
        $asOwner = $streamTemplate ? $streamTemplate->testAdminLevel('own') : false;
    }
    // Check if client can set the name of this stream
    if (isset($req['name'])) {
        $possible = Q_Config::get('Streams', 'possibleUserStreams', $req['name'], false);
        if (!$asOwner or !$possible) {
            throw new Users_Exception_NotAuthorized();
        }
    }
    // Get allowed fields
    $allowedFields = array_merge(array('publisherId', 'name', 'type', 'icon', 'file'), Streams::getExtendFieldNames($type, $asOwner));
    $fields = Q::take($req, $allowedFields);
    // Prevent setting restricted fields
    if (is_array($create)) {
        $restrictedFields = array_diff($allowedFields, $create);
        foreach ($restrictedFields as $fieldName) {
            if (in_array($fieldName, array('publisherId', 'type'))) {
                continue;
            }
            if (isset($req[$fieldName])) {
                throw new Users_Exception_NotAuthorized();
            }
        }
    }
    // Create the stream
    $stream = Streams::create($user->id, $publisherId, $type, $fields, $relate, $result);
    $messageTo = false;
    if (isset($result['messagesTo'])) {
        $messageTo = reset($result['messagesTo']);
        $messageTo = reset($messageTo);
        if (is_array($messageTo)) {
            $messageTo = reset($messageTo);
        }
        $messageTo = $messageTo->exportArray();
    }
    Q_Response::setSlot('messageTo', $messageTo);
    // Process any icon that was posted
    if ($icon === true) {
        $icon = array();
    }
    if (is_array($icon)) {
        if (empty($icon['path'])) {
            $icon['path'] = 'uploads/Streams';
        }
        if (empty($icon['subpath'])) {
            $icon['subpath'] = "{$splitId}/{$stream->name}/icon/" . time();
        }
        Q_Response::setSlot('icon', Q::event("Q/image/post", $icon));
        // the Streams/after/Q_image_save hook saves some attributes
    }
    // Process any file that was posted
    if ($file === true) {
        $file = array();
    }
    if (is_array($file)) {
        if (empty($file['path'])) {
            $file['path'] = 'uploads/Streams';
        }
        if (empty($file['subpath'])) {
            $file['subpath'] = "{$splitId}/{$stream->name}/file/" . time();
        }
        Q_Response::setSlot('file', Q::event("Q/file/post", $file));
        // the Streams/after/Q_file_save hook saves some attributes
    }
    // Re-fetch the stream object from the Streams::fetch cache,
    // since it might have been retrieved and modified to be different
    // from what is currently in $stream.
    // This also calculates the access levels on the stream.
    $stream = Streams::fetchOne($user->id, $publisherId, $stream->name);
    if (empty($req['dontSubscribe'])) {
        // autosubscribe to streams you yourself create, using templates
        $stream->subscribe();
    }
    Streams::$cache['stream'] = $stream;
}
예제 #7
0
 /**
  * Get the url of the stream's icon
  * @param {string} [$basename=""] The last part after the slash, such as "50.png"
  * @return {string} The stream's icon url
  */
 function iconUrl($basename = null)
 {
     if (empty($this->icon)) {
         return null;
     }
     $url = Q::interpolate($this->icon, array('baseUrl' => Q_Request::baseUrl(), 'publisherId' => Q_Utils::splitId($this->publisherId)));
     $url = Q_Valid::url($url) ? $url : "plugins/Streams/img/icons/{$url}";
     if ($basename) {
         if (strpos($basename, '.') === false) {
             $basename = "{$basename}.png";
         }
         $url .= "/{$basename}";
     }
     return Q_Html::themedUrl($url);
 }
예제 #8
0
 /**
  * Get the url of the user's icon
  * @param {string} [$basename=""] The last part after the slash, such as "50.png"
  * @return {string} The stream's icon url
  */
 function iconUrl($basename = null)
 {
     $replacements = array('userId' => Q_Utils::splitId($this->id));
     return Users::iconUrl(isset($this->icon) ? Q::interpolate($this->icon, $replacements) : 'default', $basename);
 }
예제 #9
0
 /**
  * Create directory name (from shipment stream name) to upload related files (labels, invoices, etc)
  * @method splitStreamName
  * @static
  * @param string $streamName shipment stream name
  * @return string
  */
 static function splitStreamName($streamName)
 {
     $parts = explode('/', $streamName);
     $last = Q_Utils::splitId(array_pop($parts));
     return implode('/', $parts) . '/' . $last;
 }