/// <summary>
        /// Instantiates a new <see cref="TagHelperContext"/>.
        /// </summary>
        /// <param name="allAttributes">Every attribute associated with the current HTML element.</param>
        /// <param name="items">Collection of items used to communicate with other <see cref="ITagHelper"/>s.</param>
        /// <param name="uniqueId">The unique identifier for the source element this <see cref="TagHelperContext" />
        /// applies to.</param>
        public TagHelperContext(
            IEnumerable<IReadOnlyTagHelperAttribute> allAttributes,
            IDictionary<object, object> items,
            string uniqueId)
        {
            if (allAttributes == null)
            {
                throw new ArgumentNullException(nameof(allAttributes));
            }

            if (items == null)
            {
                throw new ArgumentNullException(nameof(items));
            }

            if (uniqueId == null)
            {
                throw new ArgumentNullException(nameof(uniqueId));
            }

            AllAttributes = new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>(
                allAttributes.Select(attribute => new TagHelperAttribute(attribute.Name, attribute.Value)));
            Items = items;
            UniqueId = uniqueId;
        }
        /// <summary>
        /// Checks all attributes on a tag-helper element if any are asp-all-route-data or asp-route-*
        /// Returns null if no route data was found.
        /// </summary>
        /// <param name="attributes"></param>
        /// <returns></returns>
        public static Dictionary <string, string> GetRouteValues(ReadOnlyTagHelperAttributeList attributes)
        {
            bool hasAllRouteData = attributes.TryGetAttribute("asp-all-route-data", out TagHelperAttribute allRouteValues);

            if (hasAllRouteData)
            {
                return(allRouteValues.Value as Dictionary <string, string>);
            }
            else
            {
                // You can write the attributes as ASP-ROUTE-  and the name will be ASP-ROUTE so need to ignore case
                IEnumerable <TagHelperAttribute> routeAttributes = attributes.Where(x => x.Name.StartsWith("asp-route-", StringComparison.OrdinalIgnoreCase));

                if (routeAttributes.Any())
                {
                    Dictionary <string, string> routeValues = new Dictionary <string, string>();

                    foreach (TagHelperAttribute attribute in routeAttributes)
                    {
                        // asp-route- is 10 characters long
                        string parameter = attribute.Name.Substring(10).ToLower();
                        routeValues.Add(parameter, attribute.Value as string);
                    }

                    return(routeValues);
                }
            }

            return(null);
        }
        public static TagHelperAttributeList ToList(this ReadOnlyTagHelperAttributeList readOnlyTagHelperAttributeList)
        {
            TagHelperAttribute[] attrs = new TagHelperAttribute[readOnlyTagHelperAttributeList.Count];

            readOnlyTagHelperAttributeList.CopyTo(attrs, 0);

            return(new TagHelperAttributeList(attrs));
        }
Exemple #4
0
        public static TagHelperAttribute GetTagHelperAttribute(this ReadOnlyTagHelperAttributeList tagHelperAttribute, string name)
        {
            if (tagHelperAttribute.TryGetAttribute(name, out var attribute))
            {
                return(attribute);
            }

            return(null);
        }
Exemple #5
0
 /// <summary>
 /// Instantiates a new <see cref="TagHelperContext"/>.
 /// </summary>
 /// <param name="allAttributes">Every attribute associated with the current HTML element.</param>
 /// <param name="items">Collection of items used to communicate with other <see cref="ITagHelper"/>s.</param>
 /// <param name="uniqueId">The unique identifier for the source element this <see cref="TagHelperContext" />
 /// applies to.</param>
 /// <param name="getChildContentAsync">A delegate used to execute and retrieve the rendered child content
 /// asynchronously.</param>
 public TagHelperContext(
     [NotNull] IEnumerable<IReadOnlyTagHelperAttribute> allAttributes,
     [NotNull] IDictionary<object, object> items,
     [NotNull] string uniqueId,
     [NotNull] Func<bool, Task<TagHelperContent>> getChildContentAsync)
 {
     AllAttributes = new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>(
         allAttributes.Select(attribute => new TagHelperAttribute(attribute.Name, attribute.Value)));
     Items = items;
     UniqueId = uniqueId;
     _getChildContentAsync = getChildContentAsync;
 }
Exemple #6
0
        private static bool HasMissingAttributes(ReadOnlyTagHelperAttributeList allAttributes, string[] requiredAttributes)
        {
            // Check for all attribute values
            // Perf: Avoid allocating enumerator
            for (var i = 0; i < requiredAttributes.Length; i++)
            {
                if (!allAttributes.TryGetAttribute(requiredAttributes[i], out var attribute))
                {
                    // Missing attribute.
                    return(true);
                }

                if (attribute.Value is string valueAsString && string.IsNullOrEmpty(valueAsString))
                {
                    // Treat attributes with empty values as missing.
                    return(true);
                }
            }

            return(false);
        }
Exemple #7
0
        public static TagHelperContext CreateTagHelperContext(this IWebPackObject webPack,
                                                              ReadOnlyTagHelperAttributeList allAttributes,
                                                              IDictionary <object, object> items,
                                                              string uniqueId)
        {
            var customAttributeList = new TagHelperAttributeList();
            var attributes          = webPack.Resolve();

            foreach (KeyValuePair <string, object> entry in attributes)
            {
                customAttributeList.Add(new TagHelperAttribute(entry.Key, entry.Value));
            }

            foreach (var item in allAttributes)
            {
                if (!customAttributeList.ContainsName(item.Name))
                {
                    customAttributeList.Add(new TagHelperAttribute(item.Name, item.Value));
                }
            }

            return(new TagHelperContext(customAttributeList, new Dictionary <object, object>(), uniqueId));
        }
Exemple #8
0
        /// <summary>
        /// Given an HTML tag (with attributes), read the resource that should be pushed to the client.
        /// </summary>
        private PushPromise ReadPushedResourceFromTag(string tagName, ReadOnlyTagHelperAttributeList attributes)
        {
            switch (tagName)
            {
            case "script":
                // create Link: </asset/to/push.js>; rel=preload; as=script
                return(new PushPromise(attributes["src"].Value.ToString(), "script"));

            case "link":
                // create Link: </asset/to/push.css>; rel=preload; as=stylesheet
                return(new PushPromise(attributes["href"].Value.ToString(), attributes["rel"].Value?.ToString()));

            case "img":
                // create Link: </asset/to/push.jpg>; rel=preload; as=image
                return(new PushPromise(attributes["src"].Value.ToString(), "image"));

            case "a":
                // create Link: </asset/to/push.html>; rel=preload; as=document
                return(new PushPromise(attributes["href"].Value.ToString(), "document"));

            default:
                throw new NotImplementedException($"{nameof(Handler.PushPromiseHandler)} does not know how to handle '{tagName}' tags");
            }
        }
Exemple #9
0
        /// <summary>
        /// Process
        /// </summary>
        /// <param name="context">Context</param>
        /// <param name="output">Output</param>
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (output == null)
            {
                throw new ArgumentNullException(nameof(output));
            }

            //clear the output
            output.SuppressOutput();

            //required asterisk
            bool.TryParse(IsRequired, out bool required);
            if (required)
            {
                output.PreElement.SetHtmlContent("<div class='input-group input-group-required'>");
                output.PostElement.SetHtmlContent("<div class=\"input-group-btn\"><span class=\"required\">*</span></div></div>");
            }

            //contextualize IHtmlHelper
            IViewContextAware viewContextAware = _htmlHelper as IViewContextAware;

            viewContextAware?.Contextualize(ViewContext);

            //get htmlAttributes object
            Dictionary <string, object>    htmlAttributes = new Dictionary <string, object>();
            ReadOnlyTagHelperAttributeList attributes     = context.AllAttributes;

            foreach (TagHelperAttribute attribute in attributes)
            {
                if (!attribute.Name.Equals(ForAttributeName) &&
                    !attribute.Name.Equals(NameAttributeName) &&
                    !attribute.Name.Equals(ItemsAttributeName) &&
                    !attribute.Name.Equals(DisabledAttributeName) &&
                    !attribute.Name.Equals(RequiredAttributeName))
                {
                    htmlAttributes.Add(attribute.Name, attribute.Value);
                }
            }

            //generate editor
            string tagName = For != null ? For.Name : Name;

            bool.TryParse(IsMultiple, out bool multiple);
            if (!string.IsNullOrEmpty(tagName))
            {
                IHtmlContent selectList;
                if (multiple)
                {
                    selectList = _htmlHelper.Editor(tagName, "MultiSelect", new { htmlAttributes, SelectList = Items });
                }
                else
                {
                    if (htmlAttributes.ContainsKey("class"))
                    {
                        htmlAttributes["class"] += " form-control";
                    }
                    else
                    {
                        htmlAttributes.Add("class", "form-control");
                    }

                    selectList = _htmlHelper.DropDownList(tagName, Items, htmlAttributes);
                }
                output.Content.SetHtmlContent(selectList.RenderHtmlContent());
            }
        }
        public async Task TagHelper_CallsGeneratorWithExpectedValues_RealModelType(
            Type modelType,
            object model,
            bool allowMultiple)
        {
            // Arrange
            var contextAttributes = new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>(
                    Enumerable.Empty<IReadOnlyTagHelperAttribute>());
            var originalAttributes = new TagHelperAttributeList();
            var propertyName = "Property1";
            var tagName = "select";

            var tagHelperContext = new TagHelperContext(
                contextAttributes,
                items: new Dictionary<object, object>(),
                uniqueId: "test",
                getChildContentAsync: useCachedResult =>
                {
                    var tagHelperContent = new DefaultTagHelperContent();
                    tagHelperContent.SetContent("Something");
                    return Task.FromResult<TagHelperContent>(tagHelperContent);
                });
            var output = new TagHelperOutput(tagName, originalAttributes);
            var metadataProvider = new EmptyModelMetadataProvider();
            var modelExplorer = metadataProvider.GetModelExplorerForType(modelType, model);
            var modelExpression = new ModelExpression(propertyName, modelExplorer);

            var htmlGenerator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
            var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator.Object, metadataProvider);
            var currentValues = new string[0];
            htmlGenerator
                .Setup(real => real.GetCurrentValues(
                    viewContext,
                    modelExplorer,
                    propertyName,   // expression
                    allowMultiple))
                .Returns(currentValues)
                .Verifiable();
            htmlGenerator
                .Setup(real => real.GenerateSelect(
                    viewContext,
                    modelExplorer,
                    null,           // optionLabel
                    propertyName,   // expression
                    It.IsAny<IEnumerable<SelectListItem>>(),
                    currentValues,
                    allowMultiple,
                    null))          // htmlAttributes
                .Returns((TagBuilder)null)
                .Verifiable();

            var tagHelper = new SelectTagHelper(htmlGenerator.Object)
            {
                For = modelExpression,
                ViewContext = viewContext,
            };

            // Act
            await tagHelper.ProcessAsync(tagHelperContext, output);

            // Assert
            htmlGenerator.Verify();

            Assert.NotNull(viewContext.FormContext?.FormData);
            var keyValuePair = Assert.Single(
                viewContext.FormContext.FormData,
                entry => entry.Key == SelectTagHelper.SelectedValuesFormDataKey);
            Assert.Same(currentValues, keyValuePair.Value);
        }
Exemple #11
0
        public async Task PopulateHtmlTagAsync(ITagBuilderOptions options, ReadOnlyTagHelperAttributeList attributeList, Asset asset, IHtmlContentBuilder output)
        {
            TagBuilder tagBuilder = null;

            var outputMode = options.Output;

            if (!this.Options.BundleStylesheets && !this.Options.MinifyStylesheets)
            {
                outputMode = OptimizationTagOutput.Tag;
            }
            else if (OptimizationTagOutput.Tag == outputMode && (this.Options.BundleStylesheets || this.Options.MinifyStylesheets))
            {
                outputMode = OptimizationTagOutput.StaticFile;
            }

            switch (outputMode)
            {
            case OptimizationTagOutput.Default:     //same as OptimizationTagOutput.Inline
                string content = await asset.ReadContentAsStringAsync();

                output.AppendHtmlLine($"<style>{content}</style>");
                break;

            case OptimizationTagOutput.Tag:
                tagBuilder = new TagBuilder("link");
                tagBuilder.TagRenderMode = TagRenderMode.SelfClosing;
                tagBuilder.Attributes.Add("rel", "stylesheet");
                tagBuilder.Attributes.Add("type", "text/css");
                tagBuilder.Attributes.Add("href", asset.GetWebLocation(this.Options));

                output.AppendHtml(tagBuilder);
                break;

            case OptimizationTagOutput.StaticFile:
            case OptimizationTagOutput.Loader:

                string     webPath          = null;
                PathString relativeFilePath = new PathString(this.Options.StaticAssetsPath).Add($"/{asset.Name}");
                var        assetFileInfo    = this.HostingEnvironment.WebRootFileProvider.GetFileInfo(relativeFilePath);

                if (!assetFileInfo.Exists)
                {
                    content = await asset.ReadContentAsStringAsync();

                    await File.WriteAllTextAsync(assetFileInfo.PhysicalPath, content, Encoding.UTF8);
                }

                if (string.IsNullOrWhiteSpace(this.Options.AssetRootUrl))
                {
                    webPath = this.HttpContextAccessor.HttpContext.Request.PathBase.Add(relativeFilePath);
                }
                else
                {
                    if (!Uri.TryCreate(this.Options.AssetRootUrl, UriKind.Absolute, out Uri cdnRootUri))
                    {
                        throw new UriFormatException($"'{this.Options.AssetRootUrl}' is an invalid Uri. An absolute Uri is expected.");
                    }
                    Uri.TryCreate(cdnRootUri, $"{asset.Name}", out Uri webPathUri);
                    webPath = webPathUri.ToString();
                }

                tagBuilder = new TagBuilder("link");
                tagBuilder.TagRenderMode = TagRenderMode.SelfClosing;
                tagBuilder.Attributes.Add("rel", "stylesheet");
                tagBuilder.Attributes.Add("type", "text/css");
                tagBuilder.Attributes.Add("href", webPath);

                output.AppendHtml(tagBuilder);
                break;
            }
        }
Exemple #12
0
        public async Task TagHelper_CallsGeneratorWithExpectedValues_RealModelType(
            Type modelType,
            object model,
            bool allowMultiple)
        {
            // Arrange
            var contextAttributes = new ReadOnlyTagHelperAttributeList <IReadOnlyTagHelperAttribute>(
                Enumerable.Empty <IReadOnlyTagHelperAttribute>());
            var originalAttributes = new TagHelperAttributeList();
            var propertyName       = "Property1";
            var tagName            = "select";

            var tagHelperContext = new TagHelperContext(
                contextAttributes,
                items: new Dictionary <object, object>(),
                uniqueId: "test");
            var output = new TagHelperOutput(
                tagName,
                originalAttributes,
                getChildContentAsync: useCachedResult =>
            {
                var tagHelperContent = new DefaultTagHelperContent();
                tagHelperContent.SetContent("Something");
                return(Task.FromResult <TagHelperContent>(tagHelperContent));
            });
            var metadataProvider = new EmptyModelMetadataProvider();
            var modelExplorer    = metadataProvider.GetModelExplorerForType(modelType, model);
            var modelExpression  = new ModelExpression(propertyName, modelExplorer);

            var htmlGenerator = new Mock <IHtmlGenerator>(MockBehavior.Strict);
            var viewContext   = TestableHtmlGenerator.GetViewContext(model, htmlGenerator.Object, metadataProvider);
            var currentValues = new string[0];

            htmlGenerator
            .Setup(real => real.GetCurrentValues(
                       viewContext,
                       modelExplorer,
                       propertyName, // expression
                       allowMultiple))
            .Returns(currentValues)
            .Verifiable();
            htmlGenerator
            .Setup(real => real.GenerateSelect(
                       viewContext,
                       modelExplorer,
                       null,         // optionLabel
                       propertyName, // expression
                       It.IsAny <IEnumerable <SelectListItem> >(),
                       currentValues,
                       allowMultiple,
                       null))       // htmlAttributes
            .Returns((TagBuilder)null)
            .Verifiable();

            var tagHelper = new SelectTagHelper(htmlGenerator.Object)
            {
                For         = modelExpression,
                ViewContext = viewContext,
            };

            // Act
            tagHelper.Init(tagHelperContext);
            await tagHelper.ProcessAsync(tagHelperContext, output);

            // Assert
            htmlGenerator.Verify();

            var keyValuePair = Assert.Single(
                tagHelperContext.Items,
                entry => (Type)entry.Key == typeof(SelectTagHelper));

            Assert.Same(currentValues, keyValuePair.Value);
        }
Exemple #13
0
        public async Task PopulateHtmlTagAsync(ITagBuilderOptions options, ReadOnlyTagHelperAttributeList attributeList, Asset asset, IHtmlContentBuilder output)
        {
            TagBuilder tagBuilder = new TagBuilder("script");

            tagBuilder.TagRenderMode = TagRenderMode.Normal;
            tagBuilder.Attributes.Add("type", "text/javascript");

            if (attributeList.ContainsName("defer"))
            {
                tagBuilder.Attributes.Add("defer", null);
            }
            else if (attributeList.ContainsName("async"))
            {
                tagBuilder.Attributes.Add("async", null);
            }

            var outputMode = options.Output;

            if (!this.Options.BundleJavascripts && !this.Options.MinifyJavascripts && OptimizationTagOutput.Loader != outputMode)
            {
                outputMode = OptimizationTagOutput.Tag;
            }
            else if (OptimizationTagOutput.Tag == outputMode && (this.Options.BundleJavascripts || this.Options.MinifyJavascripts))
            {
                outputMode = OptimizationTagOutput.StaticFile;
            }

            switch (outputMode)
            {
            case OptimizationTagOutput.Default:     //same as OptimizationTagOutput.Inline
                string content = await asset.ReadContentAsStringAsync();

                tagBuilder.InnerHtml.SetHtmlContent(content);
                break;

            case OptimizationTagOutput.Tag:
                tagBuilder.Attributes.Add("src", asset.GetWebLocation(this.Options));
                if (null != asset?.Content)
                {
                    await asset.Content.DisposeAsync();
                }
                break;

            case OptimizationTagOutput.StaticFile:
            case OptimizationTagOutput.Loader:

                string     webPath          = null;
                PathString relativeFilePath = new PathString(this.Options.StaticAssetsPath).Add($"/{asset.Name}");
                var        assetFileInfo    = this.HostingEnvironment.WebRootFileProvider.GetFileInfo(relativeFilePath);
                if (!assetFileInfo.Exists)
                {
                    content = await asset.ReadContentAsStringAsync();

                    await File.WriteAllTextAsync(assetFileInfo.PhysicalPath, content, Encoding.UTF8);
                }


                if (string.IsNullOrWhiteSpace(this.Options.AssetRootUrl))
                {
                    webPath = this.HttpContextAccessor.HttpContext.Request.PathBase.Add(relativeFilePath);
                }
                else
                {
                    if (!Uri.TryCreate(this.Options.AssetRootUrl, UriKind.Absolute, out Uri cdnRootUri))
                    {
                        throw new UriFormatException($"'{this.Options.AssetRootUrl}' is an invalid Uri. An absolute Uri is expected.");
                    }
                    Uri.TryCreate(cdnRootUri, $"{asset.Name}", out Uri webPathUri);
                    webPath = webPathUri.ToString();
                }

                if (OptimizationTagOutput.StaticFile == outputMode)
                {
                    tagBuilder.Attributes.Add("src", webPath);
                }
                else
                {
                    tagBuilder.InnerHtml.SetHtmlContent($"adriva.optimization.loader.push('{webPath}', 1)");
                }

                if (null != asset?.Content)
                {
                    await asset.Content.DisposeAsync();
                }
                break;
            }

            output.AppendHtml(tagBuilder);
        }