PageRenderTime 20ms CodeModel.GetById 14ms app.highlight 3ms RepoModel.GetById 1ms app.codeStats 1ms

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

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