/// <inheritdoc /> /// <remarks> /// Does nothing if user provides an <c>action</c> attribute and <see cref="Antiforgery"/> is <c>null</c> or /// <c>false</c>. /// </remarks> /// <exception cref="InvalidOperationException"> /// Thrown if <c>action</c> attribute is provided and <see cref="Action"/> or <see cref="Controller"/> are /// non-<c>null</c> or if the user provided <c>asp-route-*</c> attributes. /// </exception> 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 (Method != null) { output.CopyHtmlAttribute(nameof(Method), context); } var antiforgeryDefault = true; // If "action" is already set, it means the user is attempting to use a normal <form>. if (output.Attributes.ContainsName(HtmlActionAttributeName)) { if (Action != null || Controller != null || Area != null || Route != null || RouteValues.Count != 0) { // User also specified bound attributes we cannot use. throw new InvalidOperationException( Resources.FormatFormTagHelper_CannotOverrideAction( "<form>", HtmlActionAttributeName, ActionAttributeName, ControllerAttributeName, AreaAttributeName, RouteAttributeName, RouteValuesPrefix)); } // User is using the FormTagHelper like a normal <form> tag. Antiforgery default should be false to // not force the antiforgery token on the user. antiforgeryDefault = false; } else { IDictionary<string, object> routeValues = null; if (_routeValues != null && _routeValues.Count > 0) { // Convert from Dictionary<string, string> to Dictionary<string, object>. routeValues = new Dictionary<string, object>(_routeValues.Count, StringComparer.OrdinalIgnoreCase); foreach (var routeValue in _routeValues) { routeValues.Add(routeValue.Key, routeValue.Value); } } if (Area != null) { if (routeValues == null) { routeValues = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); } // Unconditionally replace any value from asp-route-area. routeValues["area"] = Area; } TagBuilder tagBuilder; if (Route == null) { tagBuilder = Generator.GenerateForm( ViewContext, Action, Controller, routeValues, method: null, htmlAttributes: null); } else if (Action != null || Controller != null) { // Route and Action or Controller were specified. Can't determine the action attribute. throw new InvalidOperationException( Resources.FormatFormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified( "<form>", RouteAttributeName, ActionAttributeName, ControllerAttributeName, HtmlActionAttributeName)); } else { tagBuilder = Generator.GenerateRouteForm( ViewContext, Route, routeValues, method: null, htmlAttributes: null); } if (tagBuilder != null) { output.MergeAttributes(tagBuilder); output.PostContent.AppendHtml(tagBuilder.InnerHtml); } if (string.Equals(Method, FormMethod.Get.ToString(), StringComparison.OrdinalIgnoreCase)) { antiforgeryDefault = false; } } if (Antiforgery ?? antiforgeryDefault) { var antiforgeryTag = Generator.GenerateAntiforgery(ViewContext); if (antiforgeryTag != null) { output.PostContent.AppendHtml(antiforgeryTag); } } }
/// <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)); } }
/// <inheritdoc /> /// <remarks> /// Does nothing unless <see cref="TagHelperContext.Items"/> contains a /// <see cref="SelectTagHelper"/> <see cref="Type"/> entry and that entry is a non-<c>null</c> /// <see cref="CurrentValues"/> instance. Also does nothing if the associated <option> is already /// selected. /// </remarks> public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (output == null) { throw new ArgumentNullException(nameof(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; context.Items.TryGetValue(typeof(SelectTagHelper), out formDataEntry); // ... And did the SelectTagHelper determine any selected values? var currentValues = formDataEntry as CurrentValues; if (currentValues?.Values != null && currentValues.Values.Count != 0) { // Select this <option/> element if value attribute or (if no value attribute) body matches a // selected value. Body is encoded as-needed while executing child content. But TagHelperOutput // itself encodes attribute values later, when start tag is generated. bool selected; if (Value != null) { selected = currentValues.Values.Contains(Value); } else { if (currentValues.ValuesAndEncodedValues == null) { // Include encoded versions of all selected values when comparing with body. var allValues = new HashSet<string>(currentValues.Values, StringComparer.OrdinalIgnoreCase); foreach (var selectedValue in currentValues.Values) { allValues.Add(Generator.Encode(selectedValue)); } currentValues.ValuesAndEncodedValues = allValues; } TagHelperContent childContent; if (output.IsContentModified) { // Another tag helper has modified the body. Use what they wrote. childContent = output.Content; } else { childContent = await output.GetChildContentAsync(); } selected = currentValues.ValuesAndEncodedValues.Contains(childContent.GetContent()); } if (selected) { output.Attributes.Add("selected", "selected"); } } } }
/// <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; } if (AppendVersion == true) { EnsureFileVersionProvider(); if (Href != null) { var index = output.Attributes.IndexOfName(HrefAttributeName); output.Attributes[index] = new TagHelperAttribute( HrefAttributeName, _fileVersionProvider.AddFileVersionToPath(Href)); } } var builder = new DefaultTagHelperContent(); if (mode == Mode.GlobbedHref || mode == Mode.Fallback && !string.IsNullOrEmpty(HrefInclude)) { BuildGlobbedLinkTags(output.Attributes, builder); if (string.IsNullOrEmpty(Href)) { // Only HrefInclude is specified. Don't render the original tag. output.TagName = null; output.Content.SetHtmlContent(HtmlString.Empty); } } if (mode == Mode.Fallback) { string resolvedUrl; if (TryResolveUrl(FallbackHref, resolvedUrl: out resolvedUrl)) { FallbackHref = resolvedUrl; } BuildFallbackBlock(builder); } output.PostElement.SetHtmlContent(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) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (output == null) { throw new ArgumentNullException(nameof(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.SetAttribute("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.AppendHtml(tagBuilder.InnerHtml); } }