/// <summary> /// Gets a resized image for the image at the given path /// </summary> /// <param name="imagePath"></param> /// <param name="width"></param> /// <returns></returns> /// <remarks> /// If there is no media, image property or image file is found then this will return not found. /// </remarks> public IActionResult GetResized(string imagePath, int width) { var ext = Path.GetExtension(imagePath); // we need to check if it is an image by extension if (_imageUrlGenerator.IsSupportedImageFormat(ext) == false) { return(NotFound()); } //redirect to ImageProcessor thumbnail with rnd generated from last modified time of original media file DateTimeOffset?imageLastModified = null; try { imageLastModified = _mediaFileManager.FileSystem.GetLastModified(imagePath); } catch (Exception) { // if we get an exception here it's probably because the image path being requested is an image that doesn't exist // in the local media file system. This can happen if someone is storing an absolute path to an image online, which // is perfectly legal but in that case the media file system isn't going to resolve it. // so ignore and we won't set a last modified date. } var rnd = imageLastModified.HasValue ? $"&rnd={imageLastModified:yyyyMMddHHmmss}" : null; var imageUrl = _imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(imagePath) { Width = width, ImageCropMode = ImageCropMode.Max, CacheBusterValue = rnd }); return(new RedirectResult(imageUrl, false)); }
/// <summary> /// Gets a resized image for the image at the given path /// </summary> /// <param name="imagePath"></param> /// <param name="width"></param> /// <returns></returns> /// <remarks> /// If there is no media, image property or image file is found then this will return not found. /// </remarks> public IActionResult GetResized(string imagePath, int width) { // We have to use HttpUtility to encode the path here, for non-ASCII characters // We cannot use the WebUtility, as we only want to encode the path, and not the entire string var encodedImagePath = HttpUtility.UrlPathEncode(imagePath); var ext = Path.GetExtension(encodedImagePath); // check if imagePath is local to prevent open redirect if (!Uri.IsWellFormedUriString(encodedImagePath, UriKind.Relative)) { return(Unauthorized()); } // we need to check if it is an image by extension if (_imageUrlGenerator.IsSupportedImageFormat(ext) == false) { return(NotFound()); } // redirect to ImageProcessor thumbnail with rnd generated from last modified time of original media file DateTimeOffset?imageLastModified = null; try { imageLastModified = _mediaFileManager.FileSystem.GetLastModified(imagePath); } catch (Exception) { // if we get an exception here it's probably because the image path being requested is an image that doesn't exist // in the local media file system. This can happen if someone is storing an absolute path to an image online, which // is perfectly legal but in that case the media file system isn't going to resolve it. // so ignore and we won't set a last modified date. } var rnd = imageLastModified.HasValue ? $"&rnd={imageLastModified:yyyyMMddHHmmss}" : null; var imageUrl = _imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(encodedImagePath) { Width = width, ImageCropMode = ImageCropMode.Max, CacheBusterValue = rnd }); if (Url.IsLocalUrl(imageUrl)) { return(new LocalRedirectResult(imageUrl, false)); } else { return(Unauthorized()); } }
/// <summary> /// Gets the value image url for a specific width and height. /// </summary> public string GetCropUrl(int width, int height, IImageUrlGenerator imageUrlGenerator, bool useFocalPoint = false, string cacheBusterValue = null) { var options = GetCropBaseOptions(string.Empty, null, true, useFocalPoint); options.Width = width; options.Height = height; options.CacheBusterValue = cacheBusterValue; return(imageUrlGenerator.GetImageUrl(options)); }
/// <summary> /// Gets the value image URL for a specific width and height. /// </summary> public string GetCropUrl(int width, int height, IImageUrlGenerator imageUrlGenerator, string cacheBusterValue = null) { var options = GetCropBaseOptions(null, null, false); options.Width = width; options.Height = height; options.CacheBusterValue = cacheBusterValue; return(imageUrlGenerator.GetImageUrl(options)); }
/// <summary> /// Gets the value image url for a specified crop. /// </summary> public string GetCropUrl(string alias, IImageUrlGenerator imageUrlGenerator, bool useCropDimensions = true, bool useFocalPoint = false, string cacheBusterValue = null) { var crop = GetCrop(alias); // could not find a crop with the specified, non-empty, alias if (crop == null && !string.IsNullOrWhiteSpace(alias)) { return(null); } var options = GetCropBaseOptions(string.Empty, crop, string.IsNullOrWhiteSpace(alias), useFocalPoint); if (crop != null && useCropDimensions) { options.Width = crop.Width; options.Height = crop.Height; } options.CacheBusterValue = cacheBusterValue; return(imageUrlGenerator.GetImageUrl(options)); }
/// <summary> /// Gets a resized image for the image at the given path /// </summary> /// <param name="imagePath"></param> /// <param name="width"></param> /// <returns></returns> /// <remarks> /// If there is no media, image property or image file is found then this will return not found. /// </remarks> public HttpResponseMessage GetResized(string imagePath, int width) { var ext = Path.GetExtension(imagePath); // we need to check if it is an image by extension if (_contentSection.IsImageFile(ext) == false) { return(Request.CreateResponse(HttpStatusCode.NotFound)); } //redirect to ImageProcessor thumbnail with rnd generated from last modified time of original media file var response = Request.CreateResponse(HttpStatusCode.Found); DateTimeOffset?imageLastModified = null; try { imageLastModified = _mediaFileSystem.GetLastModified(imagePath); } catch (Exception) { // if we get an exception here it's probably because the image path being requested is an image that doesn't exist // in the local media file system. This can happen if someone is storing an absolute path to an image online, which // is perfectly legal but in that case the media file system isn't going to resolve it. // so ignore and we won't set a last modified date. } var rnd = imageLastModified.HasValue ? $"&rnd={imageLastModified:yyyyMMddHHmmss}" : null; var imageUrl = _imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(imagePath) { UpScale = false, Width = width, AnimationProcessMode = "first", ImageCropMode = "max", CacheBusterValue = rnd }); response.Headers.Location = new Uri(imageUrl, UriKind.RelativeOrAbsolute); return(response); }
/// <summary> /// Gets the ImageProcessor Url from the image path. /// </summary> /// <param name="imageUrl"> /// The image url. /// </param> /// <param name="imageUrlGenerator"> /// The generator that will process all the options and the image URL to return a full image urls with all processing options appended /// </param> /// <param name="cropDataSet"></param> /// <param name="width"> /// The width of the output image. /// </param> /// <param name="height"> /// The height of the output image. /// </param> /// <param name="cropAlias"> /// The crop alias. /// </param> /// <param name="quality"> /// Quality percentage of the output image. /// </param> /// <param name="imageCropMode"> /// The image crop mode. /// </param> /// <param name="imageCropAnchor"> /// The image crop anchor. /// </param> /// <param name="preferFocalPoint"> /// Use focal point to generate an output image using the focal point instead of the predefined crop if there is one /// </param> /// <param name="useCropDimensions"> /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters /// </param> /// <param name="cacheBusterValue"> /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated /// </param> /// <param name="furtherOptions"> /// These are any query string parameters (formatted as query strings) that ImageProcessor supports. For example: /// <example> /// <![CDATA[ /// furtherOptions: "&bgcolor=fff" /// ]]> /// </example> /// </param> /// <param name="ratioMode"> /// Use a dimension as a ratio /// </param> /// <param name="upScale"> /// If the image should be upscaled to requested dimensions /// </param> /// <returns> /// The <see cref="string"/>. /// </returns> public static string GetCropUrl( this string imageUrl, IImageUrlGenerator imageUrlGenerator, ImageCropperValue cropDataSet, int?width = null, int?height = null, string cropAlias = null, int?quality = null, ImageCropMode?imageCropMode = null, ImageCropAnchor?imageCropAnchor = null, bool preferFocalPoint = false, bool useCropDimensions = false, string cacheBusterValue = null, string furtherOptions = null, ImageCropRatioMode?ratioMode = null, bool upScale = true, string animationProcessMode = null) { if (string.IsNullOrEmpty(imageUrl)) { return(string.Empty); } ImageUrlGenerationOptions options; if (cropDataSet != null && (imageCropMode == ImageCropMode.Crop || imageCropMode == null)) { var crop = cropDataSet.GetCrop(cropAlias); // if a crop was specified, but not found, return null if (crop == null && !string.IsNullOrWhiteSpace(cropAlias)) { return(null); } options = cropDataSet.GetCropBaseOptions(imageUrl, crop, string.IsNullOrWhiteSpace(cropAlias), preferFocalPoint); if (crop != null & useCropDimensions) { width = crop.Width; height = crop.Height; } // If a predefined crop has been specified & there are no coordinates & no ratio mode, but a width parameter has been passed we can get the crop ratio for the height if (crop != null && string.IsNullOrEmpty(cropAlias) == false && crop.Coordinates == null && ratioMode == null && width != null && height == null) { options.HeightRatio = (decimal)crop.Height / crop.Width; } // If a predefined crop has been specified & there are no coordinates & no ratio mode, but a height parameter has been passed we can get the crop ratio for the width if (crop != null && string.IsNullOrEmpty(cropAlias) == false && crop.Coordinates == null && ratioMode == null && width == null && height != null) { options.WidthRatio = (decimal)crop.Width / crop.Height; } } else { options = new ImageUrlGenerationOptions(imageUrl) { ImageCropMode = (imageCropMode ?? ImageCropMode.Pad).ToString().ToLowerInvariant(), ImageCropAnchor = imageCropAnchor?.ToString().ToLowerInvariant() }; } options.Quality = quality; options.Width = ratioMode != null && ratioMode.Value == ImageCropRatioMode.Width ? null : width; options.Height = ratioMode != null && ratioMode.Value == ImageCropRatioMode.Height ? null : height; options.AnimationProcessMode = animationProcessMode; if (ratioMode == ImageCropRatioMode.Width && height != null) { // if only height specified then assume a square if (width == null) { width = height; } options.WidthRatio = (decimal)width / (decimal)height; } if (ratioMode == ImageCropRatioMode.Height && width != null) { // if only width specified then assume a square if (height == null) { height = width; } options.HeightRatio = (decimal)height / (decimal)width; } options.UpScale = upScale; options.FurtherOptions = furtherOptions; options.CacheBusterValue = cacheBusterValue; return(imageUrlGenerator.GetImageUrl(options)); }
/// <summary> /// Used by the RTE (and grid RTE) for drag/drop/persisting images /// </summary> /// <param name="html"></param> /// <param name="mediaParentFolder"></param> /// <param name="userId"></param> /// <returns></returns> internal string FindAndPersistPastedTempImages(string html, Guid mediaParentFolder, int userId, IImageUrlGenerator imageUrlGenerator) { // Find all img's that has data-tmpimg attribute // Use HTML Agility Pack - https://html-agility-pack.net var htmlDoc = new HtmlDocument(); htmlDoc.LoadHtml(html); var tmpImages = htmlDoc.DocumentNode.SelectNodes($"//img[@{TemporaryImageDataAttribute}]"); if (tmpImages == null || tmpImages.Count == 0) { return(html); } // An array to contain a list of URLs that // we have already processed to avoid dupes var uploadedImages = new Dictionary <string, GuidUdi>(); foreach (var img in tmpImages) { // The data attribute contains the path to the tmp img to persist as a media item var tmpImgPath = img.GetAttributeValue(TemporaryImageDataAttribute, string.Empty); if (string.IsNullOrEmpty(tmpImgPath)) { continue; } var absoluteTempImagePath = IOHelper.MapPath(tmpImgPath); var fileName = Path.GetFileName(absoluteTempImagePath); var safeFileName = fileName.ToSafeFileName(); var mediaItemName = safeFileName.ToFriendlyName(); IMedia mediaFile; GuidUdi udi; if (uploadedImages.ContainsKey(tmpImgPath) == false) { if (mediaParentFolder == Guid.Empty) { mediaFile = _mediaService.CreateMedia(mediaItemName, Constants.System.Root, Constants.Conventions.MediaTypes.Image, userId); } else { mediaFile = _mediaService.CreateMedia(mediaItemName, mediaParentFolder, Constants.Conventions.MediaTypes.Image, userId); } var fileInfo = new FileInfo(absoluteTempImagePath); var fileStream = fileInfo.OpenReadWithRetry(); if (fileStream == null) { throw new InvalidOperationException("Could not acquire file stream"); } using (fileStream) { mediaFile.SetValue(_contentTypeBaseServiceProvider, Constants.Conventions.Media.File, safeFileName, fileStream); } _mediaService.Save(mediaFile, userId); udi = mediaFile.GetUdi(); } else { // Already been uploaded & we have it's UDI udi = uploadedImages[tmpImgPath]; } // Add the UDI to the img element as new data attribute img.SetAttributeValue("data-udi", udi.ToString()); // Get the new persisted image URL var mediaTyped = _umbracoContextAccessor?.UmbracoContext?.Media.GetById(udi.Guid); if (mediaTyped == null) { throw new PanicException($"Could not find media by id {udi.Guid} or there was no UmbracoContext available."); } var location = mediaTyped.Url(); // Find the width & height attributes as we need to set the imageprocessor QueryString var width = img.GetAttributeValue("width", int.MinValue); var height = img.GetAttributeValue("height", int.MinValue); if (width != int.MinValue && height != int.MinValue) { location = imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(location) { ImageCropMode = "max", Width = width, Height = height }); } img.SetAttributeValue("src", location); // Remove the data attribute (so we do not re-process this) img.Attributes.Remove(TemporaryImageDataAttribute); // Add to the dictionary to avoid dupes if (uploadedImages.ContainsKey(tmpImgPath) == false) { uploadedImages.Add(tmpImgPath, udi); // Delete folder & image now its saved in media // The folder should contain one image - as a unique guid folder created // for each image uploaded from TinyMceController var folderName = Path.GetDirectoryName(absoluteTempImagePath); try { Directory.Delete(folderName, true); } catch (Exception ex) { _logger.Error <string>(typeof(HtmlImageSourceParser), ex, "Could not delete temp file or folder {FileName}", absoluteTempImagePath); } } } return(htmlDoc.DocumentNode.OuterHtml); }
/// <summary> /// Gets the underlying image processing service URL from the image path. /// </summary> /// <param name="imageUrl">The image URL.</param> /// <param name="imageUrlGenerator"> /// The generator that will process all the options and the image URL to return a full /// image URLs with all processing options appended. /// </param> /// <param name="cropDataSet">The crop data set.</param> /// <param name="width">The width of the output image.</param> /// <param name="height">The height of the output image.</param> /// <param name="cropAlias">The crop alias.</param> /// <param name="quality">Quality percentage of the output image.</param> /// <param name="imageCropMode">The image crop mode.</param> /// <param name="imageCropAnchor">The image crop anchor.</param> /// <param name="preferFocalPoint"> /// Use focal point to generate an output image using the focal point instead of the /// predefined crop if there is one. /// </param> /// <param name="useCropDimensions"> /// Use crop dimensions to have the output image sized according to the predefined crop /// sizes, this will override the width and height parameters. /// </param> /// <param name="cacheBusterValue"> /// Add a serialized date of the last edit of the item to ensure client cache refresh when /// updated. /// </param> /// <param name="furtherOptions"> /// These are any query string parameters (formatted as query strings) that the underlying image processing service /// supports. For example: /// <example><![CDATA[ /// furtherOptions: "bgcolor=fff" /// ]]></example> /// </param> /// <returns> /// The URL of the cropped image. /// </returns> public static string?GetCropUrl( this string imageUrl, IImageUrlGenerator imageUrlGenerator, ImageCropperValue?cropDataSet, int?width = null, int?height = null, string?cropAlias = null, int?quality = null, ImageCropMode?imageCropMode = null, ImageCropAnchor?imageCropAnchor = null, bool preferFocalPoint = false, bool useCropDimensions = false, string?cacheBusterValue = null, string?furtherOptions = null) { if (string.IsNullOrWhiteSpace(imageUrl)) { return(null); } ImageUrlGenerationOptions options; if (cropDataSet != null && (imageCropMode == ImageCropMode.Crop || imageCropMode == null)) { ImageCropperValue.ImageCropperCrop?crop = cropDataSet.GetCrop(cropAlias); // If a crop was specified, but not found, return null if (crop == null && !string.IsNullOrWhiteSpace(cropAlias)) { return(null); } options = cropDataSet.GetCropBaseOptions(imageUrl, crop, preferFocalPoint || string.IsNullOrWhiteSpace(cropAlias)); if (crop != null && useCropDimensions) { width = crop.Width; height = crop.Height; } // Calculate missing dimension if a predefined crop has been specified, but has no coordinates if (crop != null && string.IsNullOrEmpty(cropAlias) == false && crop.Coordinates == null) { if (width != null && height == null) { height = (int)MathF.Round(width.Value * ((float)crop.Height / crop.Width)); } else if (width == null && height != null) { width = (int)MathF.Round(height.Value * ((float)crop.Width / crop.Height)); } } } else { options = new ImageUrlGenerationOptions(imageUrl) { ImageCropMode = imageCropMode ?? ImageCropMode.Pad, // Not sure why we default to Pad ImageCropAnchor = imageCropAnchor, }; } options.Quality = quality; options.Width = width; options.Height = height; options.FurtherOptions = furtherOptions; options.CacheBusterValue = cacheBusterValue; return(imageUrlGenerator.GetImageUrl(options)); }
/// <summary> /// Tries to lookup the user's Gravatar to see if the endpoint can be reached, if so it returns the valid URL /// </summary> /// <param name="user"></param> /// <param name="cache"></param> /// <param name="mediaFileManager"></param> /// <param name="imageUrlGenerator"></param> /// <returns> /// A list of 5 different sized avatar URLs /// </returns> public static string[] GetUserAvatarUrls(this IUser user, IAppCache cache, MediaFileManager mediaFileManager, IImageUrlGenerator imageUrlGenerator) { // If FIPS is required, never check the Gravatar service as it only supports MD5 hashing. // Unfortunately, if the FIPS setting is enabled on Windows, using MD5 will throw an exception // and the website will not run. // Also, check if the user has explicitly removed all avatars including a Gravatar, this will be possible and the value will be "none" if (user.Avatar == "none" || CryptoConfig.AllowOnlyFipsAlgorithms) { return(new string[0]); } if (user.Avatar.IsNullOrWhiteSpace()) { var gravatarHash = user.Email?.GenerateHash <MD5>(); var gravatarUrl = "https://www.gravatar.com/avatar/" + gravatarHash + "?d=404"; // try Gravatar var gravatarAccess = cache.GetCacheItem("UserAvatar" + user.Id, () => { // Test if we can reach this URL, will fail when there's network or firewall errors var request = (HttpWebRequest)WebRequest.Create(gravatarUrl); // Require response within 10 seconds request.Timeout = 10000; try { using ((HttpWebResponse)request.GetResponse()) { } } catch (Exception) { // There was an HTTP or other error, return an null instead return(false); } return(true); }); if (gravatarAccess) { return(new[] { gravatarUrl + "&s=30", gravatarUrl + "&s=60", gravatarUrl + "&s=90", gravatarUrl + "&s=150", gravatarUrl + "&s=300", }); } return(new string[0]); } // use the custom avatar var avatarUrl = mediaFileManager.FileSystem.GetUrl(user.Avatar); return(new[] { imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(avatarUrl) { ImageCropMode = ImageCropMode.Crop, Width = 30, Height = 30, }), imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(avatarUrl) { ImageCropMode = ImageCropMode.Crop, Width = 60, Height = 60, }), imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(avatarUrl) { ImageCropMode = ImageCropMode.Crop, Width = 90, Height = 90, }), imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(avatarUrl) { ImageCropMode = ImageCropMode.Crop, Width = 150, Height = 150, }), imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(avatarUrl) { ImageCropMode = ImageCropMode.Crop, Width = 300, Height = 300, }), }.WhereNotNull().ToArray()); }