/// <inheritdoc /> public override void Process(TagHelperContext context, TagHelperOutput output) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (output == null) { throw new ArgumentNullException(nameof(output)); } output.CopyHtmlAttribute(SrcAttributeName, context); ProcessUrlAttribute(SrcAttributeName, output); if (AppendVersion) { EnsureFileVersionProvider(); // Retrieve the TagHelperOutput variation of the "src" attribute in case other TagHelpers in the // pipeline have touched the value. If the value is already encoded this ImageTagHelper may // not function properly. Src = output.Attributes[SrcAttributeName].Value as string; output.Attributes.SetAttribute(SrcAttributeName, _fileVersionProvider.AddFileVersionToPath(Src)); } }
public static string AddFileVersionToPath(this IRazorPage page, string path) { var context = page.ViewContext.HttpContext; IMemoryCache cache = context.RequestServices.GetRequiredService <IMemoryCache>(); var hostingEnvironment = context.RequestServices.GetRequiredService <IHostingEnvironment>(); var versionProvider = new FileVersionProvider(hostingEnvironment.WebRootFileProvider, cache, context.Request.Path); return(versionProvider.AddFileVersionToPath(path)); }
/// <summary> /// Appends a version hash querystring parameter to the end /// of the file path, e.g. 'myfile.js?v=examplecomputedhash' /// </summary> /// <param name="applicationRelativePath"> /// The static resource file path, which must be the full application /// relative path. /// </param> /// <returns> /// If the file is found, the path is returned with the version /// appended, otherwise the unmodified path is returned. /// </returns> public string AppendVersion(string applicationRelativePath) { // Only support site relative urls, so no need to pass requestPathBase; var versionProvider = new FileVersionProvider(_staticResourceFileProvider, _memoryCache, null); var path = versionProvider.AddFileVersionToPath(applicationRelativePath); return(path); }
private string GetVersionedSrc(string srcValue) { if (AppendVersion == true) { srcValue = FileVersionProvider.AddFileVersionToPath(ViewContext.HttpContext.Request.PathBase, srcValue); } return(srcValue); }
/// <summary> /// Adds version query string to the file /// </summary> /// <param name="path">The relative path. Should not start with "~/"</param> /// <returns></returns> private string AddFileVersionToPath(string path) { var context = this.HttpContext; var pathBase = context.Request.PathBase; var versionProvider = new FileVersionProvider(_env.WebRootFileProvider, _cache, pathBase); //var globingUrlBuilder = new GlobbingUrlBuilder(_environment.WebRootFileProvider, _memCache, pathBase); //var globingUrl = globingUrlBuilder.BuildUrlList(null, path, null); return(versionProvider.AddFileVersionToPath(path)); }
private string GetVersionedSrc(string srcValue) { EnsureFileVersionProvider(); if (_options.AppendVersion) { srcValue = _fileVersionProvider.AddFileVersionToPath(srcValue); } return(srcValue); }
private void BuildFallbackBlock(TagHelperContent builder) { EnsureGlobbingUrlBuilder(); var fallbackHrefs = GlobbingUrlBuilder.BuildUrlList(FallbackHref, FallbackHrefInclude, FallbackHrefExclude).ToArray(); if (fallbackHrefs.Length > 0) { if (FileVersion == true) { for (var i = 0; i < fallbackHrefs.Length; i++) { // fallbackHrefs come from bound attributes and globbing. Must always be non-null. Debug.Assert(fallbackHrefs[i] != null); fallbackHrefs[i] = _fileVersionProvider.AddFileVersionToPath(fallbackHrefs[i]); } } builder.Append(Environment.NewLine); // Build the <meta /> tag that's used to test for the presence of the stylesheet builder.Append(string.Format( CultureInfo.InvariantCulture, "<meta name=\"x-stylesheet-fallback-test\" class=\"{0}\" />", HtmlEncoder.HtmlEncode(FallbackTestClass))); // Build the <script /> tag that checks the effective style of <meta /> tag above and renders the extra // <link /> tag to load the fallback stylesheet if the test CSS property value is found to be false, // indicating that the primary stylesheet failed to load. builder .Append("<script>") .Append(string.Format( CultureInfo.InvariantCulture, JavaScriptResources.GetEmbeddedJavaScript(FallbackJavaScriptResourceName), JavaScriptEncoder.JavaScriptStringEncode(FallbackTestProperty), JavaScriptEncoder.JavaScriptStringEncode(FallbackTestValue), JavaScriptStringArrayEncoder.Encode(JavaScriptEncoder, fallbackHrefs))) .Append("</script>"); } }
/// <summary> /// Generates a has of the file. /// </summary> protected string AddFileVersionToPath(string fileName, IAsset asset) { if (_fileProvider == null) { _fileProvider = new FileVersionProvider( asset.GetFileProvider(HostingEnvironment), Cache, ViewContext.HttpContext.Request.PathBase); } return(_fileProvider.AddFileVersionToPath(fileName)); }
/// <inheritdoc /> public override void Process(TagHelperContext context, TagHelperOutput output) { if (AppendVersion) { EnsureFileVersionProvider(); output.Attributes[SrcAttributeName] = _fileVersionProvider.AddFileVersionToPath(Src); } else { // Pass through attribute that is also a well-known HTML attribute. output.CopyHtmlAttribute(SrcAttributeName, context); } }
private void AppendVersionedHref(string hrefName, string hrefValue, TagHelperContent builder) { if (AppendVersion == true) { hrefValue = FileVersionProvider.AddFileVersionToPath(ViewContext.HttpContext.Request.PathBase, hrefValue); } builder .AppendHtml(hrefName) .AppendHtml("=\"") .Append(hrefValue) .AppendHtml("\" "); }
public override void Process(TagHelperContext context, TagHelperOutput output) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (output == null) { throw new ArgumentNullException(nameof(output)); } output.CopyHtmlAttribute(SrcAttributeName, context); ProcessUrlAttribute(SrcAttributeName, output); if (AppendVersion) { EnsureFileVersionProvider(); Src = output.Attributes[SrcAttributeName].Value as string; // Check if the CDN Base URI is set and add to the src attribute if (!string.IsNullOrWhiteSpace(CdnUri)) { output.Attributes.SetAttribute(SrcAttributeName, string.Format("{0}{1}", CdnUri, _fileVersionProvider.AddFileVersionToPath(Src))); } } //Retrieve any existing onerror handler code var onError = output.Attributes[OnErrorAttributeName]?.Value as string; //Check if there's a fallback source and no onerror handler if (!string.IsNullOrWhiteSpace(FallbackSrc) && string.IsNullOrWhiteSpace(onError)) { string resolvedUrl; if (TryResolveUrl(FallbackSrc, out resolvedUrl)) { FallbackSrc = resolvedUrl; } if (AppendVersion) { FallbackSrc = _fileVersionProvider.AddFileVersionToPath(FallbackSrc); } //Apply fallback handler code onError = $"this.src='{FallbackSrc}';console.log('{Src} NOT FOUND.')"; output.Attributes.SetAttribute(OnErrorAttributeName, onError); } }
/// <inheritdoc /> public override void Process(TagHelperContext context, TagHelperOutput output) { if (AppendVersion) { EnsureFileVersionProvider(); string resolvedUrl; if (TryResolveUrl(Src, encodeWebRoot: false, resolvedUrl: out resolvedUrl)) { Src = resolvedUrl; } output.Attributes[SrcAttributeName] = _fileVersionProvider.AddFileVersionToPath(Src); } else { // Pass through attribute that is also a well-known HTML attribute. output.CopyHtmlAttribute(SrcAttributeName, context); ProcessUrlAttribute(SrcAttributeName, output); } }
public override void Process(TagHelperContext context, TagHelperOutput output) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (output == null) { throw new ArgumentNullException(nameof(output)); } output.CopyHtmlAttribute(SrcAttributeName, context); ProcessUrlAttribute(SrcAttributeName, output); var bareUrl = Src.Substring(0, Src.LastIndexOf('.')); if (AppendVersion) { EnsureFileVersionProvider(); } foreach (var format in SourceFormats) { var formatUrl = $"{bareUrl}.{format}"; var finalUrl = AppendVersion ? FileVersionProvider.AddFileVersionToPath(ViewContext.HttpContext.Request.PathBase, formatUrl) : formatUrl; output.Content.AppendHtml($@"<source type=""image/{format}"" srcset=""{finalUrl}"" />"); } var url = AppendVersion ? FileVersionProvider.AddFileVersionToPath(ViewContext.HttpContext.Request.PathBase, Src) : Src; output.Content.AppendHtml(!Eager ? $@"<img src=""{url}"" alt=""{Alt}"" width=""{Width}"" height=""{Height}"">" : $@"<img src=""{url}"" alt=""{Alt}"" width=""{Width}"" height=""{Height}"" loading=""lazy"">"); output.Attributes.Clear(); }
public override void Process(TagHelperContext context, TagHelperOutput output) { if (AppendIntegrity == true) { EnsureFileVersionProvider(); // Pass through attribute that is also a well-known HTML attribute. if (Href != null) { output.CopyHtmlAttribute(HrefAttributeName, context); } // process ProcessUrlAttribute(HrefAttributeName, output); // get the tag helper version of the Href Href = output.Attributes[HrefAttributeName]?.Value as string; if (Href != null) { // get the pre-versioned path or generate it ourselves var versionedPath = AppendVersion == true ? Href : _fileVersionProvider.AddFileVersionToPath(Href); int queryIndex; if ((queryIndex = versionedPath.IndexOf(QueryFragment)) > -1) { var query = QueryHelpers.ParseQuery(versionedPath.Substring(queryIndex)); if (query != null && query.ContainsKey(VersionKey)) { var version = query[VersionKey]; output.Attributes.Add(IntegrityAttributeName, $"{IntegrityHashName}-{version}"); output.Attributes.Add(CrossOriginAttributeName, CrossOriginAnonymousName); } } } } }
private void AppendFallbackHrefs(TagHelperContent builder, IReadOnlyList <string> fallbackHrefs) { builder.AppendHtml("["); var firstAdded = false; // Perf: Avoid allocating enumerator and read interface .Count once rather than per iteration var fallbackHrefsCount = fallbackHrefs.Count; for (var i = 0; i < fallbackHrefsCount; i++) { if (firstAdded) { builder.AppendHtml(",\""); } else { builder.AppendHtml("\""); firstAdded = true; } // fallbackHrefs come from bound attributes (a C# context) and globbing. Must always be non-null. Debug.Assert(fallbackHrefs[i] != null); var valueToWrite = fallbackHrefs[i]; if (AppendVersion == true) { valueToWrite = FileVersionProvider.AddFileVersionToPath(ViewContext.HttpContext.Request.PathBase, fallbackHrefs[i]); } // Must HTML-encode the href attribute value to ensure the written <link/> element is valid. Must also // JavaScript-encode that value to ensure the doc.write() statement is valid. valueToWrite = HtmlEncoder.Encode(valueToWrite); valueToWrite = JavaScriptEncoder.Encode(valueToWrite); builder.AppendHtml(valueToWrite); builder.AppendHtml("\""); } builder.AppendHtml("]"); }
/// <inheritdoc /> public override void Process(TagHelperContext context, TagHelperOutput output) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (output == null) { throw new ArgumentNullException(nameof(output)); } // Pass through attribute that is also a well-known HTML attribute. if (Src != null) { output.CopyHtmlAttribute(SrcAttributeName, context); } // If there's no "src" attribute in output.Attributes this will noop. ProcessUrlAttribute(SrcAttributeName, output); // Retrieve the TagHelperOutput variation of the "src" attribute in case other TagHelpers in the // pipeline have touched the value. If the value is already encoded this ScriptTagHelper may // not function properly. Src = output.Attributes[SrcAttributeName]?.Value as string; if (!AttributeMatcher.TryDetermineMode(context, ModeDetails, Compare, out var mode)) { // No attributes matched so we have nothing to do return; } if (AppendVersion == true) { EnsureFileVersionProvider(); if (Src != null) { var index = output.Attributes.IndexOfName(SrcAttributeName); var existingAttribute = output.Attributes[index]; output.Attributes[index] = new TagHelperAttribute( existingAttribute.Name, FileVersionProvider.AddFileVersionToPath(ViewContext.HttpContext.Request.PathBase, Src), existingAttribute.ValueStyle); } } var builder = output.PostElement; builder.Clear(); if (mode == Mode.GlobbedSrc || mode == Mode.Fallback && !string.IsNullOrEmpty(SrcInclude)) { BuildGlobbedScriptTags(output.Attributes, builder); if (string.IsNullOrEmpty(Src)) { // Only SrcInclude is specified. Don't render the original tag. output.TagName = null; output.Content.SetContent(string.Empty); } } if (mode == Mode.Fallback) { if (TryResolveUrl(FallbackSrc, resolvedUrl: out string resolvedUrl)) { FallbackSrc = resolvedUrl; } BuildFallbackBlock(output.Attributes, builder); } }
/// <inheritdoc /> public override void Process(TagHelperContext context, TagHelperOutput output) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (output == null) { throw new ArgumentNullException(nameof(output)); } // Pass through attribute that is also a well-known HTML attribute. if (Href != null) { output.CopyHtmlAttribute(HrefAttributeName, context); } // If there's no "href" attribute in output.Attributes this will noop. ProcessUrlAttribute(HrefAttributeName, output); // Retrieve the TagHelperOutput variation of the "href" attribute in case other TagHelpers in the // pipeline have touched the value. If the value is already encoded this LinkTagHelper may // not function properly. Href = output.Attributes[HrefAttributeName]?.Value as string; Mode mode; if (!AttributeMatcher.TryDetermineMode(context, ModeDetails, Compare, out mode)) { // No attributes matched so we have nothing to do return; } // NOTE: Values in TagHelperOutput.Attributes may already be HTML-encoded. var attributes = new TagHelperAttributeList(output.Attributes); if (AppendVersion == true) { EnsureFileVersionProvider(); if (Href != null) { output.Attributes[HrefAttributeName].Value = _fileVersionProvider.AddFileVersionToPath(Href); } } var builder = new DefaultTagHelperContent(); if (mode == Mode.GlobbedHref || mode == Mode.Fallback && !string.IsNullOrEmpty(HrefInclude)) { BuildGlobbedLinkTags(attributes, builder); if (string.IsNullOrEmpty(Href)) { // Only HrefInclude is specified. Don't render the original tag. output.TagName = null; output.Content.SetContent(HtmlString.Empty); } } if (mode == Mode.Fallback) { string resolvedUrl; if (TryResolveUrl(FallbackHref, resolvedUrl: out resolvedUrl)) { FallbackHref = resolvedUrl; } BuildFallbackBlock(builder); } output.PostElement.SetContent(builder); }
/// <summary> /// Processes the tag helper. /// </summary> /// <param name="context">The <see cref="TagHelperContext"/> instance.</param> /// <param name="output">The <see cref="TagHelperOutput"/> instance.</param> public override void Process(TagHelperContext context, TagHelperOutput output) { // Don't load from CDN in development if (_env.IsDevelopment()) { return; } var cdnUri = (CdnUri.EndsWith("/")) ? CdnUri.TrimEnd('/') : CdnUri; if (String.IsNullOrWhiteSpace(cdnUri)) { return; } if (!pathAttribues.ContainsKey(output.TagName)) { return; } var pathAttributeName = pathAttribues[output.TagName]; if (!output.Attributes.ContainsName(pathAttributeName)) { return; } ProcessUrlAttribute(pathAttributeName, output); var path = output.Attributes[pathAttributeName].Value?.ToString(); if (String.IsNullOrWhiteSpace(path)) { return; } var index = path.IndexOf('?'); if (index > 0) { path = path.Substring(index); } // Don't change path if it's absolute Uri uri; if (Uri.TryCreate(path, UriKind.Absolute, out uri)) { return; } if (AppendVersion) { EnsureFileVersionProvider(); path = _fileVersionProvider.AddFileVersionToPath(path); } if (path.StartsWith("~/")) { path = path.Substring(1); } if (!path.StartsWith("/")) { path = "/" + path; } var cdnPath = cdnUri + path; output.Attributes.SetAttribute(pathAttributeName, cdnPath); }
private void BuildFallbackBlock(TagHelperAttributeList attributes, DefaultTagHelperContent builder) { EnsureGlobbingUrlBuilder(); EnsureFileVersionProvider(); var fallbackSrcs = GlobbingUrlBuilder.BuildUrlList(FallbackSrc, FallbackSrcInclude, FallbackSrcExclude); if (fallbackSrcs.Any()) { // Build the <script> tag that checks the test method and if it fails, renders the extra script. builder.Append(Environment.NewLine) .Append("<script>(") .Append(FallbackTestExpression) .Append("||document.write(\""); // May have no "src" attribute in the dictionary e.g. if Src and SrcInclude were not bound. if (!attributes.ContainsName(SrcAttributeName)) { // Need this entry to place each fallback source. attributes.Add(new TagHelperAttribute(SrcAttributeName, value: null)); } foreach (var src in fallbackSrcs) { // Fallback "src" values come from bound attributes and globbing. Must always be non-null. Debug.Assert(src != null); builder.Append("<script"); foreach (var attribute in attributes) { if (!attribute.Name.Equals(SrcAttributeName, StringComparison.OrdinalIgnoreCase)) { var encodedKey = JavaScriptEncoder.JavaScriptStringEncode(attribute.Name); var attributeValue = attribute.Value.ToString(); var encodedValue = JavaScriptEncoder.JavaScriptStringEncode(attributeValue); AppendAttribute(builder, encodedKey, encodedValue, escapeQuotes: true); } else { // Ignore attribute.Value; use src instead. var attributeValue = src; if (AppendVersion == true) { attributeValue = _fileVersionProvider.AddFileVersionToPath(attributeValue); } // attribute.Key ("src") does not need to be JavaScript-encoded. var encodedValue = JavaScriptEncoder.JavaScriptStringEncode(attributeValue); AppendAttribute(builder, attribute.Name, encodedValue, escapeQuotes: true); } } builder.Append("><\\/script>"); } builder.Append("\"));</script>"); } }
/// <inheritdoc /> public override void Process(TagHelperContext context, TagHelperOutput output) { string resolvedUrl; // Pass through attribute that is also a well-known HTML attribute. if (Src != null) { output.CopyHtmlAttribute(SrcAttributeName, context); if (TryResolveUrl(Src, encodeWebRoot: false, resolvedUrl: out resolvedUrl)) { Src = resolvedUrl; } ProcessUrlAttribute(SrcAttributeName, output); } var modeResult = AttributeMatcher.DetermineMode(context, ModeDetails); modeResult.LogDetails(Logger, this, context.UniqueId, ViewContext.View.Path); if (!modeResult.FullMatches.Any()) { // No attributes matched so we have nothing to do return; } // NOTE: Values in TagHelperOutput.Attributes may already be HTML-encoded. var attributes = new TagHelperAttributeList(output.Attributes); if (AppendVersion == true) { EnsureFileVersionProvider(); var attributeStringValue = output.Attributes[SrcAttributeName]?.Value as string; if (attributeStringValue != null) { output.Attributes[SrcAttributeName].Value = _fileVersionProvider.AddFileVersionToPath(attributeStringValue); } } var builder = new DefaultTagHelperContent(); // Get the highest matched mode var mode = modeResult.FullMatches.Select(match => match.Mode).Max(); if (mode == Mode.GlobbedSrc || mode == Mode.Fallback && !string.IsNullOrEmpty(SrcInclude)) { BuildGlobbedScriptTags(attributes, builder); if (string.IsNullOrEmpty(Src)) { // Only SrcInclude is specified. Don't render the original tag. output.TagName = null; output.Content.SetContent(string.Empty); } } if (mode == Mode.Fallback) { if (TryResolveUrl(FallbackSrc, encodeWebRoot: false, resolvedUrl: out resolvedUrl)) { FallbackSrc = resolvedUrl; } BuildFallbackBlock(attributes, builder); } output.PostElement.SetContent(builder); }
/// <inheritdoc /> public override void Process(TagHelperContext context, TagHelperOutput output) { string resolvedUrl; // Pass through attribute that is also a well-known HTML attribute. if (Href != null) { output.CopyHtmlAttribute(HrefAttributeName, context); } // If there's no "href" attribute in output.Attributes this will noop. ProcessUrlAttribute(HrefAttributeName, output); // Retrieve the TagHelperOutput variation of the "href" attribute in case other TagHelpers in the // pipeline have touched the value. If the value is already encoded this LinkTagHelper may // not function properly. Href = output.Attributes[HrefAttributeName]?.Value as string; var modeResult = AttributeMatcher.DetermineMode(context, ModeDetails); modeResult.LogDetails(Logger, this, context.UniqueId, ViewContext.View.Path); if (!modeResult.FullMatches.Any()) { // No attributes matched so we have nothing to do return; } // NOTE: Values in TagHelperOutput.Attributes may already be HTML-encoded. var attributes = new TagHelperAttributeList(output.Attributes); if (AppendVersion == true) { EnsureFileVersionProvider(); if (Href != null) { output.Attributes[HrefAttributeName].Value = _fileVersionProvider.AddFileVersionToPath(Href); } } var builder = new DefaultTagHelperContent(); // Get the highest matched mode var mode = modeResult.FullMatches.Select(match => match.Mode).Max(); if (mode == Mode.GlobbedHref || mode == Mode.Fallback && !string.IsNullOrEmpty(HrefInclude)) { BuildGlobbedLinkTags(attributes, builder); if (string.IsNullOrEmpty(Href)) { // Only HrefInclude is specified. Don't render the original tag. output.TagName = null; output.Content.SetContent(HtmlString.Empty); } } if (mode == Mode.Fallback) { if (TryResolveUrl(FallbackHref, encodeWebRoot: false, resolvedUrl: out resolvedUrl)) { FallbackHref = resolvedUrl; } BuildFallbackBlock(builder); } output.PostElement.SetContent(builder); }