HtmlContentExtension.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. using FluentValidation.Internal;
  2. using GreenTree.Strohrmann.ERP.Core.Extension;
  3. using Microsoft.AspNetCore.Html;
  4. using Microsoft.AspNetCore.Mvc.ModelBinding;
  5. using Microsoft.AspNetCore.Mvc.Rendering;
  6. using Microsoft.EntityFrameworkCore.Infrastructure;
  7. using Newtonsoft.Json;
  8. using Newtonsoft.Json.Linq;
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Globalization;
  12. using System.IO;
  13. using System.Linq;
  14. using System.Linq.Expressions;
  15. using System.Text;
  16. using System.Threading.Tasks;
  17. namespace GreenTree.Strohrmann.ERP.Web.Extension
  18. {
  19. public static class HtmlContentExtension
  20. {
  21. #region Inputs
  22. /// <summary>
  23. /// Return HTML markup for the <paramref name="expression"/>, using a display template for a bootstrap extension token field.
  24. /// See <see href="https://sliptree.github.io/bootstrap-tokenfield/">bootstrap-tokenfield</see> for more information of the
  25. /// input type.
  26. /// </summary>
  27. /// <typeparam name="TModel">Model type.</typeparam>
  28. /// <typeparam name="TValue">Model value type.</typeparam>
  29. /// <param name="htmlHelper">HtmlHelper context.</param>
  30. /// <param name="expression">Expression for the requested model enumeration value.</param>
  31. /// <param name="delimiter">The delimiter string used to seperate token values.</param>
  32. /// <param name="htmlAttributes">Additional HTML attributes.</param>
  33. /// <param name="availableValues">The available values for the tokenbox.</param>
  34. /// <param name="allowOnlyOnce"><c>True</c> when every available value can only be selected once.</param>
  35. public static IHtmlContent TokenBoxFor<TModel, TValue>(
  36. this IHtmlHelper<TModel> htmlHelper,
  37. Expression<Func<TModel, TValue>> expression,
  38. string delimiter = "|",
  39. object htmlAttributes = null,
  40. string[] availableValues = null,
  41. bool allowOnlyOnce = true) where TValue : IEnumerable<string>
  42. {
  43. var attributes = htmlAttributes == null
  44. ? new string[0]
  45. : htmlAttributes.GetType().GetProperties()
  46. .Select(p => String.Format("{0}='{1}'", p.Name.Replace("_", "-"), p.GetValue(htmlAttributes)))
  47. .ToArray();
  48. var func = expression.Compile();
  49. var id = Guid.NewGuid().ToShortString();
  50. var scriptBuilder = new StringBuilder();
  51. var memberInfo = expression.Body.NodeType == ExpressionType.MemberAccess
  52. ? ((MemberExpression)expression.Body).Member
  53. : null;
  54. var type = expression.Body.GetType();
  55. if (availableValues != null)
  56. {
  57. var autoComplete = JsonConvertWithoutQuotes(new
  58. {
  59. autocomplete = new
  60. {
  61. source = availableValues,
  62. delay = 200
  63. },
  64. showAutocompleteOnFocus = true
  65. });
  66. scriptBuilder.AppendFormat(
  67. "<script>" +
  68. " $('#{0}').tokenfield({1})",
  69. id, autoComplete);
  70. if (allowOnlyOnce)
  71. {
  72. scriptBuilder.Append(
  73. ".on('tokenfield:createtoken', function (e) { " +
  74. " if ($(this).tokenfield('getTokens').some(t => t.value === e.attrs.value)) return false;" +
  75. "})");
  76. }
  77. if (memberInfo != null)
  78. {
  79. var inputCreationScriptPart = String.Format(
  80. "function (e) {{ " +
  81. " $(\"[name = '{1}'\").remove();" +
  82. " $(this).tokenfield('getTokens').forEach(" +
  83. " function (obj) {{ $('#{0}').after('<input type=\"hidden\" name=\"{1}\" value=\"' + obj.value + '\" />'); }});" +
  84. "}}", id, memberInfo.Name);
  85. scriptBuilder.AppendFormat(
  86. ".on('tokenfield:createdtoken', {0})", inputCreationScriptPart);
  87. scriptBuilder.AppendFormat(
  88. ".on('tokenfield:removedtoken', {0})", inputCreationScriptPart);
  89. }
  90. scriptBuilder.Append(";</script>");
  91. }
  92. else
  93. {
  94. scriptBuilder.AppendFormat("<script>$('#{0}').tokenfield();</script>", id);
  95. }
  96. if (htmlHelper.ViewData.Model == null)
  97. {
  98. return new HtmlString(
  99. String.Format(
  100. "<input id='{0}' class='tokenBox' value='{1}' data-delimiter='{2}' {3} /> {4}",
  101. id,
  102. String.Empty,
  103. delimiter,
  104. String.Join(" ", attributes),
  105. scriptBuilder));
  106. }
  107. else
  108. {
  109. var values = func(htmlHelper.ViewData.Model);
  110. var valueInputs = values
  111. .Select(v => String.Format(
  112. "<input type=\"hidden\" name=\"{0}\" value=\"{1}\" />",
  113. memberInfo == null
  114. ? id
  115. : memberInfo.Name,
  116. v));
  117. return new HtmlString(
  118. String.Format(
  119. "<input id='{0}' class='tokenBox' value='{1}' data-delimiter='{2}' {3} /> {4} {5}",
  120. id,
  121. values == null
  122. ? String.Empty
  123. : String.Join(delimiter, values),
  124. delimiter,
  125. String.Join(" ", attributes),
  126. scriptBuilder,
  127. String.Join(" ", valueInputs)));
  128. }
  129. }
  130. /// <summary>
  131. /// Return HTML markup for the <paramref name="expression"/>, using a bootstrap badge containing 'Yes' or 'No'.
  132. /// See <see href="https://getbootstrap.com/docs/4.1/components/badge/">bootstrap badge</see> for more information of the badge.
  133. /// </summary>
  134. /// <typeparam name="TModel">Model type.</typeparam>
  135. /// <param name="htmlHelper">HtmlHelper context.</param>
  136. /// <param name="expression">Expression for the requested model bool value.</param>
  137. public static IHtmlContent YesNoBadgeFor<TModel>(
  138. this IHtmlHelper<TModel> htmlHelper,
  139. Expression<Func<TModel, bool>> expression)
  140. {
  141. var func = expression.Compile();
  142. if (htmlHelper.ViewData.Model == null)
  143. {
  144. return new HtmlString("<span class='badge badge-secondary'>N/A</span>");
  145. }
  146. else
  147. {
  148. return
  149. func(htmlHelper.ViewData.Model)
  150. ? new HtmlString("<span class='badge badge-success'>Ja</span>")
  151. : new HtmlString("<span class='badge badge-danger'>Nein</span>");
  152. }
  153. }
  154. /// <summary>
  155. /// Return HTML markup for the <paramref name="expression"/>, using a display template for a country selection field.
  156. /// </summary>
  157. /// <typeparam name="TModel">Model type.</typeparam>
  158. /// <typeparam name="TValue">Model value type.</typeparam>
  159. /// <param name="htmlHelper">HtmlHelper context.</param>
  160. /// <param name="expression">Expression for the requested model enumeration value.</param>
  161. /// <param name="htmlAttributes">Additional HTML attributes.</param>
  162. public static IHtmlContent CountrySelectionFor<TModel, TValue>(
  163. this IHtmlHelper<TModel> htmlHelper,
  164. Expression<Func<TModel, TValue>> expression,
  165. object htmlAttributes = null)
  166. {
  167. var attributes = htmlAttributes == null
  168. ? new string[0]
  169. : htmlAttributes.GetType().GetProperties()
  170. .Select(p => String.Format("{0}='{1}'", p.Name.Replace("_", "-"), p.GetValue(htmlAttributes)))
  171. .ToArray();
  172. var func = expression.Compile();
  173. var funcVal = htmlHelper.ViewData.Model == null
  174. ? String.Empty
  175. : func(htmlHelper.ViewData.Model).ToString();
  176. var memberInfo = expression.Body.NodeType == ExpressionType.MemberAccess
  177. ? ((MemberExpression)expression.Body).Member
  178. : null;
  179. var name = memberInfo == null
  180. ? Guid.NewGuid().ToShortString()
  181. : memberInfo.Name;
  182. var selectBuilder = new StringBuilder();
  183. selectBuilder.AppendFormat("<select id=\"{0}\" name =\"{0}\" {1}>", name, String.Join(" ", htmlAttributes));
  184. var countries = CultureInfo.GetCultures(CultureTypes.AllCultures)
  185. .Where(c => c.LCID != CultureInfo.InvariantCulture.LCID && !c.IsNeutralCulture)
  186. .Select(c => new RegionInfo(c.LCID))
  187. .Distinct()
  188. .OrderBy(r => r.DisplayName);
  189. foreach (var country in countries)
  190. {
  191. if (country.EnglishName == funcVal)
  192. selectBuilder.AppendFormat("<option selected value=\"{0}\">{1}</option>", country.EnglishName, country.DisplayName);
  193. else
  194. selectBuilder.AppendFormat("<option value=\"{0}\">{1}</option>", country.EnglishName, country.DisplayName);
  195. }
  196. selectBuilder.Append("</select>");
  197. return new HtmlString(selectBuilder.ToString());
  198. }
  199. #endregion
  200. #region Helper
  201. /// <summary>
  202. /// Generates a random short ID string
  203. /// </summary>
  204. /// <param name="htmlHelper">HtmlHelper context.</param>
  205. public static string RandomId(this IHtmlHelper htmlHelper)
  206. {
  207. return Guid.NewGuid().ToShortString();
  208. }
  209. /// <summary>
  210. /// Converts an object in a JS literal object format
  211. /// </summary>
  212. /// <param name="value">The object to be converted.</param>
  213. private static string JsonConvertWithoutQuotes(object value)
  214. {
  215. var serializer = new JsonSerializer();
  216. var stringWriter = new StringWriter();
  217. using (var writer = new JsonTextWriter(stringWriter))
  218. {
  219. writer.QuoteName = false;
  220. serializer.Serialize(writer, value);
  221. }
  222. return stringWriter.ToString();
  223. }
  224. #endregion
  225. }
  226. }