PageRenderTime 28ms CodeModel.GetById 9ms app.highlight 13ms RepoModel.GetById 2ms app.codeStats 0ms

/BlogEngine/BlogEngine.NET/App_Code/Controls/Blogroll.cs

#
C# | 452 lines | 282 code | 60 blank | 110 comment | 37 complexity | 4ac7fab81025afca037d8d1b01308bd6 MD5 | raw file
  1// --------------------------------------------------------------------------------------------------------------------
  2// <summary>
  3//   Creates and displays a dynamic blogroll.
  4// </summary>
  5// --------------------------------------------------------------------------------------------------------------------
  6
  7namespace App_Code.Controls
  8{
  9    using System;
 10    using System.Collections.Generic;
 11    using System.IO;
 12    using System.Linq;
 13    using System.Net;
 14    using System.Web;
 15    using System.Web.UI;
 16    using System.Web.UI.HtmlControls;
 17    using System.Xml;
 18
 19    using BlogEngine.Core;
 20
 21    /// <summary>
 22    /// Creates and displays a dynamic blogroll.
 23    /// </summary>
 24    public class Blogroll : Control
 25    {
 26        /// <summary>
 27        /// Initializes a new instance of the <see cref="Blogroll"/> class.
 28        /// </summary>
 29        public Blogroll()
 30        {
 31            this.ShowRssIcon = true;
 32            BlogRollItem.Saved += BlogRollItemSaved;
 33        }
 34
 35        /// <summary>
 36        /// Handles the Saved event of the BlogRollItem control.
 37        /// </summary>
 38        /// <param name="sender">The source of the event.</param>
 39        /// <param name="e">The <see cref="BlogEngine.Core.SavedEventArgs"/> instance containing the event data.</param>
 40        private static void BlogRollItemSaved(object sender, SavedEventArgs e)
 41        {
 42            switch (e.Action)
 43            {
 44                case SaveAction.Insert:
 45                    AddBlog((BlogRollItem)sender);
 46                    break;
 47                case SaveAction.Delete:
 48                    var affected = Items.FirstOrDefault(req => req.RollItem.Equals(sender));
 49                    Items.Remove(affected);
 50                    break;
 51            }
 52
 53            if (((e.Action == SaveAction.Insert || e.Action == SaveAction.Delete) || e.Action == SaveAction.Update) &&
 54                Items.Count > 0)
 55            {
 56                // Re-sort _Items collection in case sorting of blogroll items was changed.
 57                Items.Sort((br1, br2) => br1.RollItem.CompareTo(br2.RollItem));
 58            }
 59        }
 60
 61        /// <summary>
 62        /// Sends server control content to a provided <see cref="T:System.Web.UI.HtmlTextWriter"/> object, which writes the content to be rendered on the client.
 63        /// </summary>
 64        /// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the server control content.</param>
 65        protected override void Render(HtmlTextWriter writer)
 66        {
 67            if (this.Page.IsPostBack || this.Page.IsCallback)
 68            {
 69                return;
 70            }
 71
 72            var ul = this.DisplayBlogroll();
 73            using (var sw = new StringWriter())
 74            {
 75                ul.RenderControl(new HtmlTextWriter(sw));
 76                var html = sw.ToString();
 77
 78                writer.WriteLine("<div id=\"blogroll\">");
 79                writer.WriteLine(html);
 80                writer.WriteLine("</div>");
 81            }
 82        }
 83
 84        #region Private fields
 85
 86        /// <summary>
 87        /// The last updated.
 88        /// </summary>
 89        private static Dictionary<Guid, DateTime> blogsLastUpdated = new Dictionary<Guid, DateTime>();
 90
 91        /// <summary>
 92        /// The items.
 93        /// </summary>
 94        private static Dictionary<Guid, List<BlogRequest>> blogsItems = new Dictionary<Guid, List<BlogRequest>>();
 95
 96        #endregion
 97
 98        #region Properties
 99
100        private static DateTime LastUpdated
101        {
102            get
103            {
104                Guid blogId = Blog.CurrentInstance.Id;
105
106                if (!blogsLastUpdated.ContainsKey(blogId))
107                {
108                    lock (SyncRoot)
109                    {
110                        if (!blogsLastUpdated.ContainsKey(blogId))
111                        {
112                            blogsLastUpdated[blogId] = DateTime.Now;
113                        }
114                    }
115                }
116
117                return blogsLastUpdated[blogId];
118            }
119            set
120            {
121                blogsLastUpdated[Blog.CurrentInstance.Id] = value;
122            }
123        }
124
125        private static List<BlogRequest> Items
126        {
127            get
128            {
129                Guid blogId = Blog.CurrentInstance.Id;
130
131                if (!blogsItems.ContainsKey(blogId))
132                {
133                    lock (SyncRoot)
134                    {
135                        if (!blogsItems.ContainsKey(blogId))
136                        {
137                            blogsItems[blogId] = new List<BlogRequest>();
138                        }
139                    }
140                }
141
142                return blogsItems[blogId];
143            }
144        }
145
146        /// <summary>
147        ///     Determines whether the RSS icon is displayed (default true)
148        /// </summary>
149        public bool ShowRssIcon { get; set; }
150
151        /// <summary>
152        ///     Display the description of the blog as a tool tip (default false)
153        /// </summary>
154        public bool DescriptionToolTip { get; set; }
155
156        #endregion
157
158        #region Methods
159
160        /// <summary>
161        /// The sync root.
162        /// </summary>
163        private static readonly object SyncRoot = new object();
164
165        /// <summary>
166        /// Displays the RSS item collection.
167        /// </summary>
168        /// <returns>
169        /// The output.
170        /// </returns>
171        private HtmlGenericControl DisplayBlogroll()
172        {
173            if (DateTime.Now > LastUpdated.AddMinutes(BlogSettings.Instance.BlogrollUpdateMinutes) &&
174                BlogSettings.Instance.BlogrollVisiblePosts > 0)
175            {
176                Items.Clear();
177                LastUpdated = DateTime.Now;
178            }
179
180            if (Items.Count == 0)
181            {
182                lock (SyncRoot)
183                {
184                    if (Items.Count == 0)
185                    {
186                        foreach (var roll in BlogRollItem.BlogRolls)
187                        {
188                            AddBlog(roll);
189                        }
190                    }
191                }
192            }
193
194            return this.BindControls();
195        }
196
197        /// <summary>
198        /// Parses the processed RSS items and returns the HTML
199        /// </summary>
200        /// <returns>
201        /// The output.
202        /// </returns>
203        private HtmlGenericControl BindControls()
204        {
205            var ul = new HtmlGenericControl("ul");
206            ul.Attributes.Add("class", "xoxo");
207            foreach (var item in Items)
208            {
209                HtmlAnchor feedAnchor = null;
210
211                if (this.ShowRssIcon)
212                {
213                    feedAnchor = new HtmlAnchor { HRef = item.RollItem.FeedUrl.AbsoluteUri };
214
215                    using (var image = new HtmlImage
216                        {
217                            Src = string.Format("{0}pics/rssButton.png", Utils.ApplicationRelativeWebRoot), Alt = string.Format("RSS feed for {0}", item.RollItem.Title)
218                        })
219                    {
220                        feedAnchor.Controls.Add(image);
221                    }
222                }
223
224                var webAnchor = new HtmlAnchor
225                {
226                    HRef = item.RollItem.BlogUrl.AbsoluteUri,
227                    InnerHtml = EnsureLength(item.RollItem.Title)
228                };
229
230                if (this.DescriptionToolTip)
231                {
232                    webAnchor.Attributes["title"] = item.RollItem.Description;
233                }
234
235                if (!String.IsNullOrEmpty(item.RollItem.Xfn))
236                {
237                    webAnchor.Attributes["rel"] = item.RollItem.Xfn;
238                }
239
240                using (var li = new HtmlGenericControl("li"))
241                {
242                    if (null != feedAnchor)
243                    {
244                        li.Controls.Add(feedAnchor);
245                    }
246
247                    li.Controls.Add(webAnchor);
248
249                    AddRssChildItems(item, li);
250                    ul.Controls.Add(li);
251                }
252            }
253
254            return ul;
255        }
256
257        /// <summary>
258        /// Adds the RSS child items.
259        /// </summary>
260        /// <param name="item">The blog request item.</param>
261        /// <param name="li">The list item.</param>
262        private static void AddRssChildItems(BlogRequest item, HtmlGenericControl li)
263        {
264            if (item.ItemTitles.Count <= 0 || BlogSettings.Instance.BlogrollVisiblePosts <= 0)
265            {
266                return;
267            }
268
269            using (var div = new HtmlGenericControl("ul"))
270            {
271                for (var i = 0; i < item.ItemTitles.Count; i++)
272                {
273                    if (i >= BlogSettings.Instance.BlogrollVisiblePosts)
274                    {
275                        break;
276                    }
277
278                    var subLi = new HtmlGenericControl("li");
279                    using (var a = new HtmlAnchor
280                        {
281                            HRef = item.ItemLinks[i], Title = HttpUtility.HtmlEncode(item.ItemTitles[i]), InnerHtml = EnsureLength(item.ItemTitles[i])
282                        })
283                    {
284                        subLi.Controls.Add(a);
285                    }
286
287                    div.Controls.Add(subLi);
288                }
289
290                li.Controls.Add(div);
291            }
292        }
293
294        /// <summary>
295        /// Ensures that the name is no longer than the MaxLength.
296        /// </summary>
297        /// <param name="textToShorten">
298        /// The text To Shorten.
299        /// </param>
300        /// <returns>
301        /// The ensure length.
302        /// </returns>
303        private static string EnsureLength(string textToShorten)
304        {
305            return textToShorten.Length > BlogSettings.Instance.BlogrollMaxLength
306                       ? string.Format("{0}...", textToShorten.Substring(0, BlogSettings.Instance.BlogrollMaxLength).Trim())
307                       : HttpUtility.HtmlEncode(textToShorten);
308        }
309
310        /// <summary>
311        /// Adds a blog to the item collection and start retrieving the blogs.
312        /// </summary>
313        /// <param name="br">
314        /// The blogroll item.
315        /// </param>
316        private static void AddBlog(BlogRollItem br)
317        {
318            var affected = Items.FirstOrDefault(r => r.RollItem.Equals(br));
319            if (affected != null)
320            {
321                return;
322            }
323
324            var req = (HttpWebRequest)WebRequest.Create(br.FeedUrl);
325            req.Credentials = CredentialCache.DefaultNetworkCredentials;
326
327            var blogRequest = new BlogRequest(br, req);
328            Items.Add(blogRequest);
329
330            GetRequestData data = new GetRequestData()
331            {
332                BlogInstanceId = Blog.CurrentInstance.Id,
333                BlogRequest = blogRequest
334            };
335
336            req.BeginGetResponse(ProcessResponse, data);
337        }
338
339        /// <summary>
340        /// Gets the request and processes the response.
341        /// </summary>
342        /// <param name="async">
343        /// The async result.
344        /// </param>
345        private static void ProcessResponse(IAsyncResult asyncResult)
346        {
347            GetRequestData data = (GetRequestData)asyncResult.AsyncState;
348            Blog.InstanceIdOverride = data.BlogInstanceId;
349            var blogReq = data.BlogRequest;
350
351            try
352            {
353                using (var response = (HttpWebResponse)blogReq.Request.EndGetResponse(asyncResult))
354                {
355                    var doc = new XmlDocument();
356                    var responseStream = response.GetResponseStream();
357                    if (responseStream != null)
358                    {
359                        doc.Load(responseStream);
360                    }
361
362                    var nodes = doc.SelectNodes("rss/channel/item");
363                    if (nodes != null)
364                    {
365                        foreach (XmlNode node in nodes)
366                        {
367                            var titleNode = node.SelectSingleNode("title");
368                            var linkNode = node.SelectSingleNode("link");
369                            if (titleNode == null || linkNode == null)
370                            {
371                                continue;
372                            }
373
374                            var title = titleNode.InnerText;
375                            var link = linkNode.InnerText;
376
377                            // var date = DateTime.Now;
378                            // if (node.SelectSingleNode("pubDate") != null)
379                            // {
380                            //     date = DateTime.Parse(node.SelectSingleNode("pubDate").InnerText);
381                            // }
382                            blogReq.ItemTitles.Add(title);
383                            blogReq.ItemLinks.Add(link);
384                        }
385                    }
386                }
387            }
388            catch (Exception ex)
389            {
390                Utils.Log("App_Code.Controls.Blogroll.ProcessRequest", ex);
391            }
392        }
393
394        #endregion
395
396        /// <summary>
397        /// Data used during the async HTTP request for blogrolls.
398        /// </summary>
399        private class GetRequestData
400        {
401            public Guid BlogInstanceId { get; set; }
402            public BlogRequest BlogRequest { get; set; }
403        }
404    }
405
406    /// <summary>
407    /// The blog request.
408    /// </summary>
409    internal class BlogRequest
410    {
411        /// <summary>
412        /// Gets or sets the roll item.
413        /// </summary>
414        /// <value>The roll item.</value>
415        internal BlogRollItem RollItem { get; set; }
416
417        /// <summary>
418        /// Gets or sets the request.
419        /// </summary>
420        /// <value>The request.</value>
421        internal HttpWebRequest Request { get; set; }
422
423        /// <summary>
424        /// Gets or sets the item titles.
425        /// </summary>
426        /// <value>The item titles.</value>
427        internal List<string> ItemTitles { get; set; }
428
429        /// <summary>
430        /// Gets or sets the item links.
431        /// </summary>
432        /// <value>The item links.</value>
433        internal List<string> ItemLinks { get; set; }
434
435        /// <summary>
436        /// Initializes a new instance of the <see cref="BlogRequest"/> class.
437        /// </summary>
438        /// <param name="rollItem">
439        /// The roll item.
440        /// </param>
441        /// <param name="request">
442        /// The request.
443        /// </param>
444        internal BlogRequest(BlogRollItem rollItem, HttpWebRequest request)
445        {
446            this.ItemTitles = new List<string>();
447            this.ItemLinks = new List<string>();
448            this.RollItem = rollItem;
449            this.Request = request;
450        }
451    }
452}