PageRenderTime 50ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/BlogEngine/DotNetSlave.BusinessLogic/Web/Controls/BlogBasePage.cs

#
C# | 472 lines | 268 code | 58 blank | 146 comment | 38 complexity | d9715c2d24ebca3ac7f6aef38f2c4dc3 MD5 | raw file
Possible License(s): LGPL-2.1, Apache-2.0, BSD-3-Clause
  1. namespace BlogEngine.Core.Web.Controls
  2. {
  3. using System;
  4. using System.Globalization;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Web;
  9. using System.Web.UI;
  10. using System.Web.UI.HtmlControls;
  11. /// <summary>
  12. /// All pages in the custom themes as well as pre-defined pages in the root
  13. /// must inherit from this class.
  14. /// </summary>
  15. /// <remarks>
  16. /// The class is responsible for assigning the theme to all
  17. /// derived pages as well as adding RSS, RSD, tracking script
  18. /// and a whole lot more.
  19. /// </remarks>
  20. public abstract class BlogBasePage : Page
  21. {
  22. #region Constants and Fields
  23. /// <summary>
  24. /// The theme.
  25. /// </summary>
  26. private readonly string theme = BlogSettings.Instance.Theme;
  27. #endregion
  28. #region Public Methods
  29. /// <summary>
  30. /// Adds the generic link to the header.
  31. /// </summary>
  32. /// <param name="relation">
  33. /// The relation string.
  34. /// </param>
  35. /// <param name="title">
  36. /// The title string.
  37. /// </param>
  38. /// <param name="href">
  39. /// The href string.
  40. /// </param>
  41. public virtual void AddGenericLink(string relation, string title, string href)
  42. {
  43. this.AddGenericLink(null, relation, title, href);
  44. }
  45. /// <summary>
  46. /// Adds the generic link to the header.
  47. /// </summary>
  48. /// <param name="type">
  49. /// The type string.
  50. /// </param>
  51. /// <param name="relation">
  52. /// The relation string.
  53. /// </param>
  54. /// <param name="title">
  55. /// The title string.
  56. /// </param>
  57. /// <param name="href">
  58. /// The href string.
  59. /// </param>
  60. public virtual void AddGenericLink(string type, string relation, string title, string href)
  61. {
  62. this.Page.Header.Controls.Add(GetGenericLink(type, relation, title, href));
  63. }
  64. /// <summary>
  65. /// Adds a Stylesheet reference to the HTML head tag.
  66. /// </summary>
  67. /// <param name="url">
  68. /// The relative URL.
  69. /// </param>
  70. /// <param name="insertAtFront">
  71. /// If true, inserts in beginning of HTML head tag.
  72. /// </param>
  73. public virtual void AddStylesheetInclude(string url, bool insertAtFront)
  74. {
  75. var link = new HtmlLink();
  76. link.Attributes["type"] = "text/css";
  77. link.Attributes["href"] = url;
  78. link.Attributes["rel"] = "stylesheet";
  79. if (insertAtFront)
  80. {
  81. this.Page.Header.Controls.AddAt(0, link);
  82. }
  83. else
  84. {
  85. this.Page.Header.Controls.Add(link);
  86. }
  87. }
  88. /// <summary>
  89. /// Adds a Stylesheet reference to the HTML head tag.
  90. /// </summary>
  91. /// <param name="url">
  92. /// The relative URL.
  93. /// </param>
  94. public virtual void AddStylesheetInclude(string url)
  95. {
  96. this.AddStylesheetInclude(url, false);
  97. }
  98. #endregion
  99. #region Methods
  100. /// <summary>
  101. /// Adds code to the HTML head section.
  102. /// </summary>
  103. protected virtual void AddCustomCodeToHead()
  104. {
  105. var code = string.Format(
  106. CultureInfo.InvariantCulture,
  107. "{0}<!-- Start custom code -->{0}{1}{0}<!-- End custom code -->{0}",
  108. Environment.NewLine,
  109. BlogSettings.Instance.HtmlHeader);
  110. var control = new LiteralControl(code);
  111. this.Page.Header.Controls.Add(control);
  112. }
  113. /// <summary>
  114. /// Adds the default stylesheet language
  115. /// </summary>
  116. protected virtual void AddDefaultLanguages()
  117. {
  118. this.Response.AppendHeader("Content-Style-Type", "text/css");
  119. this.Response.AppendHeader("Content-Script-Type", "text/javascript");
  120. }
  121. /// <summary>
  122. /// Add global style sheets before any custom css
  123. /// </summary>
  124. protected virtual void AddGlobalStyles()
  125. {
  126. // add styles in the ~/Styles folder to the page header
  127. var s = Path.Combine(HttpContext.Current.Server.MapPath(Utils.ApplicationRelativeWebRoot), "Styles");
  128. var fileEntries = Directory.GetFiles(s);
  129. foreach (var fileName in
  130. fileEntries.Where(fileName => fileName.EndsWith(".css", StringComparison.OrdinalIgnoreCase)))
  131. {
  132. this.AddStylesheetInclude(
  133. string.Format("{0}Styles/{1}", Utils.ApplicationRelativeWebRoot, Utils.ExtractFileNameFromPath(fileName)), true);
  134. }
  135. }
  136. /// <summary>
  137. /// Adds the content-type meta tag to the header.
  138. /// </summary>
  139. protected virtual void AddMetaContentType()
  140. {
  141. var meta = new HtmlMeta
  142. {
  143. HttpEquiv = "content-type",
  144. Content =
  145. string.Format(
  146. "{0}; charset={1}", this.Response.ContentType, this.Response.ContentEncoding.HeaderName)
  147. };
  148. this.Page.Header.Controls.Add(meta);
  149. }
  150. /// <summary>
  151. /// Add a meta tag to the page's header.
  152. /// </summary>
  153. /// <param name="name">
  154. /// The tag name.
  155. /// </param>
  156. /// <param name="value">
  157. /// The tag value.
  158. /// </param>
  159. protected virtual void AddMetaTag(string name, string value)
  160. {
  161. if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(value))
  162. {
  163. return;
  164. }
  165. var meta = new HtmlMeta { Name = name, Content = value };
  166. this.Page.Header.Controls.Add(meta);
  167. }
  168. /// <summary>
  169. /// Adds a JavaScript to the bottom of the page at runtime.
  170. /// </summary>
  171. /// <remarks>
  172. /// You must add the script tags to the BlogSettings.Instance.TrackingScript.
  173. /// </remarks>
  174. protected virtual void AddTrackingScript()
  175. {
  176. var sb = new StringBuilder();
  177. if (BlogSettings.Instance.ModerationType == BlogSettings.Moderation.Disqus)
  178. {
  179. sb.Append("<script type=\"text/javascript\"> \n");
  180. sb.Append("//<![CDATA[ \n");
  181. sb.Append("(function() { ");
  182. sb.Append("var links = document.getElementsByTagName('a'); ");
  183. sb.Append("var query = '?'; ");
  184. sb.Append("for(var i = 0; i < links.length; i++) { ");
  185. sb.Append("if(links[i].href.indexOf('#disqus_thread') >= 0) { ");
  186. sb.Append("query += 'url' + i + '=' + encodeURIComponent(links[i].href) + '&'; ");
  187. sb.Append("}}");
  188. sb.Append("document.write('<script charset=\"utf-8\" type=\"text/javascript\" src=\"http://disqus.com/forums/");
  189. sb.Append(BlogSettings.Instance.DisqusWebsiteName);
  190. sb.Append("/get_num_replies.js' + query + '\"></' + 'script>'); ");
  191. sb.Append("})(); \n");
  192. sb.Append("//]]> \n");
  193. sb.Append("</script> \n");
  194. }
  195. if (!string.IsNullOrEmpty(BlogSettings.Instance.TrackingScript))
  196. {
  197. sb.Append(BlogSettings.Instance.TrackingScript);
  198. }
  199. var s = sb.ToString();
  200. if (!string.IsNullOrEmpty(s))
  201. {
  202. this.ClientScript.RegisterStartupScript(this.GetType(), "tracking", string.Format("\n{0}", s), false);
  203. }
  204. }
  205. /// <summary>
  206. /// Finds all stylesheets in the header and changes the
  207. /// path so it points to css.axd which removes the whitespace.
  208. /// </summary>
  209. protected virtual void CompressCss()
  210. {
  211. if (this.Request.QueryString["theme"] != null)
  212. {
  213. return;
  214. }
  215. foreach (Control control in this.Page.Header.Controls)
  216. {
  217. var c = control as HtmlControl;
  218. if (c == null || c.Attributes["type"] == null ||
  219. !c.Attributes["type"].Equals("text/css", StringComparison.OrdinalIgnoreCase))
  220. {
  221. continue;
  222. }
  223. // if a CSS filename has ".min.css" in it, it is probably an already
  224. // minified CSS file -- skip these.
  225. if (c.Attributes["href"].StartsWith("http://") ||
  226. c.Attributes["href"].IndexOf(".min.css", StringComparison.OrdinalIgnoreCase) != -1)
  227. {
  228. continue;
  229. }
  230. var url = string.Format("{0}themes/{1}/css.axd?name={2}", Utils.ApplicationRelativeWebRoot, this.theme, c.Attributes["href"]);
  231. c.Attributes["href"] = url.Replace(".css", string.Format("{0}.css", BlogSettings.Instance.Version()));
  232. c.EnableViewState = false;
  233. }
  234. }
  235. /// <summary>
  236. /// Creates and returns a generic link control.
  237. /// </summary>
  238. /// <param name="type">
  239. /// The HtmlLink's "type" attribute value.
  240. /// </param>
  241. /// <param name="relation">
  242. /// The HtmlLink's "rel" attribute value.
  243. /// </param>
  244. /// <param name="title">
  245. /// The HtmlLink's "title" attribute value.
  246. /// </param>
  247. /// <param name="href">
  248. /// The HtmlLink's "href" attribute value.
  249. /// </param>
  250. private static HtmlLink GetGenericLink(string type, string relation, string title, string href)
  251. {
  252. var link = new HtmlLink();
  253. if (type != null) { link.Attributes["type"] = type; }
  254. link.Attributes["rel"] = relation;
  255. link.Attributes["title"] = title;
  256. link.Attributes["href"] = href;
  257. return link;
  258. }
  259. /// <summary>
  260. /// Raises the <see cref="E:System.Web.UI.TemplateControl.Error"></see> event.
  261. /// </summary>
  262. /// <param name="e">
  263. /// An <see cref="T:System.EventArgs"></see> that contains the event data.
  264. /// </param>
  265. protected override void OnError(EventArgs e)
  266. {
  267. var ctx = HttpContext.Current;
  268. var exception = ctx.Server.GetLastError();
  269. if (exception != null && exception.Message.Contains("callback"))
  270. {
  271. // This is a robot spam attack so we send it a 404 status to make it go away.
  272. ctx.Response.StatusCode = 404;
  273. ctx.Server.ClearError();
  274. Comment.OnSpamAttack();
  275. }
  276. base.OnError(e);
  277. }
  278. /// <summary>
  279. /// Raises the <see cref="E:System.Web.UI.Control.Load"/> event.
  280. /// Adds links and javascript to the HTML header tag.
  281. /// </summary>
  282. /// <param name="e">The <see cref="T:System.EventArgs"/> object that contains the event data.</param>
  283. protected override void OnLoad(EventArgs e)
  284. {
  285. base.OnLoad(e);
  286. string relativeWebRoot = Utils.RelativeWebRoot;
  287. Uri absoluteWebRoot = Utils.AbsoluteWebRoot;
  288. string instanceName = BlogSettings.Instance.Name;
  289. if (!this.Page.IsCallback)
  290. {
  291. // Links
  292. this.AddGenericLink("contents", "Archive", string.Format("{0}archive.aspx", relativeWebRoot));
  293. this.AddGenericLink("start", instanceName, relativeWebRoot);
  294. this.AddGenericLink("application/rdf+xml", "meta", "SIOC", string.Format("{0}sioc.axd", absoluteWebRoot));
  295. this.AddGenericLink("application/apml+xml", "meta", "APML", string.Format("{0}apml.axd", absoluteWebRoot));
  296. this.AddGenericLink("application/rdf+xml", "meta", "FOAF", string.Format("{0}foaf.axd", absoluteWebRoot));
  297. if (string.IsNullOrEmpty(BlogSettings.Instance.AlternateFeedUrl))
  298. {
  299. this.AddGenericLink(
  300. "application/rss+xml",
  301. "alternate",
  302. string.Format("{0} (RSS)", instanceName),
  303. string.Format("{0}?format=rss", Utils.FeedUrl));
  304. this.AddGenericLink(
  305. "application/atom+xml",
  306. "alternate",
  307. string.Format("{0} (ATOM)", instanceName),
  308. string.Format("{0}?format=atom", Utils.FeedUrl));
  309. }
  310. else
  311. {
  312. this.AddGenericLink("application/rss+xml", "alternate", instanceName, Utils.FeedUrl);
  313. }
  314. this.AddGenericLink("application/rsd+xml", "edituri", "RSD", string.Format("{0}rsd.axd", absoluteWebRoot));
  315. this.AddMetaContentType();
  316. this.AddDefaultLanguages();
  317. // this.AddLocalizationKeys();
  318. this.AddGlobalStyles();
  319. Utils.AddFolderJavaScripts(this, "Scripts", true);
  320. Utils.AddJavaScriptResourcesToPage(this);
  321. Utils.AddFolderJavaScripts(this, string.Format("themes/{0}", this.theme), true);
  322. if (BlogSettings.Instance.EnableOpenSearch)
  323. {
  324. this.AddGenericLink(
  325. "application/opensearchdescription+xml",
  326. "search",
  327. instanceName,
  328. string.Format("{0}opensearch.axd", absoluteWebRoot));
  329. }
  330. if (!string.IsNullOrEmpty(BlogSettings.Instance.HtmlHeader))
  331. {
  332. this.AddCustomCodeToHead();
  333. }
  334. this.AddTrackingScript();
  335. }
  336. if (Security.IsAuthorizedTo(Rights.ManageWidgets))
  337. {
  338. Utils.AddJavaScriptInclude(this, string.Format("{0}admin/widget.js", Utils.ApplicationRelativeWebRoot), true, false, true);
  339. }
  340. if (BlogSettings.Instance.RemoveWhitespaceInStyleSheets)
  341. {
  342. this.CompressCss();
  343. }
  344. }
  345. /// <summary>
  346. /// Raises the <see cref="E:System.Web.UI.Page.PreInit"/> event at the beginning of page initialization.
  347. /// Assignes the selected theme to the pages.
  348. /// </summary>
  349. /// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
  350. protected override void OnPreInit(EventArgs e)
  351. {
  352. bool allowViewing = false;
  353. // - To prevent authenticated users from accessing the site, you would assign
  354. // that user to a role that does not have the right to ViewPublicPosts.
  355. // - To prevent unauthenticated users from accessing the site, remove
  356. // the ViewPublicPosts from the Anonymous role.
  357. // - If the user is authenticated, but hasn't been assigned to any roles, allow
  358. // them to access the site.
  359. // - Even though we allow authenticated users without any roles to access the
  360. // site, the user will still usually not be able to view any published posts.
  361. // It is ideal that all users are assigned to a role, even if that role has
  362. // minimal rights such as ViewPublicPosts.
  363. if (Security.IsAuthorizedTo(Rights.ViewPublicPosts))
  364. allowViewing = true;
  365. else if (Security.IsAuthenticated && Security.GetCurrentUserRoles().Length == 0)
  366. allowViewing = true;
  367. if (!allowViewing)
  368. {
  369. this.Response.Redirect(string.Format("{0}Account/login.aspx", Utils.RelativeWebRoot));
  370. }
  371. this.MasterPageFile = string.Format("{0}themes/{1}/site.master", Utils.ApplicationRelativeWebRoot, BlogSettings.Instance.GetThemeWithAdjustments(null));
  372. base.OnPreInit(e);
  373. if (this.Page.IsPostBack || string.IsNullOrEmpty(this.Request.QueryString["deletepost"]))
  374. {
  375. return;
  376. }
  377. var post = Post.GetPost(new Guid(this.Request.QueryString["deletepost"]));
  378. if (post == null || !post.CanUserDelete)
  379. {
  380. return;
  381. }
  382. post.Delete();
  383. post.Save();
  384. this.Response.Redirect(Utils.RelativeWebRoot);
  385. }
  386. /// <summary>
  387. /// Raises the <see cref="E:System.Web.UI.Page.PreRenderComplete"></see> event after
  388. /// the <see cref="M:System.Web.UI.Page.OnPreRenderComplete(System.EventArgs)"></see> event and before the page is rendered.
  389. /// </summary>
  390. /// <param name="e">
  391. /// An <see cref="T:System.EventArgs"></see> that contains the event data.
  392. /// </param>
  393. protected override void OnPreRenderComplete(EventArgs e)
  394. {
  395. base.OnPreRenderComplete(e);
  396. if (BlogSettings.Instance.UseBlogNameInPageTitles)
  397. {
  398. this.Page.Title = string.Format("{0} | {1}", BlogSettings.Instance.Name, this.Page.Title);
  399. }
  400. }
  401. /// <summary>
  402. /// Initializes the <see cref="T:System.Web.UI.HtmlTextWriter"></see> object and calls on the child
  403. /// controls of the <see cref="T:System.Web.UI.Page"></see> to render.
  404. /// </summary>
  405. /// <param name="writer">
  406. /// The <see cref="T:System.Web.UI.HtmlTextWriter"></see> that receives the page content.
  407. /// </param>
  408. protected override void Render(HtmlTextWriter writer)
  409. {
  410. base.Render(new RewriteFormHtmlTextWriter(writer));
  411. }
  412. #endregion
  413. }
  414. }