public void CopyHtmlAttribute_DoesNotOverrideAttributes() { // Arrange var attributeName = "hello"; var tagHelperOutput = new TagHelperOutput( "p", attributes: new Dictionary<string, object>() { { attributeName, "world2" } }); var expectedAttribute = new KeyValuePair<string, object>(attributeName, "world2"); var tagHelperContext = new TagHelperContext( allAttributes: new Dictionary<string, object>(StringComparer.Ordinal) { { attributeName, "world" } }, items: new Dictionary<object, object>(), uniqueId: "test", getChildContentAsync: () => { var tagHelperContent = new DefaultTagHelperContent(); tagHelperContent.Append("Something Else"); return Task.FromResult<TagHelperContent>(tagHelperContent); }); // Act tagHelperOutput.CopyHtmlAttribute(attributeName, tagHelperContext); // Assert var attribute = Assert.Single(tagHelperOutput.Attributes); Assert.Equal(expectedAttribute, attribute); }
/// <inheritdoc /> /// <remarks> /// Does nothing unless <see cref="FormContext.FormData"/> contains a /// <see cref="SelectTagHelper.SelectedValuesFormDataKey"/> entry and that entry is a non-empty /// <see cref="ICollection{string}"/> instance. Also does nothing if the associated <option> is already /// selected. /// </remarks> public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { // Pass through attributes that are also well-known HTML attributes. if (Value != null) { output.CopyHtmlAttribute(nameof(Value), context); } // Nothing to do if this <option/> is already selected. if (!output.Attributes.ContainsName("selected")) { // Is this <option/> element a child of a <select/> element the SelectTagHelper targeted? object formDataEntry; ViewContext.FormContext.FormData.TryGetValue( SelectTagHelper.SelectedValuesFormDataKey, out formDataEntry); // ... And did the SelectTagHelper determine any selected values? var selectedValues = formDataEntry as ICollection<string>; if (selectedValues != null && selectedValues.Count != 0) { // Encode all selected values for comparison with element content. var encodedValues = new HashSet<string>(StringComparer.OrdinalIgnoreCase); foreach (var selectedValue in selectedValues) { encodedValues.Add(Generator.Encode(selectedValue)); } // Select this <option/> element if value attribute or content matches a selected value. Callers // encode values as-needed while executing child content. But TagHelperOutput itself // encodes attribute values later, when start tag is generated. bool selected; if (Value != null) { selected = selectedValues.Contains(Value); } else if (output.IsContentModified) { selected = encodedValues.Contains(output.Content.GetContent()); } else { var childContent = await context.GetChildContentAsync(); selected = encodedValues.Contains(childContent.GetContent()); } if (selected) { output.Attributes.Add("selected", "selected"); } } } }
/// <inheritdoc /> public override void Process(TagHelperContext context, TagHelperOutput output) { if (FileVersion) { EnsureFileVersionProvider(); output.Attributes[SrcAttributeName] = _fileVersionProvider.AddFileVersionToPath(Src); } else { // Pass through attribute that is also a well-known HTML attribute. output.CopyHtmlAttribute(SrcAttributeName, context); } }
/// <inheritdoc /> public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { // Pass through attribute that is also a well-known HTML attribute. if (Src != null) { output.CopyHtmlAttribute(SrcAttributeName, context); } 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; } // Get the highest matched mode var mode = modeResult.FullMatches.Select(match => match.Mode).Max(); // NOTE: Values in TagHelperOutput.Attributes may already be HTML-encoded. var attributes = new TagHelperAttributeList(output.Attributes); var builder = new DefaultTagHelperContent(); var originalContent = await context.GetChildContentAsync(); if (mode == Mode.Fallback && string.IsNullOrEmpty(SrcInclude) || mode == Mode.FileVersion) { // No globbing to do, just build a <script /> tag to match the original one in the source file // Or just add file version to the script tag. BuildScriptTag(originalContent, attributes, builder); } else { BuildGlobbedScriptTags(originalContent, attributes, builder); } if (mode == Mode.Fallback) { BuildFallbackBlock(attributes, builder); } // We've taken over tag rendering, so prevent rendering the outer tag output.TagName = null; output.Content.SetContent(builder); }
/// <inheritdoc /> /// <remarks>Does nothing if <see cref="For"/> is <c>null</c>.</remarks> /// <exception cref="InvalidOperationException"> /// Thrown if <see cref="Format"/> is non-<c>null</c> but <see cref="For"/> is <c>null</c>. /// </exception> public override void Process(TagHelperContext context, TagHelperOutput output) { // Pass through attributes that are also well-known HTML attributes. Must be done prior to any copying // from a TagBuilder. if (InputTypeName != null) { output.CopyHtmlAttribute("type", context); } if (Value != null) { output.CopyHtmlAttribute(nameof(Value), context); } // Note null or empty For.Name is allowed because TemplateInfo.HtmlFieldPrefix may be sufficient. // IHtmlGenerator will enforce name requirements. var metadata = For.Metadata; var modelExplorer = For.ModelExplorer; if (metadata == null) { throw new InvalidOperationException(Resources.FormatTagHelpers_NoProvidedMetadata( "<input>", ForAttributeName, nameof(IModelMetadataProvider), For.Name)); } string inputType; string inputTypeHint; if (string.IsNullOrEmpty(InputTypeName)) { // Note GetInputType never returns null. inputType = GetInputType(modelExplorer, out inputTypeHint); } else { inputType = InputTypeName.ToLowerInvariant(); inputTypeHint = null; } // inputType may be more specific than default the generator chooses below. if (!output.Attributes.ContainsName("type")) { output.Attributes["type"] = inputType; } TagBuilder tagBuilder; switch (inputType) { case "checkbox": GenerateCheckBox(modelExplorer, output); return; case "hidden": tagBuilder = Generator.GenerateHidden( ViewContext, modelExplorer, For.Name, value: For.Model, useViewData: false, htmlAttributes: null); break; case "password": tagBuilder = Generator.GeneratePassword( ViewContext, modelExplorer, For.Name, value: null, htmlAttributes: null); break; case "radio": tagBuilder = GenerateRadio(modelExplorer); break; default: tagBuilder = GenerateTextBox(modelExplorer, inputTypeHint, inputType); break; } if (tagBuilder != null) { // This TagBuilder contains the one <input/> element of interest. Since this is not the "checkbox" // special-case, output is a self-closing element no longer guaranteed. output.MergeAttributes(tagBuilder); output.Content.Append(tagBuilder.InnerHtml); } }
/// <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); // Resolve any application relative URLs (~/) now so they can be used in comparisons later. if (TryResolveUrl(Href, encodeWebRoot: false, resolvedUrl: out resolvedUrl)) { Href = resolvedUrl; } ProcessUrlAttribute(HrefAttributeName, 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[HrefAttributeName]?.Value as string; if (attributeStringValue != null) { output.Attributes[HrefAttributeName].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.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(string.Empty); } } if (mode == Mode.Fallback) { if (TryResolveUrl(FallbackHref, encodeWebRoot: false, resolvedUrl: out resolvedUrl)) { FallbackHref = resolvedUrl; } BuildFallbackBlock(builder); } output.PostElement.SetContent(builder); }
/// <inheritdoc /> public override void Process(TagHelperContext context, TagHelperOutput 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[SrcAttributeName] = _fileVersionProvider.AddFileVersionToPath(Src); } }
/// <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 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; 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 (Src != null) { output.Attributes[SrcAttributeName].Value = _fileVersionProvider.AddFileVersionToPath(Src); } } 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) { // Pass through attribute that is also a well-known HTML attribute. if (Href != null) { output.CopyHtmlAttribute(HrefAttributeName, context); } var modeResult = AttributeMatcher.DetermineMode(context, ModeDetails); var logger = Logger ?? LoggerFactory.CreateLogger<LinkTagHelper>(); modeResult.LogDetails(logger, this, context.UniqueId, ViewContext.View.Path); if (!modeResult.FullMatches.Any()) { // No attributes matched so we have nothing to do return; } // Get the highest matched mode var mode = modeResult.FullMatches.Select(match => match.Mode).Max(); // NOTE: Values in TagHelperOutput.Attributes may already be HTML-encoded. var attributes = new Dictionary<string, object>(output.Attributes); var builder = new DefaultTagHelperContent(); if (mode == Mode.Fallback && string.IsNullOrEmpty(HrefInclude) || mode == Mode.FileVersion) { // No globbing to do, just build a <link /> tag to match the original one in the source file. // Or just add file version to the link tag. BuildLinkTag(attributes, builder); } else { BuildGlobbedLinkTags(attributes, builder); } if (mode == Mode.Fallback) { BuildFallbackBlock(builder); } // We've taken over tag rendering, so prevent rendering the outer tag output.TagName = null; output.Content.SetContent(builder); }
public override void Process(TagHelperContext context, TagHelperOutput output) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (output == null) { throw new ArgumentNullException(nameof(output)); } if (Src != null) { output.CopyHtmlAttribute(SrcAttributeName, context); } ProcessUrlAttribute(SrcAttributeName, output); Src = output.Attributes[SrcAttributeName]?.Value as string; var modeResult = AttributeMatcher.DetermineMode(context, ModeDetails); modeResult.LogDetails(Logger, this, context.UniqueId, ViewContext.View.Path); if (!modeResult.FullMatches.Any()) { return; } if(string.IsNullOrWhiteSpace(SrcDist) && string.IsNullOrWhiteSpace(SrcInclude) && !HostingEnvironment.IsDevelopment()) { return; } if(string.IsNullOrEmpty(SrcInclude) && HostingEnvironment.IsDevelopment()) { return; } var attributes = new TagHelperAttributeList(output.Attributes); var builder = new DefaultTagHelperContent(); var mode = modeResult.FullMatches.Select(match => match.Mode).Max(); 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); } output.PostElement.SetContent(builder); }