There are various formats supported for the name, mostly due to legacy concerns.
- http(s)://domain/path.ext: A fully qualified url.
- /path/from/uploads.ext: This is a locally uploaded file.
- /path/to/uploads/path.ext: A full path starting from the uploads directory (deprecated).
- ~type/path.ext: A specific type of upload provided by a plugin (deprecated).
- type://domain/path.ext: A specific type of upload provied by a plugin with additional domain information.
/** * Generate a Gravatar image URL based on the provided email address. * * @link http://en.gravatar.com/site/implement/images/ Gravatar Image Requests * @param string $email Email address for the user, used to generate the avatar ID. * @param int $size Target image size. * @return string A formatted Gravatar image URL. */ public static function generateUrl($email, $size = 80) { $avatarID = md5(strtolower($email)); // Figure out our base URLs. Gravatar doesn't support SVGs, so we're stuck with using Vanillicon v1. if (Gdn::request()->scheme() === 'https') { $baseUrl = 'https://secure.gravatar.com/avatar'; $vanilliconBaseUrl = 'https://vanillicon.com'; } else { $baseUrl = 'http://www.gravatar.com/avatar'; $vanilliconBaseUrl = 'http://vanillicon.com'; } if (c('Plugins.Gravatar.UseVanillicon', true)) { // Version 1 of Vanillicon only supports three sizes. Figure out which one is best for this image. if ($size <= 50) { $vanilliconSize = 50; } elseif ($size <= 100) { $vanilliconSize = 100; } else { $vanilliconSize = 200; } $default = "{$vanilliconBaseUrl}/{$avatarID}_{$vanilliconSize}.png"; } else { $configuredDefaultAvatar = c('Plugins.Gravatar.DefaultAvatar', c('Garden.DefaultAvatar')); if ($configuredDefaultAvatar) { $defaultParsed = Gdn_Upload::parse($configuredDefaultAvatar); $default = val('Url', $defaultParsed); } } if (empty($default)) { $default = asset($size <= 50 ? 'plugins/Gravatar/default.png' : 'plugins/Gravatar/default_250.png', true); } $query = ['default' => $default, 'rating' => c('Plugins.Gravatar.Rating', 'g'), 'size' => $size]; return $baseUrl . "/{$avatarID}/?" . http_build_query($query); }
/** * If passed path leads to an image, return size * * @param string $Path Path to file. * @return array [0] => Height, [1] => Width. */ public static function getImageSize($Path) { // Static FireEvent for intercepting non-local files. $Sender = new stdClass(); $Sender->Returns = array(); $Sender->EventArguments = array(); $Sender->EventArguments['Path'] =& $Path; $Sender->EventArguments['Parsed'] = Gdn_Upload::parse($Path); Gdn::pluginManager()->callEventHandlers($Sender, 'Gdn_Upload', 'CopyLocal'); if (!in_array(strtolower(pathinfo($Path, PATHINFO_EXTENSION)), array('gif', 'jpg', 'jpeg', 'png'))) { return array(0, 0); } $ImageSize = @getimagesize($Path); if (is_array($ImageSize)) { if (!in_array($ImageSize[2], array(IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG))) { return array(0, 0); } return array($ImageSize[0], $ImageSize[1]); } return array(0, 0); }
/** * Create and display a thumbnail of an uploaded file. */ public function utilityController_mediaThumbnail_create($sender, $media_id) { // When it makes it into core, it will be available in // functions.general.php require 'generate_thumbnail.php'; $model = new Gdn_Model('Media'); $media = $model->getID($media_id, DATASET_TYPE_ARRAY); if (!$media) { throw notFoundException('File'); } // Get actual path to the file. $local_path = Gdn_Upload::copyLocal($media['Path']); if (!file_exists($local_path)) { throw notFoundException('File'); } $file_extension = pathinfo($local_path, PATHINFO_EXTENSION); // Generate new path for thumbnail $thumb_path = $this->getBaseUploadDestinationDir() . '/' . 'thumb'; // Grab full path with filename, and validate it. $thumb_destination_path = $this->getAbsoluteDestinationFilePath($local_path, $file_extension, $thumb_path); // Create thumbnail, and grab debug data from whole process. $thumb_payload = generate_thumbnail($local_path, $thumb_destination_path, array('height' => c('Plugins.FileUpload.ThumbnailHeight', 128))); if ($thumb_payload['success'] === true) { // Thumbnail dimensions $thumb_height = round($thumb_payload['result_height']); $thumb_width = round($thumb_payload['result_width']); // Move the thumbnail to its proper location. Calling SaveAs with // cloudfiles enabled will trigger the move to cloudfiles, so use // same path for each arg in SaveAs. The file will be removed from the local filesystem. $parsed = Gdn_Upload::parse($thumb_destination_path); $target = $thumb_destination_path; // $parsed['Name']; $Upload = new Gdn_Upload(); $filepath_parsed = $Upload->saveAs($thumb_destination_path, $target, array('source' => 'content')); // Save thumbnail information to DB. $model->save(array('MediaID' => $media_id, 'StorageMethod' => $filepath_parsed['Type'], 'ThumbWidth' => $thumb_width, 'ThumbHeight' => $thumb_height, 'ThumbPath' => $filepath_parsed['SaveName'])); // Remove cf scratch copy, typically in cftemp, if there was actually a file pulled in from CF. if (strpos($local_path, 'cftemp') !== false) { if (!unlink($local_path)) { // Maybe add logging for local cf copies not deleted. } } $url = $filepath_parsed['Url']; } else { // Fix the thumbnail information so this isn't requested again and again. $model->save(array('MediaID' => $media_id, 'ImageWidth' => 0, 'ImageHeight' => 0, 'ThumbPath' => '')); $url = asset('/plugins/FileUpload/images/file.png'); } redirect($url, 301); }
/** * Delete an uploaded file & its media record. * * @param int $MediaID Unique ID on Media table. */ protected function trashFile($MediaID) { $Media = $this->mediaModel()->getID($MediaID); if ($Media) { $this->mediaModel()->delete($Media); $Deleted = false; // Allow interception $this->EventArguments['Parsed'] = Gdn_Upload::parse($Media->Path); $this->EventArguments['Handled'] =& $Deleted; // Allow skipping steps below $this->fireEvent('TrashFile'); if (!$Deleted) { $DirectPath = MediaModel::pathUploads() . DS . $Media->Path; if (file_exists($DirectPath)) { $Deleted = @unlink($DirectPath); } } if (!$Deleted) { $CalcPath = FileUploadPlugin::findLocalMedia($Media, true, true); if (file_exists($CalcPath)) { @unlink($CalcPath); } } } }
/** * Saves the specified image at $Target in the specified format with the * specified dimensions (or the existing dimensions if height/width are not provided. * * @param string The path to the source image. Typically this is the tmp file name returned by $this->ValidateUpload(); * @param string The full path to where the image should be saved, including image name. * @param int An integer value indicating the maximum allowed height of the image (in pixels). * @param int An integer value indicating the maximum allowed width of the image (in pixels). * @param array Options additional options for saving the image. * - <b>Crop</b>: Image proportions will always remain constrained. The Crop parameter is a boolean value indicating if the image should be cropped when one dimension (height or width) goes beyond the constrained proportions. * - <b>OutputType</b>: The format in which the output image should be saved. Options are: jpg, png, and gif. Default is jpg. * - <b>ImageQuality</b>: An integer value representing the qualityof the saved image. Ranging from 0 (worst quality, smaller file) to 100 (best quality, biggest file). * - <b>SourceX, SourceY</b>: If you want to create a thumbnail that is a crop of the image these are the coordinates of the thumbnail. * - <b>SourceHeight. SourceWidth</b>: If you want to create a thumbnail that is a crop of the image these are it's dimensions. */ public static function saveImageAs($Source, $Target, $Height = '', $Width = '', $Options = array()) { $Crop = false; $OutputType = ''; $ImageQuality = c('Garden.UploadImage.Quality', 100); // Make function work like it used to. $Args = func_get_args(); $SaveGif = false; if (count($Args) > 5) { $Crop = val(4, $Args, $Crop); $OutputType = val(5, $Args, $OutputType); $ImageQuality = val(6, $Args, $ImageQuality); } elseif (is_bool($Options)) { $Crop = $Options; } else { $Crop = val('Crop', $Options, $Crop); $OutputType = val('OutputType', $Options, $OutputType); $ImageQuality = val('ImageQuality', $Options, $ImageQuality); $SaveGif = val('SaveGif', $Options); } // Set some boundaries for $ImageQuality if ($ImageQuality < 10) { $ImageQuality = 10; } if ($ImageQuality > 100 || !is_numeric($ImageQuality)) { $ImageQuality = 100; } // Make sure type, height & width are properly defined. if (!function_exists('gd_info')) { throw new Exception(T('The uploaded file could not be processed because GD is not installed.')); } $GdInfo = gd_info(); $Size = getimagesize($Source); list($WidthSource, $HeightSource, $Type) = $Size; $WidthSource = val('SourceWidth', $Options, $WidthSource); $HeightSource = val('SourceHeight', $Options, $HeightSource); if ($Height == '' || !is_numeric($Height)) { $Height = $HeightSource; } if ($Width == '' || !is_numeric($Width)) { $Width = $WidthSource; } if (!$OutputType) { $OutputTypes = array(1 => 'gif', 2 => 'jpeg', 3 => 'png', 17 => 'ico'); $OutputType = val($Type, $OutputTypes, 'jpg'); } elseif ($Type == 17 && $OutputType != 'ico') { // Icons cannot be converted throw new Exception(T('Upload cannot convert icons.')); } // Figure out the target path. $TargetParsed = Gdn_Upload::parse($Target); $TargetPath = PATH_UPLOADS . '/' . ltrim($TargetParsed['Name'], '/'); if (!file_exists(dirname($TargetPath))) { mkdir(dirname($TargetPath), 0777, true); } // Don't resize if the source dimensions are smaller than the target dimensions or an icon $XCoord = val('SourceX', $Options, 0); $YCoord = val('SourceY', $Options, 0); if (($HeightSource > $Height || $WidthSource > $Width) && $Type != 17) { $AspectRatio = (double) $WidthSource / $HeightSource; if ($Crop === false) { if (round($Width / $AspectRatio) > $Height) { $Width = round($Height * $AspectRatio); } else { $Height = round($Width / $AspectRatio); } } else { $HeightDiff = $HeightSource - $Height; $WidthDiff = $WidthSource - $Width; if ($WidthDiff > $HeightDiff) { // Crop the original width down $NewWidthSource = round($Width * $HeightSource / $Height); // And set the original x position to the cropped start point. if (!isset($Options['SourceX'])) { $XCoord = round(($WidthSource - $NewWidthSource) / 2); } $WidthSource = $NewWidthSource; } else { // Crop the original height down $NewHeightSource = round($Height * $WidthSource / $Width); // And set the original y position to the cropped start point. if (!isset($Options['SourceY'])) { $YCoord = 0; // crop to top because most portraits show the face at the top. } $HeightSource = $NewHeightSource; } } } else { // Neither target dimension is larger than the original, so keep the original dimensions. $Height = $HeightSource; $Width = $WidthSource; } $Process = true; if ($WidthSource <= $Width && $HeightSource <= $Height && $Type == 1 && $SaveGif) { $Process = false; } // Never process icons if ($Type == 17) { $Process = false; } if ($Process) { // Create GD image from the provided file, but first check if we have the necessary tools $SourceImage = false; switch ($Type) { case 1: if (val('GIF Read Support', $GdInfo) || val('GIF Write Support', $GdInfo)) { $SourceImage = imagecreatefromgif($Source); } break; case 2: if (val('JPG Support', $GdInfo) || val('JPEG Support', $GdInfo)) { $SourceImage = imagecreatefromjpeg($Source); } break; case 3: if (val('PNG Support', $GdInfo)) { $SourceImage = imagecreatefrompng($Source); imagealphablending($SourceImage, true); } break; } if (!$SourceImage) { throw new Exception(sprintf(T('You cannot save images of this type (%s).'), $Type)); } // Create a new image from the raw source if (function_exists('imagecreatetruecolor')) { $TargetImage = imagecreatetruecolor($Width, $Height); // Only exists if GD2 is installed } else { $TargetImage = imagecreate($Width, $Height); // Always exists if any GD is installed } if ($OutputType == 'png') { imagealphablending($TargetImage, false); imagesavealpha($TargetImage, true); } imagecopyresampled($TargetImage, $SourceImage, 0, 0, $XCoord, $YCoord, $Width, $Height, $WidthSource, $HeightSource); imagedestroy($SourceImage); // Check for EXIF rotation tag, and rotate the image if present if (function_exists('exif_read_data') && ($Type == IMAGETYPE_JPEG || $Type == IMAGETYPE_TIFF_II || $Type == IMAGETYPE_TIFF_MM)) { $ImageExif = exif_read_data($Source); if (!empty($ImageExif['Orientation'])) { switch ($ImageExif['Orientation']) { case 3: $TargetImage = imagerotate($TargetImage, 180, 0); break; case 6: $TargetImage = imagerotate($TargetImage, -90, 0); list($Width, $Height) = array($Height, $Width); break; case 8: $TargetImage = imagerotate($TargetImage, 90, 0); list($Width, $Height) = array($Height, $Width); break; } } } // No need to check these, if we get here then whichever function we need will be available if ($OutputType == 'gif') { imagegif($TargetImage, $TargetPath); } elseif ($OutputType == 'png') { imagepng($TargetImage, $TargetPath, 10 - (int) ($ImageQuality / 10)); } elseif ($OutputType == 'ico') { self::imageIco($TargetImage, $TargetPath); } else { imagejpeg($TargetImage, $TargetPath, $ImageQuality); } } else { copy($Source, $TargetPath); } // Allow a plugin to move the file to a differnt location. $Sender = new stdClass(); $Sender->EventArguments = array(); $Sender->EventArguments['Path'] = $TargetPath; $Parsed = self::parse($TargetPath); $Parsed['Width'] = $Width; $Parsed['Height'] = $Height; $Sender->EventArguments['Parsed'] =& $Parsed; $Sender->Returns = array(); Gdn::pluginManager()->callEventHandlers($Sender, 'Gdn_Upload', 'SaveAs'); return $Sender->EventArguments['Parsed']; }
/** * Determine if a URI matches the format of a valid type/domain upload. * * @param string $uri The URI to test. This would be the value saved in the database (ex. GDN_User.Photo). * @return bool Returns **true** if {@link uri} looks like an uploaded file or **false** otherwise. */ public static function isUploadUri($uri) { $parsed = Gdn_Upload::parse($uri); return !empty($parsed['Url']) && val('Type', $parsed) !== 'external'; }