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