From df5a6d30d6d791ad1d3d857dd1a890a5511033bb Mon Sep 17 00:00:00 2001 From: Pranav K Date: Wed, 22 Jan 2014 09:36:53 -0800 Subject: [PATCH] * Migrating HtmlHelper stub * Updating project json files to new format --- samples/MvcSample/project.json | 15 +- src/Microsoft.AspNet.Mvc.Forms/CryptoUtil.cs | 54 +++ .../Html/FormMethod.cs | 9 + .../Html/HtmlHelper.cs | 342 ++++++++++++++++++ .../Html/HtmlHelperOfT.cs | 23 ++ .../Html/HtmlString.cs | 18 + .../Html/InputType.cs | 17 + .../Html/TagBuilder.cs | 208 +++++++++++ .../Html/TagRenderMode.cs | 14 + .../Microsoft.AspNet.Mvc.Forms.csproj | 79 ++++ .../Properties/AssemblyInfo.cs | 36 ++ src/Microsoft.AspNet.Mvc.Forms/TypeHelpers.cs | 172 +++++++++ .../packages.config | 5 + .../Microsoft.AspNet.Mvc.Razor.csproj | 1 + src/Microsoft.AspNet.Mvc.Razor/project.json | 31 +- src/Microsoft.AspNet.Mvc/project.json | 27 +- 16 files changed, 1014 insertions(+), 37 deletions(-) create mode 100644 src/Microsoft.AspNet.Mvc.Forms/CryptoUtil.cs create mode 100644 src/Microsoft.AspNet.Mvc.Forms/Html/FormMethod.cs create mode 100644 src/Microsoft.AspNet.Mvc.Forms/Html/HtmlHelper.cs create mode 100644 src/Microsoft.AspNet.Mvc.Forms/Html/HtmlHelperOfT.cs create mode 100644 src/Microsoft.AspNet.Mvc.Forms/Html/HtmlString.cs create mode 100644 src/Microsoft.AspNet.Mvc.Forms/Html/InputType.cs create mode 100644 src/Microsoft.AspNet.Mvc.Forms/Html/TagBuilder.cs create mode 100644 src/Microsoft.AspNet.Mvc.Forms/Html/TagRenderMode.cs create mode 100644 src/Microsoft.AspNet.Mvc.Forms/Microsoft.AspNet.Mvc.Forms.csproj create mode 100644 src/Microsoft.AspNet.Mvc.Forms/Properties/AssemblyInfo.cs create mode 100644 src/Microsoft.AspNet.Mvc.Forms/TypeHelpers.cs create mode 100644 src/Microsoft.AspNet.Mvc.Forms/packages.config diff --git a/samples/MvcSample/project.json b/samples/MvcSample/project.json index 4e7b50bf0f..b259c2f0ee 100644 --- a/samples/MvcSample/project.json +++ b/samples/MvcSample/project.json @@ -1,14 +1,15 @@ { "dependencies": { - "Owin": { "version": "1.0" }, - "Microsoft.Owin.Diagnostics": { "version": "2.0.2" }, - "Microsoft.Owin": { "version": "2.0.2" }, - "Microsoft.AspNet.Mvc": { "version": "" }, + "Owin": "1.0", + "Microsoft.Owin.Diagnostics": "2.0.2", + "Microsoft.Owin": "2.0.2", + "Microsoft.AspNet.Mvc": "", }, "configurations": { "net45": { - "dependencies": { - "System.Net.Http" : { } - } + "dependencies": { + "System.Net.Http" : "" + } } + } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Forms/CryptoUtil.cs b/src/Microsoft.AspNet.Mvc.Forms/CryptoUtil.cs new file mode 100644 index 0000000000..134af5003c --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Forms/CryptoUtil.cs @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Security.Cryptography; + +namespace Microsoft.AspNet.Mvc +{ + internal static class CryptoUtil + { + // This method is specially written to take the same amount of time + // regardless of where 'a' and 'b' differ. Please do not optimize it. + public static bool AreByteArraysEqual(byte[] a, byte[] b) + { + if (a == null || b == null || a.Length != b.Length) + { + return false; + } + + bool areEqual = true; + for (int i = 0; i < a.Length; i++) + { + areEqual &= (a[i] == b[i]); + } + return areEqual; + } + + // Computes a SHA256 hash over all of the input parameters. + // Each parameter is UTF8 encoded and preceded by a 7-bit encoded + // integer describing the encoded byte length of the string. + [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "MemoryStream is resilient to double-Dispose")] + public static byte[] ComputeSHA256(IList parameters) + { + using (MemoryStream ms = new MemoryStream()) + { + using (BinaryWriter bw = new BinaryWriter(ms)) + { + foreach (string parameter in parameters) + { + bw.Write(parameter); // also writes the length as a prefix; unambiguous + } + bw.Flush(); + + using (SHA256Cng sha256 = new SHA256Cng()) + { + byte[] retVal = sha256.ComputeHash(ms.GetBuffer(), 0, checked((int)ms.Length)); + return retVal; + } + } + } + } + } +} diff --git a/src/Microsoft.AspNet.Mvc.Forms/Html/FormMethod.cs b/src/Microsoft.AspNet.Mvc.Forms/Html/FormMethod.cs new file mode 100644 index 0000000000..8f66b6649f --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Forms/Html/FormMethod.cs @@ -0,0 +1,9 @@ + +namespace Microsoft.AspNet.Mvc +{ + public enum FormMethod + { + Get, + Post + } +} diff --git a/src/Microsoft.AspNet.Mvc.Forms/Html/HtmlHelper.cs b/src/Microsoft.AspNet.Mvc.Forms/Html/HtmlHelper.cs new file mode 100644 index 0000000000..87a176c51b --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Forms/Html/HtmlHelper.cs @@ -0,0 +1,342 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Net; +using System.Text; + +namespace Microsoft.AspNet.Mvc +{ + public class HtmlHelper + { + public static readonly string ValidationInputCssClassName = "input-validation-error"; + public static readonly string ValidationInputValidCssClassName = "input-validation-valid"; + public static readonly string ValidationMessageCssClassName = "field-validation-error"; + public static readonly string ValidationMessageValidCssClassName = "field-validation-valid"; + public static readonly string ValidationSummaryCssClassName = "validation-summary-errors"; + public static readonly string ValidationSummaryValidCssClassName = "validation-summary-valid"; + + private static readonly object _html5InputsModeKey = new object(); + + public HtmlHelper(RequestContext requestContext, ViewData viewData) + { + if (requestContext == null) + { + throw new ArgumentNullException("requestContext"); + } + + RequestContext = requestContext; + ViewData = viewData; + // ClientValidationRuleFactory = (name, metadata) => ModelValidatorProviders.Providers.GetValidators(metadata ?? ModelMetadata.FromStringExpression(name, ViewData), ViewContext).SelectMany(v => v.GetClientValidationRules()); + } + + //internal Func> ClientValidationRuleFactory { get; set; } + + public RequestContext RequestContext { get; private set; } + + public ViewData ViewData + { + get; + private set; + } + + /// + /// Creates a dictionary of HTML attributes from the input object, + /// translating underscores to dashes. + /// + /// new { data_name="value" } will translate to the entry { "data-name" , "value" } + /// in the resulting dictionary. + /// + /// + /// Anonymous object describing HTML attributes. + /// A dictionary that represents HTML attributes. + public static Dictionary AnonymousObjectToHtmlAttributes(object htmlAttributes) + { + Dictionary result; + IDictionary valuesAsDictionary = htmlAttributes as IDictionary; + if (valuesAsDictionary != null) + { + result = new Dictionary(valuesAsDictionary, StringComparer.OrdinalIgnoreCase); + } + else + { + result = new Dictionary(StringComparer.OrdinalIgnoreCase); + + if (htmlAttributes != null) + { + PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(htmlAttributes); + foreach (PropertyDescriptor prop in properties) + { + object val = prop.GetValue(htmlAttributes); + result.Add(prop.Name, val); + } + } + } + + return result; + } + + //[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "For consistency, all helpers are instance methods.")] + //public HtmlString AntiForgeryToken() + //{ + // return new HtmlString(AntiForgery.GetHtml().ToString()); + //} + + ///// + ///// Set this property to to have templated helpers such as Html.EditorFor render date and time + ///// values as Rfc3339 compliant strings. + ///// + ///// + ///// The scope of this setting is for the current view alone. Sub views and parent views + ///// will default to unless explicitly set otherwise. + ///// + //[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "The usage of the property is as an instance property of the helper.")] + //public Html5DateRenderingMode Html5DateRenderingMode + //{ + // get + // { + // object value; + // if (ScopeStorage.CurrentScope.TryGetValue(_html5InputsModeKey, out value)) + // { + // return (Html5DateRenderingMode)value; + // } + // return default(Html5DateRenderingMode); + // } + // set + // { + // ScopeStorage.CurrentScope[_html5InputsModeKey] = value; + // } + //} + + //[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "For consistency, all helpers are instance methods.")] + //public string AttributeEncode(string value) + //{ + // return (!String.IsNullOrEmpty(value)) ? HttpUtility.HtmlAttributeEncode(value) : String.Empty; + //} + + //public string AttributeEncode(object value) + //{ + // return AttributeEncode(Convert.ToString(value, CultureInfo.InvariantCulture)); + //} + + [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "For consistency, all helpers are instance methods.")] + public string Encode(string value) + { + return (!String.IsNullOrEmpty(value)) ? WebUtility.HtmlEncode(value) : String.Empty; + } + + [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "For consistency, all helpers are instance methods.")] + public string Encode(object value) + { + return value != null ? WebUtility.HtmlEncode(value.ToString()) : String.Empty; + } + + internal static IView FindPartialView(ViewContext viewContext, string partialViewName, IViewEngine viewEngine) + { + ViewEngineResult result = viewEngine.FindView(viewContext, partialViewName).Result; + if (result.View != null) + { + return result.View; + } + + StringBuilder locationsText = new StringBuilder(); + foreach (string location in result.SearchedLocations) + { + locationsText.AppendLine(); + locationsText.Append(location); + } + + throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, + "MvcResources.Common_PartialViewNotFound", partialViewName, locationsText)); + } + + public static string GenerateIdFromName(string name) + { + if (name == null) + { + throw new ArgumentNullException("name"); + } + + return TagBuilder.CreateSanitizedId(name); + } + + //public static string GenerateLink(RequestContext requestContext, RouteCollection routeCollection, string linkText, string routeName, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary htmlAttributes) + //{ + // return GenerateLink(requestContext, routeCollection, linkText, routeName, actionName, controllerName, null /* protocol */, null /* hostName */, null /* fragment */, routeValues, htmlAttributes); + //} + + //public static string GenerateLink(RequestContext requestContext, RouteCollection routeCollection, string linkText, string routeName, string actionName, string controllerName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, IDictionary htmlAttributes) + //{ + // return GenerateLinkInternal(requestContext, routeCollection, linkText, routeName, actionName, controllerName, protocol, hostName, fragment, routeValues, htmlAttributes, true /* includeImplicitMvcValues */); + //} + + //private static string GenerateLinkInternal(RequestContext requestContext, RouteCollection routeCollection, string linkText, string routeName, string actionName, string controllerName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, IDictionary htmlAttributes, bool includeImplicitMvcValues) + //{ + // string url = UrlHelper.GenerateUrl(routeName, actionName, controllerName, protocol, hostName, fragment, routeValues, routeCollection, requestContext, includeImplicitMvcValues); + // TagBuilder tagBuilder = new TagBuilder("a") + // { + // InnerHtml = (!String.IsNullOrEmpty(linkText)) ? HttpUtility.HtmlEncode(linkText) : String.Empty + // }; + // tagBuilder.MergeAttributes(htmlAttributes); + // tagBuilder.MergeAttribute("href", url); + // return tagBuilder.ToString(TagRenderMode.Normal); + //} + + //public static string GenerateRouteLink(RequestContext requestContext, RouteCollection routeCollection, string linkText, string routeName, RouteValueDictionary routeValues, IDictionary htmlAttributes) + //{ + // return GenerateRouteLink(requestContext, routeCollection, linkText, routeName, null /* protocol */, null /* hostName */, null /* fragment */, routeValues, htmlAttributes); + //} + + //public static string GenerateRouteLink(RequestContext requestContext, RouteCollection routeCollection, string linkText, string routeName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, IDictionary htmlAttributes) + //{ + // return GenerateLinkInternal(requestContext, routeCollection, linkText, routeName, null /* actionName */, null /* controllerName */, protocol, hostName, fragment, routeValues, htmlAttributes, false /* includeImplicitMvcValues */); + //} + + public static string GetFormMethodString(FormMethod method) + { + switch (method) + { + case FormMethod.Get: + return "get"; + case FormMethod.Post: + return "post"; + default: + return "post"; + } + } + + public static string GetInputTypeString(InputType inputType) + { + switch (inputType) + { + case InputType.CheckBox: + return "checkbox"; + case InputType.Hidden: + return "hidden"; + case InputType.Password: + return "password"; + case InputType.Radio: + return "radio"; + case InputType.Text: + return "text"; + default: + return "text"; + } + } + + //internal object GetModelStateValue(string key, Type destinationType) + //{ + // ModelState modelState; + // if (ViewData.ModelState.TryGetValue(key, out modelState)) + // { + // if (modelState.Value != null) + // { + // return modelState.Value.ConvertTo(destinationType, null /* culture */); + // } + // } + // return null; + //} + + //public IDictionary GetUnobtrusiveValidationAttributes(string name) + //{ + // return GetUnobtrusiveValidationAttributes(name, metadata: null); + //} + + //// Only render attributes if unobtrusive client-side validation is enabled, and then only if we've + //// never rendered validation for a field with this name in this form. Also, if there's no form context, + //// then we can't render the attributes (we'd have no
to attach them to). + //public IDictionary GetUnobtrusiveValidationAttributes(string name, ModelMetadata metadata) + //{ + // Dictionary results = new Dictionary(); + + // // The ordering of these 3 checks (and the early exits) is for performance reasons. + // if (!ViewContext.UnobtrusiveJavaScriptEnabled) + // { + // return results; + // } + + // FormContext formContext = ViewContext.GetFormContextForClientValidation(); + // if (formContext == null) + // { + // return results; + // } + + // string fullName = ViewData.TemplateInfo.GetFullHtmlFieldName(name); + // if (formContext.RenderedField(fullName)) + // { + // return results; + // } + + // formContext.RenderedField(fullName, true); + + // IEnumerable clientRules = ClientValidationRuleFactory(name, metadata); + // UnobtrusiveValidationAttributesGenerator.GetValidationAttributes(clientRules, results); + + // return results; + //} + + /// + /// Wraps HTML markup in an IHtmlString, which will enable HTML markup to be + /// rendered to the output without getting HTML encoded. + /// + /// HTML markup string. + /// An IHtmlString that represents HTML markup. + [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "For consistency, all helpers are instance methods.")] + public HtmlString Raw(string value) + { + return new HtmlString(value); + } + + /// + /// Wraps HTML markup from the string representation of an object in an IHtmlString, + /// which will enable HTML markup to be rendered to the output without getting HTML encoded. + /// + /// object with string representation as HTML markup + /// An IHtmlString that represents HTML markup. + [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "For consistency, all helpers are instance methods.")] + public HtmlString Raw(object value) + { + return new HtmlString(value == null ? null : value.ToString()); + } + + //internal virtual void RenderPartialInternal(string partialViewName, ViewData viewData, TModel model, TextWriter writer, IViewEngine viewEngine) + //{ + // if (String.IsNullOrEmpty(partialViewName)) + // { + // throw new ArgumentException("MvcResources.Common_NullOrEmpty", "partialViewName"); + // } + + // ViewData newViewData = null; + + // if (model == null) + // { + // if (viewData == null) + // { + // newViewData = new ViewData(ViewContext.ViewData); + // } + // else + // { + // newViewData = new ViewData(viewData); + // } + // } + // else + // { + // if (viewData == null) + // { + // newViewData = new ViewData(model); + // } + // else + // { + // newViewData = new ViewData(viewData) { Model = model }; + // } + // } + + // ViewContext newViewContext = new ViewContext(ViewContext, ViewContext.View, newViewData, ViewContext.TempData, writer); + // IView view = FindPartialView(newViewContext, partialViewName, viewEngine); + // view.Render(newViewContext, writer); + //} + } +} diff --git a/src/Microsoft.AspNet.Mvc.Forms/Html/HtmlHelperOfT.cs b/src/Microsoft.AspNet.Mvc.Forms/Html/HtmlHelperOfT.cs new file mode 100644 index 0000000000..f9e8d24a31 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Forms/Html/HtmlHelperOfT.cs @@ -0,0 +1,23 @@ +using System; + +namespace Microsoft.AspNet.Mvc +{ + public class HtmlHelper : HtmlHelper + { + public HtmlHelper(RequestContext requestContext, ViewData viewData) + : base(requestContext, viewData) + { + if (requestContext == null) + { + throw new ArgumentNullException("requestContext"); + } + + ViewData = viewData; + } + + public new ViewData ViewData + { + get; private set; + } + } +} diff --git a/src/Microsoft.AspNet.Mvc.Forms/Html/HtmlString.cs b/src/Microsoft.AspNet.Mvc.Forms/Html/HtmlString.cs new file mode 100644 index 0000000000..b71180201d --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Forms/Html/HtmlString.cs @@ -0,0 +1,18 @@ + +namespace Microsoft.AspNet.Mvc +{ + public class HtmlString + { + private readonly string _input; + + public HtmlString(string input) + { + _input = input; + } + + public override string ToString() + { + return _input; + } + } +} diff --git a/src/Microsoft.AspNet.Mvc.Forms/Html/InputType.cs b/src/Microsoft.AspNet.Mvc.Forms/Html/InputType.cs new file mode 100644 index 0000000000..9dd8a97c46 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Forms/Html/InputType.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Mvc +{ + public enum InputType + { + CheckBox, + Hidden, + Password, + Radio, + Text + } +} diff --git a/src/Microsoft.AspNet.Mvc.Forms/Html/TagBuilder.cs b/src/Microsoft.AspNet.Mvc.Forms/Html/TagBuilder.cs new file mode 100644 index 0000000000..9b2acaeee4 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Forms/Html/TagBuilder.cs @@ -0,0 +1,208 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Net; +using System.Text; + +namespace Microsoft.AspNet.Mvc +{ + public class TagBuilder + { + private string _idAttributeDotReplacement; + + private string _innerHtml; + + public TagBuilder(string tagName) + { + if (String.IsNullOrEmpty(tagName)) + { + throw new ArgumentException("CommonResources.Argument_Cannot_Be_Null_Or_Empty", "tagName"); + } + + TagName = tagName; + Attributes = new SortedDictionary(StringComparer.Ordinal); + } + + public IDictionary Attributes { get; private set; } + + public string InnerHtml + { + get { return _innerHtml ?? String.Empty; } + set { _innerHtml = value; } + } + + public string TagName { get; private set; } + + public void AddCssClass(string value) + { + string currentValue; + + if (Attributes.TryGetValue("class", out currentValue)) + { + Attributes["class"] = value + " " + currentValue; + } + else + { + Attributes["class"] = value; + } + } + + public static string CreateSanitizedId(string originalId) + { + return CreateSanitizedId(originalId, "." /*HtmlHelper.IdAttributeDotReplacement*/); + } + + public static string CreateSanitizedId(string originalId, string invalidCharReplacement) + { + if (String.IsNullOrEmpty(originalId)) + { + return null; + } + + if (invalidCharReplacement == null) + { + throw new ArgumentNullException("invalidCharReplacement"); + } + + char firstChar = originalId[0]; + + StringBuilder sb = new StringBuilder(originalId.Length); + sb.Append(firstChar); + + for (int i = 1; i < originalId.Length; i++) + { + char thisChar = originalId[i]; + if (!Char.IsWhiteSpace(thisChar)) + { + sb.Append(thisChar); + } + else + { + sb.Append(invalidCharReplacement); + } + } + + return sb.ToString(); + } + + public void GenerateId(string name) + { + if (!Attributes.ContainsKey("id")) + { + string sanitizedId = name; // CreateSanitizedId(name, IdAttributeDotReplacement); + if (!String.IsNullOrEmpty(sanitizedId)) + { + Attributes["id"] = sanitizedId; + } + } + } + + private void AppendAttributes(StringBuilder sb) + { + foreach (var attribute in Attributes) + { + string key = attribute.Key; + if (String.Equals(key, "id", StringComparison.Ordinal) && String.IsNullOrEmpty(attribute.Value)) + { + continue; + } + //string value = HttpUtility.HtmlAttributeEncode(attribute.Value); + string value = WebUtility.HtmlEncode(attribute.Value); + sb.Append(' ') + .Append(key) + .Append("=\"") + .Append(value) + .Append('"'); + } + } + + public void MergeAttribute(string key, string value) + { + MergeAttribute(key, value, replaceExisting: false); + } + + public void MergeAttribute(string key, string value, bool replaceExisting) + { + if (String.IsNullOrEmpty(key)) + { + throw new ArgumentException("CommonResources.Argument_Cannot_Be_Null_Or_Empty", "key"); + } + + if (replaceExisting || !Attributes.ContainsKey(key)) + { + Attributes[key] = value; + } + } + + public void MergeAttributes(IDictionary attributes) + { + MergeAttributes(attributes, replaceExisting: false); + } + + public void MergeAttributes(IDictionary attributes, bool replaceExisting) + { + if (attributes != null) + { + foreach (var entry in attributes) + { + string key = Convert.ToString(entry.Key, CultureInfo.InvariantCulture); + string value = Convert.ToString(entry.Value, CultureInfo.InvariantCulture); + MergeAttribute(key, value, replaceExisting); + } + } + } + + public void SetInnerText(string innerText) + { + InnerHtml = WebUtility.HtmlEncode(innerText); + } + + internal HtmlString ToHtmlString(TagRenderMode renderMode) + { + return new HtmlString(ToString(renderMode)); + } + + public override string ToString() + { + return ToString(TagRenderMode.Normal); + } + + public string ToString(TagRenderMode renderMode) + { + StringBuilder sb = new StringBuilder(); + switch (renderMode) + { + case TagRenderMode.StartTag: + sb.Append('<') + .Append(TagName); + AppendAttributes(sb); + sb.Append('>'); + break; + case TagRenderMode.EndTag: + sb.Append("'); + break; + case TagRenderMode.SelfClosing: + sb.Append('<') + .Append(TagName); + AppendAttributes(sb); + sb.Append(" />"); + break; + default: + sb.Append('<') + .Append(TagName); + AppendAttributes(sb); + sb.Append('>') + .Append(InnerHtml) + .Append("'); + break; + } + return sb.ToString(); + } + } +} diff --git a/src/Microsoft.AspNet.Mvc.Forms/Html/TagRenderMode.cs b/src/Microsoft.AspNet.Mvc.Forms/Html/TagRenderMode.cs new file mode 100644 index 0000000000..e727617c66 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Forms/Html/TagRenderMode.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.Runtime.CompilerServices; + +namespace Microsoft.AspNet.Mvc +{ + public enum TagRenderMode + { + Normal, + StartTag, + EndTag, + SelfClosing + } +} diff --git a/src/Microsoft.AspNet.Mvc.Forms/Microsoft.AspNet.Mvc.Forms.csproj b/src/Microsoft.AspNet.Mvc.Forms/Microsoft.AspNet.Mvc.Forms.csproj new file mode 100644 index 0000000000..aa816d393b --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Forms/Microsoft.AspNet.Mvc.Forms.csproj @@ -0,0 +1,79 @@ + + + + + Debug + AnyCPU + {AB4CDC03-176C-460F-8955-4202F6D53FED} + Library + Properties + Microsoft.AspNet.Mvc + Microsoft.AspNet.Mvc.Forms + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\..\packages\Microsoft.Owin.2.1.0\lib\net45\Microsoft.Owin.dll + + + ..\..\packages\Owin.1.0\lib\net40\Owin.dll + + + + + + + + + + + + Code + + + + + + + + + + + {ec38534c-a2d1-413f-97d1-55eef5d2fb71} + Microsoft.AspNet.CoreServices + + + {2a0c26f1-0240-4ae1-ae00-4691c291b122} + Microsoft.AspNet.Mvc + + + + + + + + \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Forms/Properties/AssemblyInfo.cs b/src/Microsoft.AspNet.Mvc.Forms/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..6ba2f874f5 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Forms/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Microsoft.AspNet.Mvc.Helpers")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Microsoft.AspNet.Mvc.Helpers")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("93a358b1-7016-473f-a691-0724ae09dae2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Microsoft.AspNet.Mvc.Forms/TypeHelpers.cs b/src/Microsoft.AspNet.Mvc.Forms/TypeHelpers.cs new file mode 100644 index 0000000000..b26a2b8446 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Forms/TypeHelpers.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Mvc +{ + internal delegate bool TryGetValueDelegate(object dictionary, string key, out object value); + + internal static class TypeHelpers + { + private static readonly Dictionary _tryGetValueDelegateCache = new Dictionary(); + private static readonly ReaderWriterLockSlim _tryGetValueDelegateCacheLock = new ReaderWriterLockSlim(); + + private static readonly MethodInfo _strongTryGetValueImplInfo = typeof(TypeHelpers).GetMethod("StrongTryGetValueImpl", BindingFlags.NonPublic | BindingFlags.Static); + + public static readonly Assembly MsCorLibAssembly = typeof(string).Assembly; + public static readonly Assembly MvcAssembly = null; //typeof(ControllerBase).Assembly; + + // method is used primarily for lighting up new .NET Framework features even if MVC targets the previous version + // thisParameter is the 'this' parameter if target method is instance method, should be null for static method + public static TDelegate CreateDelegate(Assembly assembly, string typeName, string methodName, object thisParameter) where TDelegate : class + { + // ensure target type exists + Type targetType = assembly.GetType(typeName, false /* throwOnError */); + if (targetType == null) + { + return null; + } + + return CreateDelegate(targetType, methodName, thisParameter); + } + + public static TDelegate CreateDelegate(Type targetType, string methodName, object thisParameter) where TDelegate : class + { + // ensure target method exists + ParameterInfo[] delegateParameters = typeof(TDelegate).GetMethod("Invoke").GetParameters(); + Type[] argumentTypes = Array.ConvertAll(delegateParameters, pInfo => pInfo.ParameterType); + MethodInfo targetMethod = targetType.GetMethod(methodName, argumentTypes); + if (targetMethod == null) + { + return null; + } + + TDelegate d = Delegate.CreateDelegate(typeof(TDelegate), thisParameter, targetMethod, false /* throwOnBindFailure */) as TDelegate; + return d; + } + + public static TryGetValueDelegate CreateTryGetValueDelegate(Type targetType) + { + TryGetValueDelegate result; + + _tryGetValueDelegateCacheLock.EnterReadLock(); + try + { + if (_tryGetValueDelegateCache.TryGetValue(targetType, out result)) + { + return result; + } + } + finally + { + _tryGetValueDelegateCacheLock.ExitReadLock(); + } + + Type dictionaryType = ExtractGenericInterface(targetType, typeof(IDictionary<,>)); + + // just wrap a call to the underlying IDictionary.TryGetValue() where string can be cast to TKey + if (dictionaryType != null) + { + Type[] typeArguments = dictionaryType.GetGenericArguments(); + Type keyType = typeArguments[0]; + Type returnType = typeArguments[1]; + + if (keyType.IsAssignableFrom(typeof(string))) + { + MethodInfo strongImplInfo = _strongTryGetValueImplInfo.MakeGenericMethod(keyType, returnType); + result = (TryGetValueDelegate)Delegate.CreateDelegate(typeof(TryGetValueDelegate), strongImplInfo); + } + } + + // wrap a call to the underlying IDictionary.Item() + if (result == null && typeof(IDictionary).IsAssignableFrom(targetType)) + { + result = TryGetValueFromNonGenericDictionary; + } + + _tryGetValueDelegateCacheLock.EnterWriteLock(); + try + { + _tryGetValueDelegateCache[targetType] = result; + } + finally + { + _tryGetValueDelegateCacheLock.ExitWriteLock(); + } + + return result; + } + + public static Type ExtractGenericInterface(Type queryType, Type interfaceType) + { + if (MatchesGenericType(queryType, interfaceType)) + { + return queryType; + } + Type[] queryTypeInterfaces = queryType.GetInterfaces(); + return MatchGenericTypeFirstOrDefault(queryTypeInterfaces, interfaceType); + } + + public static object GetDefaultValue(Type type) + { + return (TypeAllowsNullValue(type)) ? null : Activator.CreateInstance(type); + } + + public static bool IsCompatibleObject(object value) + { + return (value is T || (value == null && TypeAllowsNullValue(typeof(T)))); + } + + public static bool IsNullableValueType(Type type) + { + return Nullable.GetUnderlyingType(type) != null; + } + + private static bool MatchesGenericType(Type type, Type matchType) + { + return type.IsGenericType && type.GetGenericTypeDefinition() == matchType; + } + + private static Type MatchGenericTypeFirstOrDefault(Type[] types, Type matchType) + { + for (int i = 0; i < types.Length; i++) + { + Type type = types[i]; + if (MatchesGenericType(type, matchType)) + { + return type; + } + } + return null; + } + + private static bool StrongTryGetValueImpl(object dictionary, string key, out object value) + { + IDictionary strongDict = (IDictionary)dictionary; + + TValue strongValue; + bool retVal = strongDict.TryGetValue((TKey)(object)key, out strongValue); + value = strongValue; + return retVal; + } + + private static bool TryGetValueFromNonGenericDictionary(object dictionary, string key, out object value) + { + IDictionary weakDict = (IDictionary)dictionary; + + bool containsKey = weakDict.Contains(key); + value = (containsKey) ? weakDict[key] : null; + return containsKey; + } + + public static bool TypeAllowsNullValue(Type type) + { + return (!type.IsValueType || IsNullableValueType(type)); + } + } +} diff --git a/src/Microsoft.AspNet.Mvc.Forms/packages.config b/src/Microsoft.AspNet.Mvc.Forms/packages.config new file mode 100644 index 0000000000..fb47f58ced --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Forms/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Razor/Microsoft.AspNet.Mvc.Razor.csproj b/src/Microsoft.AspNet.Mvc.Razor/Microsoft.AspNet.Mvc.Razor.csproj index f78349c8da..db1f1caac6 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/Microsoft.AspNet.Mvc.Razor.csproj +++ b/src/Microsoft.AspNet.Mvc.Razor/Microsoft.AspNet.Mvc.Razor.csproj @@ -85,6 +85,7 @@ +