1+ // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
2+ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+ using System ;
5+ using System . Linq ;
6+ using Microsoft . AspNet . Mvc . Rendering ;
7+ using Microsoft . AspNet . Mvc . TagHelpers . Internal ;
8+ using Microsoft . AspNet . Razor . Runtime . TagHelpers ;
9+
10+ namespace Microsoft . AspNet . Mvc . TagHelpers
11+ {
12+ /// <summary>
13+ /// <see cref="ITagHelper"/> implementation targeting <a> elements.
14+ /// </summary>
15+ [ TagName ( "a" ) ]
16+ public class AnchorTagHelper : TagHelper
17+ {
18+ private const string RouteAttributePrefix = "route-" ;
19+
20+ [ Activate ]
21+ private ViewContext ViewContext { get ; set ; }
22+
23+ [ Activate ]
24+ private IHtmlGenerator Generator { get ; set ; }
25+
26+ /// <summary>
27+ /// The name of the action method.
28+ /// </summary>
29+ /// <remarks>Cannot be provided if <see cref="Route"/> is specified.</remarks>
30+ public string Action { get ; set ; }
31+
32+ /// <summary>
33+ /// The name of the controller.
34+ /// </summary>
35+ /// <remarks>Cannot be provided if <see cref="Route"/> is specified.</remarks>
36+ public string Controller { get ; set ; }
37+
38+ /// <summary>
39+ /// The protocol for the URL, such as "http" or "https".
40+ /// </summary>
41+ public string Protocol { get ; set ; }
42+
43+ /// <summary>
44+ /// The host name for the URL.
45+ /// </summary>
46+ public string Host { get ; set ; }
47+
48+ /// <summary>
49+ /// The URL fragment name (the anchor name).
50+ /// </summary>
51+ public string Fragment { get ; set ; }
52+
53+ /// <summary>
54+ /// Name of the route.
55+ /// </summary>
56+ /// <remarks>Cannot be provided if <see cref="Action"/> or <see cref="Controller"/> is specified.</remarks>
57+ public string Route { get ; set ; }
58+
59+ /// <inheritdoc />
60+ /// <remarks>Does nothing if user provides a "href" attribute. Cannot specify a "href" attribute AND
61+ /// <see cref="Action"/>, <see cref="Controller"/> or <see cref="Route"/>.</remarks>
62+ public override void Process ( TagHelperContext context , TagHelperOutput output )
63+ {
64+ // If there's an "href" on the tag it means it's being used as a normal anchor.
65+ if ( output . Attributes . ContainsKey ( "href" ) )
66+ {
67+ if ( Action != null || Controller != null || Route != null )
68+ {
69+ // User specified an href AND a Action, Controller or Route; can't determine the href attribute.
70+ throw new InvalidOperationException (
71+ Resources . FormatAnchorTagHelper_CannotDetermineHrefOneSpecified (
72+ nameof ( AnchorTagHelper ) ,
73+ nameof ( Action ) ,
74+ nameof ( Controller ) ,
75+ nameof ( Route ) ) ) ;
76+ }
77+
78+ // User is using the AnchorTagHelper as normal anchor tag, restore any provided attributes.
79+ RestoreBoundHtmlAttributes ( context , output ) ;
80+ }
81+ else
82+ {
83+ TagBuilder tagBuilder ;
84+
85+ var prefixedValues = TagHelperOutputHelper . PullPrefixedAttributes ( RouteAttributePrefix , output ) ;
86+
87+ // Generator.GenerateActionLink || GenerateRouteLink does not accept a Dictionary<string, string> for
88+ // route values.
89+ var routeValues = prefixedValues . ToDictionary (
90+ attribute => attribute . Key . Substring ( RouteAttributePrefix . Length ) ,
91+ attribute => ( object ) attribute . Value ) ;
92+
93+ if ( Route == null )
94+ {
95+ tagBuilder = Generator . GenerateActionLink ( linkText : string . Empty ,
96+ actionName : Action ,
97+ controllerName : Controller ,
98+ protocol : Protocol ,
99+ hostname : Host ,
100+ fragment : Fragment ,
101+ routeValues : routeValues ,
102+ htmlAttributes : null ) ;
103+ }
104+ else if ( Action != null || Controller != null )
105+ {
106+ // Route and Action or Controller were specified. Can't determine the href attribute.
107+ throw new InvalidOperationException (
108+ Resources . FormatAnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified (
109+ nameof ( AnchorTagHelper ) ,
110+ nameof ( Route ) ,
111+ nameof ( Action ) ,
112+ nameof ( Controller ) ) ) ;
113+ }
114+ else
115+ {
116+ tagBuilder = Generator . GenerateRouteLink ( linkText : string . Empty ,
117+ routeName : Route ,
118+ protocol : Protocol ,
119+ hostName : Host ,
120+ fragment : Fragment ,
121+ routeValues : routeValues ,
122+ htmlAttributes : null ) ;
123+ }
124+
125+ TagHelperOutputHelper . MergeAttributes ( tagBuilder , output ) ;
126+ }
127+ }
128+
129+ private void RestoreBoundHtmlAttributes ( TagHelperContext context , TagHelperOutput output )
130+ {
131+ if ( Action != null )
132+ {
133+ TagHelperOutputHelper . RestoreBoundHtmlAttribute ( nameof ( Action ) , context , output ) ;
134+ }
135+
136+ if ( Controller != null )
137+ {
138+ TagHelperOutputHelper . RestoreBoundHtmlAttribute ( nameof ( Controller ) , context , output ) ;
139+ }
140+
141+ if ( Protocol != null )
142+ {
143+ TagHelperOutputHelper . RestoreBoundHtmlAttribute ( nameof ( Protocol ) , context , output ) ;
144+ }
145+
146+ if ( Host != null )
147+ {
148+ TagHelperOutputHelper . RestoreBoundHtmlAttribute ( nameof ( Host ) , context , output ) ;
149+ }
150+
151+ if ( Fragment != null )
152+ {
153+ TagHelperOutputHelper . RestoreBoundHtmlAttribute ( nameof ( Fragment ) , context , output ) ;
154+ }
155+
156+ if ( Route != null )
157+ {
158+ TagHelperOutputHelper . RestoreBoundHtmlAttribute ( nameof ( Route ) , context , output ) ;
159+ }
160+ }
161+ }
162+ }
0 commit comments