PageRenderTime 49ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/BlogEngine/BlogEngine.NET/App_Code/Extensions/BreakPost.cs

#
C# | 153 lines | 91 code | 23 blank | 39 comment | 11 complexity | b3aa68c76fd29cb86a0017b541a24c0a MD5 | raw file
Possible License(s): LGPL-2.1, Apache-2.0, BSD-3-Clause
  1. #region using
  2. using System;
  3. using System.Linq;
  4. using System.Text.RegularExpressions;
  5. using System.Web;
  6. using BlogEngine.Core;
  7. using BlogEngine.Core.Web.Controls;
  8. using BlogEngine.Core.Web.Extensions;
  9. using Resources;
  10. #endregion
  11. /// <summary>
  12. /// Breaks a post where [more] is found in the body and adds a link to full post.
  13. /// </summary>
  14. [Extension(
  15. "Breaks a post where [more] is found in the body and adds a link to full post",
  16. "1.4",
  17. "BlogEngine.NET",
  18. 1010)]
  19. public class BreakPost
  20. {
  21. #region Constants and Fields
  22. /// <summary>
  23. /// The closed tag regex.
  24. /// </summary>
  25. private static readonly Regex ClosedTagRegex = new Regex(
  26. @"</([A-Z][A-Z0-9]*?)\b[^>]*>", RegexOptions.IgnoreCase | RegexOptions.Compiled);
  27. /// <summary>
  28. /// The opening tag regex.
  29. /// </summary>
  30. private static readonly Regex OpeningTagRegex = new Regex(
  31. @"<([A-Z][A-Z0-9]*?)\b[^>/]*>", RegexOptions.IgnoreCase | RegexOptions.Compiled);
  32. #endregion
  33. #region Constructors and Destructors
  34. /// <summary>
  35. /// Initializes static members of the <see cref="BreakPost"/> class.
  36. /// Hooks up an event handler to the Post.Serving event.
  37. /// </summary>
  38. static BreakPost()
  39. {
  40. Post.Serving += PostServing;
  41. }
  42. #endregion
  43. #region Methods
  44. /// <summary>
  45. /// Replaces the [more] string with a hyperlink to the full post.
  46. /// </summary>
  47. /// <param name="sender">
  48. /// The sender.
  49. /// </param>
  50. /// <param name="e">
  51. /// The event arguments.
  52. /// </param>
  53. private static void AddMoreLink(object sender, ServingEventArgs e)
  54. {
  55. var post = (Post)sender;
  56. var index = e.Body.IndexOf("[more]");
  57. var link = string.Format("<a class=\"more\" href=\"{0}#continue\">{1}...</a>", post.RelativeLink, labels.more);
  58. var newBody = e.Body.Substring(0, index);
  59. // Need to close any open HTML tags in NewBody where the matching close tags have been truncated.
  60. var closingTagsToAppend = string.Empty;
  61. var openingTagsCollection = OpeningTagRegex.Matches(newBody);
  62. if (openingTagsCollection.Count > 0)
  63. {
  64. // Copy the opening tags in MatchCollection to a generic list.
  65. var openingTags = openingTagsCollection.Cast<Match>().Where(openTag => openTag.Groups.Count == 2).Select(
  66. openTag => openTag.Groups[1].Value).ToList();
  67. var closingTagsCollection = ClosedTagRegex.Matches(newBody);
  68. // Iterate through closed tags and remove the first matching open tag from the openingTags list.
  69. foreach (var indexToRemove in from Match closedTag in closingTagsCollection
  70. where closedTag.Groups.Count == 2
  71. select openingTags.FindIndex(
  72. openTag =>
  73. openTag.Equals(
  74. closedTag.Groups[1].Value, StringComparison.InvariantCultureIgnoreCase))
  75. into indexToRemove
  76. where indexToRemove != -1
  77. select indexToRemove)
  78. {
  79. openingTags.RemoveAt(indexToRemove);
  80. }
  81. // A closing tag needs to be created for any remaining tags in the openingTags list.
  82. if (openingTags.Count > 0)
  83. {
  84. // Reverse the order of the tags so tags opened later are closed first.
  85. openingTags.Reverse();
  86. closingTagsToAppend = string.Format("</{0}>", string.Join("></", openingTags.ToArray()));
  87. }
  88. }
  89. e.Body = newBody + link + closingTagsToAppend;
  90. }
  91. /// <summary>
  92. /// Handles the Serving event of the Post control.
  93. /// Handles the Post.Serving event to take care of the [more] keyword.
  94. /// </summary>
  95. /// <param name="sender">The source of the event.</param>
  96. /// <param name="e">The <see cref="BlogEngine.Core.ServingEventArgs"/> instance containing the event data.</param>
  97. private static void PostServing(object sender, ServingEventArgs e)
  98. {
  99. if (!ExtensionManager.ExtensionEnabled("BreakPost"))
  100. return;
  101. if (!e.Body.Contains("[more]"))
  102. return;
  103. switch (e.Location)
  104. {
  105. case ServingLocation.PostList:
  106. AddMoreLink(sender, e);
  107. break;
  108. case ServingLocation.SinglePost:
  109. PrepareFullPost(e);
  110. break;
  111. case ServingLocation.Feed:
  112. e.Body = e.Body.Replace("[more]", string.Empty);
  113. break;
  114. }
  115. }
  116. /// <summary>
  117. /// Replaces the [more] string on the full post page.
  118. /// </summary>
  119. /// <param name="e">
  120. /// The event arguments.
  121. /// </param>
  122. private static void PrepareFullPost(ServingEventArgs e)
  123. {
  124. var request = HttpContext.Current.Request;
  125. e.Body = request.UrlReferrer == null || request.UrlReferrer.Host != request.Url.Host
  126. ? e.Body.Replace("[more]", string.Empty)
  127. : e.Body.Replace("[more]", "<span id=\"continue\"></span>");
  128. }
  129. #endregion
  130. }