using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using Umbraco.Core; using Umbraco.Core.Exceptions; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Web.Composing; using Umbraco.Web.Models; using Umbraco.Web.Mvc; namespace Umbraco.Web { /// /// Extension methods for UrlHelper for use in templates /// public static class UrlHelperRenderExtensions { #region GetCropUrl /// /// Gets the ImageProcessor Url of a media item by the crop alias (using default media item property alias of "umbracoFile") /// /// /// /// The IPublishedContent item. /// /// /// The crop alias e.g. thumbnail /// /// /// Whether to HTML encode this URL - default is true - w3c standards require HTML attributes to be HTML encoded but this can be /// set to false if using the result of this method for CSS. /// /// public static IHtmlString GetCropUrl(this UrlHelper urlHelper, IPublishedContent mediaItem, string cropAlias, bool htmlEncode = true) { var url = mediaItem.GetCropUrl(cropAlias: cropAlias, useCropDimensions: true); return htmlEncode ? new HtmlString(HttpUtility.HtmlEncode(url)) : new HtmlString(url); } /// /// Gets the ImageProcessor Url by the crop alias using the specified property containing the image cropper Json data on the IPublishedContent item. /// /// /// /// The IPublishedContent item. /// /// /// The property alias of the property containing the Json data e.g. umbracoFile /// /// /// The crop alias e.g. thumbnail /// /// /// Whether to HTML encode this URL - default is true - w3c standards require HTML attributes to be HTML encoded but this can be /// set to false if using the result of this method for CSS. /// /// /// The ImageProcessor.Web Url. /// public static IHtmlString GetCropUrl(this UrlHelper urlHelper, IPublishedContent mediaItem, string propertyAlias, string cropAlias, bool htmlEncode = true) { var url = mediaItem.GetCropUrl(propertyAlias: propertyAlias, cropAlias: cropAlias, useCropDimensions: true); return htmlEncode ? new HtmlString(HttpUtility.HtmlEncode(url)) : new HtmlString(url); } /// /// Gets the ImageProcessor Url from the image path. /// /// /// The IPublishedContent item. /// /// /// The width of the output image. /// /// /// The height of the output image. /// /// /// Property alias of the property containing the Json data. /// /// /// The crop alias. /// /// /// Quality percentage of the output image. /// /// /// The image crop mode. /// /// /// The image crop anchor. /// /// /// Use focal point to generate an output image using the focal point instead of the predefined crop if there is one /// /// /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters /// /// /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated /// /// /// These are any query string parameters (formatted as query strings) that ImageProcessor supports. For example: /// /// /// /// /// /// Use a dimension as a ratio /// /// /// If the image should be upscaled to requested dimensions /// /// /// /// Whether to HTML encode this URL - default is true - w3c standards require HTML attributes to be HTML encoded but this can be /// set to false if using the result of this method for CSS. /// /// /// The . /// public static IHtmlString GetCropUrl(this UrlHelper urlHelper, IPublishedContent mediaItem, int? width = null, int? height = null, string propertyAlias = Umbraco.Core.Constants.Conventions.Media.File, string cropAlias = null, int? quality = null, ImageCropMode? imageCropMode = null, ImageCropAnchor? imageCropAnchor = null, bool preferFocalPoint = false, bool useCropDimensions = false, bool cacheBuster = true, string furtherOptions = null, ImageCropRatioMode? ratioMode = null, bool upScale = true, bool htmlEncode = true) { var url = mediaItem.GetCropUrl(width, height, propertyAlias, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions, ratioMode, upScale); return htmlEncode ? new HtmlString(HttpUtility.HtmlEncode(url)) : new HtmlString(url); } /// /// Gets the ImageProcessor Url from the image path. /// /// /// The image url. /// /// /// The width of the output image. /// /// /// The height of the output image. /// /// /// The Json data from the Umbraco Core Image Cropper property editor /// /// /// The crop alias. /// /// /// Quality percentage of the output image. /// /// /// The image crop mode. /// /// /// The image crop anchor. /// /// /// Use focal point to generate an output image using the focal point instead of the predefined crop if there is one /// /// /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters /// /// /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated /// /// /// These are any query string parameters (formatted as query strings) that ImageProcessor supports. For example: /// /// /// /// /// /// Use a dimension as a ratio /// /// /// If the image should be upscaled to requested dimensions /// /// /// /// Whether to HTML encode this URL - default is true - w3c standards require HTML attributes to be HTML encoded but this can be /// set to false if using the result of this method for CSS. /// /// /// The . /// public static IHtmlString GetCropUrl(this UrlHelper urlHelper, string imageUrl, int? width = null, int? height = null, string imageCropperValue = 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, bool htmlEncode = true) { var url = imageUrl.GetCropUrl(width, height, imageCropperValue, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions, ratioMode, upScale); return htmlEncode ? new HtmlString(HttpUtility.HtmlEncode(url)) : new HtmlString(url); } public static IHtmlString GetCropUrl(this UrlHelper urlHelper, ImageCropperValue imageCropperValue, 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, bool htmlEncode = true) { var imageUrl = imageCropperValue.Src; var url = imageUrl.GetCropUrl(imageCropperValue, width, height, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions, ratioMode, upScale); return htmlEncode ? new HtmlString(HttpUtility.HtmlEncode(url)) : new HtmlString(url); } #endregion /// /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController /// /// /// /// /// public static string SurfaceAction(this UrlHelper url, string action, string controllerName) { return url.SurfaceAction(action, controllerName, null); } /// /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController /// /// /// /// /// /// public static string SurfaceAction(this UrlHelper url, string action, string controllerName, object additionalRouteVals) { return url.SurfaceAction(action, controllerName, "", additionalRouteVals); } /// /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController /// /// /// /// /// /// /// public static string SurfaceAction(this UrlHelper url, string action, string controllerName, string area, object additionalRouteVals) { if (string.IsNullOrEmpty(action)) throw new ArgumentNullOrEmptyException(nameof(action)); if (string.IsNullOrEmpty(controllerName)) throw new ArgumentNullOrEmptyException(nameof(controllerName)); var encryptedRoute = CreateEncryptedRouteString(controllerName, action, area, additionalRouteVals); var result = Current.UmbracoContext.OriginalRequestUrl.AbsolutePath.EnsureEndsWith('?') + "ufprt=" + encryptedRoute; return result; } /// /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController /// /// /// /// /// public static string SurfaceAction(this UrlHelper url, string action, Type surfaceType) { return url.SurfaceAction(action, surfaceType, null); } /// /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController /// /// /// /// /// /// public static string SurfaceAction(this UrlHelper url, string action, Type surfaceType, object additionalRouteVals) { if (string.IsNullOrEmpty(action)) throw new ArgumentNullOrEmptyException(nameof(action)); if (surfaceType == null) throw new ArgumentNullException(nameof(surfaceType)); var area = ""; var surfaceController = Current.SurfaceControllerTypes.SingleOrDefault(x => x == surfaceType); if (surfaceController == null) throw new InvalidOperationException("Could not find the surface controller of type " + surfaceType.FullName); var metaData = PluginController.GetMetadata(surfaceController); if (metaData.AreaName.IsNullOrWhiteSpace() == false) { //set the area to the plugin area area = metaData.AreaName; } var encryptedRoute = CreateEncryptedRouteString(metaData.ControllerName, action, area, additionalRouteVals); var result = Current.UmbracoContext.OriginalRequestUrl.AbsolutePath.EnsureEndsWith('?') + "ufprt=" + encryptedRoute; return result; } /// /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController /// /// /// /// /// public static string SurfaceAction(this UrlHelper url, string action) where T : SurfaceController { return url.SurfaceAction(action, typeof (T)); } /// /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController /// /// /// /// /// /// public static string SurfaceAction(this UrlHelper url, string action, object additionalRouteVals) where T : SurfaceController { return url.SurfaceAction(action, typeof (T), additionalRouteVals); } /// /// Generates a Absolute Media Item URL based on the current context /// /// /// /// public static string GetAbsoluteMediaUrl(this UrlHelper urlHelper, IPublishedContent mediaItem) { if (urlHelper == null) throw new ArgumentNullException("urlHelper"); if (mediaItem == null) throw new ArgumentNullException("mediaItem"); if (urlHelper.RequestContext.HttpContext.Request.Url != null) { var requestUrl = urlHelper.RequestContext.HttpContext.Request.Url.GetLeftPart(UriPartial.Authority); return string.Format("{0}{1}", requestUrl, mediaItem.Url); } return null; } /// /// This is used in methods like BeginUmbracoForm and SurfaceAction to generate an encrypted string which gets submitted in a request for which /// Umbraco can decrypt during the routing process in order to delegate the request to a specific MVC Controller. /// /// /// /// /// /// internal static string CreateEncryptedRouteString(string controllerName, string controllerAction, string area, object additionalRouteVals = null) { if (string.IsNullOrEmpty(controllerName)) throw new ArgumentNullOrEmptyException(nameof(controllerName)); if (string.IsNullOrEmpty(controllerAction)) throw new ArgumentNullOrEmptyException(nameof(controllerAction)); if (area == null) throw new ArgumentNullException(nameof(area)); //need to create a params string as Base64 to put into our hidden field to use during the routes var surfaceRouteParams = $"c={HttpUtility.UrlEncode(controllerName)}&a={HttpUtility.UrlEncode(controllerAction)}&ar={area}"; //checking if the additional route values is already a dictionary and convert to querystring string additionalRouteValsAsQuery; if (additionalRouteVals != null) { if (additionalRouteVals is Dictionary additionalRouteValsAsDictionary) additionalRouteValsAsQuery = additionalRouteValsAsDictionary.ToQueryString(); else additionalRouteValsAsQuery = additionalRouteVals.ToDictionary().ToQueryString(); } else additionalRouteValsAsQuery = null; if (additionalRouteValsAsQuery.IsNullOrWhiteSpace() == false) surfaceRouteParams += "&" + additionalRouteValsAsQuery; return surfaceRouteParams.EncryptWithMachineKey(); } } }