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(); } }
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; } } }
/** * 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; }
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); }
/** * 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; }
/** * 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); }
/** * 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); }
/** * 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; }