#region Using using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Globalization; using System.IO; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Data; using BlogEngine.Core; using BlogEngine.Core.Web.Controls; #endregion public partial class User_controls_CommentView : UserControl, ICallbackEventHandler { #region ICallbackEventHandler Members private string _Callback; /// /// Returns the results of a callback event that targets a control. /// /// The result of the callback. public string GetCallbackResult() { return _Callback; } /// /// Processes a callback event that targets a control. /// /// A string that represents an event argument to pass to the event handler. public void RaiseCallbackEvent(string eventArgument) { if (!BlogSettings.Instance.IsCommentsEnabled) return; string[] args = eventArgument.Split(new string[] { "-|-" }, StringSplitOptions.None); string author = args[0]; string email = args[1]; string website = args[2]; string country = args[3]; string content = args[4]; bool notify = bool.Parse(args[5]); bool isPreview = bool.Parse(args[6]); string sentCaptcha = args[7]; //If there is no "reply to" comment, args[8] is empty Guid replyToCommentID = String.IsNullOrEmpty(args[8]) ? Guid.Empty : new Guid(args[8]); string avatar = args[9]; string storedCaptcha = hfCaptcha.Value; if (sentCaptcha != storedCaptcha) return; Comment comment = new Comment(); comment.Id = Guid.NewGuid(); comment.ParentId = replyToCommentID; comment.Author = Server.HtmlEncode(author); comment.Email = email; comment.Content = Server.HtmlEncode(content); comment.IP = Request.UserHostAddress; comment.Country = country; comment.DateCreated = DateTime.Now; comment.Parent = Post; comment.IsApproved = !BlogSettings.Instance.EnableCommentsModeration; comment.Avatar = avatar.Trim(); if (Page.User.Identity.IsAuthenticated) comment.IsApproved = true; if (website.Trim().Length > 0) { if (!website.ToLowerInvariant().Contains("://")) website = "http://" + website; Uri url; if (Uri.TryCreate(website, UriKind.Absolute, out url)) comment.Website = url; } if (!isPreview) { if (notify && !Post.NotificationEmails.Contains(email)) Post.NotificationEmails.Add(email); else if (!notify && Post.NotificationEmails.Contains(email)) Post.NotificationEmails.Remove(email); Post.AddComment(comment); SetCookie(author, email, website, country); } string path = Utils.RelativeWebRoot + "themes/" + BlogSettings.Instance.Theme + "/CommentView.ascx"; CommentViewBase control = (CommentViewBase)LoadControl(path); control.Comment = comment; control.Post = Post; using (StringWriter sw = new StringWriter()) { control.RenderControl(new HtmlTextWriter(sw)); _Callback = sw.ToString(); } } #endregion protected string NameInputId = string.Empty; protected string DefaultName = string.Empty; protected void Page_Load(object sender, EventArgs e) { NameInputId = "txtName" + DateTime.Now.Ticks.ToString(); if (Post == null) Response.Redirect(Utils.RelativeWebRoot); if (!Page.IsPostBack && !Page.IsCallback) { if (Page.User.Identity.IsAuthenticated) { if (Request.QueryString["deletecomment"] != null) DeleteComment(); if (Request.QueryString["deletecommentandchildren"] != null) DeleteCommentAndChildren(); if (!string.IsNullOrEmpty(Request.QueryString["approvecomment"])) ApproveComment(); if (!string.IsNullOrEmpty(Request.QueryString["approveallcomments"])) ApproveAllComments(); } string path = Utils.RelativeWebRoot + "themes/" + BlogSettings.Instance.Theme + "/CommentView.ascx"; if (NestingSupported) { // newer, nested comments AddNestedComments(path, Post.NestedComments, phComments); } else { // old, non nested code //Add approved Comments foreach (Comment comment in Post.Comments) { if (comment.IsApproved || !BlogSettings.Instance.EnableCommentsModeration) { CommentViewBase control = (CommentViewBase)LoadControl(path); control.Comment = comment; control.Post = Post; phComments.Controls.Add(control); } } //Add unapproved comments if (Page.User.Identity.IsAuthenticated) { foreach (Comment comment in Post.Comments) { if (!comment.IsApproved) { CommentViewBase control = (CommentViewBase)LoadControl(path); control.Comment = comment; control.Post = Post; phComments.Controls.Add(control); } } } } if (BlogSettings.Instance.IsCommentsEnabled) { if (!Post.IsCommentsEnabled || (BlogSettings.Instance.DaysCommentsAreEnabled > 0 && Post.DateCreated.AddDays(BlogSettings.Instance.DaysCommentsAreEnabled) < DateTime.Now.Date)) { phAddComment.Visible = false; lbCommentsDisabled.Visible = true; } BindCountries(); GetCookie(); hfCaptcha.Value = Guid.NewGuid().ToString(); } else { phAddComment.Visible = false; } //InititializeCaptcha(); } Page.ClientScript.GetCallbackEventReference(this, "arg", null, string.Empty); } private void AddNestedComments(string path, List nestedComments, PlaceHolder phComments) { foreach (Comment comment in nestedComments) { CommentViewBase control = (CommentViewBase)LoadControl(path); if (comment.IsApproved || !BlogSettings.Instance.EnableCommentsModeration || (!comment.IsApproved && Page.User.Identity.IsAuthenticated)) { control.Comment = comment; control.Post = Post; if (comment.Comments.Count > 0) { // find the next placeholder and add the subcomments to it PlaceHolder phSubComments = control.FindControl("phSubComments") as PlaceHolder; if (phSubComments != null) { AddNestedComments(path, comment.Comments, phSubComments); } } phComments.Controls.Add(control); } } } private void ApproveComment() { foreach (Comment comment in Post.NotApprovedComments) { if (comment.Id == new Guid(Request.QueryString["approvecomment"])) { Post.ApproveComment(comment); int index = Request.RawUrl.IndexOf("?"); string url = Request.RawUrl.Substring(0, index); Response.Redirect(url, true); } } } private void ApproveAllComments() { Post.ApproveAllComments(); int index = Request.RawUrl.IndexOf("?"); string url = Request.RawUrl.Substring(0, index); Response.Redirect(url, true); } private void DeleteComment() { foreach (Comment comment in Post.Comments) { if (comment.Id == new Guid(Request.QueryString["deletecomment"])) { Post.RemoveComment(comment); int index = Request.RawUrl.IndexOf("?"); string url = Request.RawUrl.Substring(0, index) + "#comment"; Response.Redirect(url, true); } } } private void DeleteCommentAndChildren() { foreach (Comment comment in Post.Comments) { if (comment.Id == new Guid(Request.QueryString["deletecommentandchildren"])) { // collect comments to delete first so the Nesting isn't lost List commentsToDelete = new List(); CollectCommentToDelete(comment, commentsToDelete); foreach (Comment commentToDelete in commentsToDelete) Post.RemoveComment(commentToDelete); int index = Request.RawUrl.IndexOf("?"); string url = Request.RawUrl.Substring(0, index) + "#comment"; Response.Redirect(url, true); } } } private void CollectCommentToDelete(Comment comment, List commentsToDelete) { commentsToDelete.Add(comment); // recursive collection foreach (Comment subComment in comment.Comments) CollectCommentToDelete(subComment, commentsToDelete); } /// /// Binds the country dropdown list with countries retrieved /// from the .NET Framework. /// public void BindCountries() { StringDictionary dic = new StringDictionary(); List col = new List(); foreach (CultureInfo ci in CultureInfo.GetCultures(CultureTypes.SpecificCultures)) { RegionInfo ri = new RegionInfo(ci.Name); if (!dic.ContainsKey(ri.EnglishName)) dic.Add(ri.EnglishName, ri.TwoLetterISORegionName.ToLowerInvariant()); if (!col.Contains(ri.EnglishName)) col.Add(ri.EnglishName); } // Add custom cultures if (!dic.ContainsValue("bd")) { dic.Add("Bangladesh", "bd"); col.Add("Bangladesh"); } col.Sort(); ddlCountry.Items.Add(new ListItem("[Not specified]", "")); foreach (string key in col) { ddlCountry.Items.Add(new ListItem(key, dic[key])); } if (ddlCountry.SelectedIndex == 0) { ddlCountry.SelectedValue = ResolveRegion().TwoLetterISORegionName.ToLowerInvariant(); this.SetFlagImageUrl(); } } /// /// Resolves the region based on the browser language. /// public static RegionInfo ResolveRegion() { string[] languages = HttpContext.Current.Request.UserLanguages; if (languages == null || languages.Length == 0) return new RegionInfo(CultureInfo.CurrentCulture.LCID); try { string language = languages[0].ToLowerInvariant().Trim(); CultureInfo culture = CultureInfo.CreateSpecificCulture(language); return new RegionInfo(culture.LCID); } catch (ArgumentException) { try { return new RegionInfo(CultureInfo.CurrentCulture.LCID); } catch (ArgumentException) { // the googlebot sometimes gives a culture LCID of 127 which is invalid // so assume US english if invalid LCID return new RegionInfo(1033); } } } private void SetFlagImageUrl() { if (!string.IsNullOrEmpty(ddlCountry.SelectedValue)) { imgFlag.ImageUrl = Utils.RelativeWebRoot + "pics/flags/" + ddlCountry.SelectedValue + ".png"; } else { imgFlag.ImageUrl = Utils.RelativeWebRoot + "pics/pixel.png"; } } #region Cookies /// /// Sets a cookie with the entered visitor information /// so it can be prefilled on next visit. /// private void SetCookie(string name, string email, string website, string country) { HttpCookie cookie = new HttpCookie("comment"); cookie.Expires = DateTime.Now.AddMonths(24); cookie.Values.Add("name", Server.UrlEncode(name.Trim())); cookie.Values.Add("email", email.Trim()); cookie.Values.Add("url", website.Trim()); cookie.Values.Add("country", country); Response.Cookies.Add(cookie); } /// /// Gets the cookie with visitor information if any is set. /// Then fills the contact information fields in the form. /// private void GetCookie() { HttpCookie cookie = Request.Cookies["comment"]; try { if (cookie != null) { DefaultName = Server.UrlDecode(cookie.Values["name"]); txtEmail.Text = cookie.Values["email"]; txtWebsite.Text = cookie.Values["url"]; ddlCountry.SelectedValue = cookie.Values["country"]; this.SetFlagImageUrl(); } else if (Page.User.Identity.IsAuthenticated) { MembershipUser user = Membership.GetUser(); DefaultName = user.UserName; txtEmail.Text = user.Email; txtWebsite.Text = Request.Url.Host; } } catch (Exception) { // Couldn't retrieve info on the visitor/user } } #endregion #region Protected methods and properties private Post _Post; /// /// Gets or sets the post from which the comments are parsed. /// public Post Post { get { return _Post; } set { _Post = value; } } /// /// Displays a delete link to visitors that is authenticated /// using the default membership provider. /// /// The id of the comment. protected string AdminLink(string id) { if (Page.User.Identity.IsAuthenticated) { StringBuilder sb = new StringBuilder(); foreach (Comment comment in Post.Comments) { if (comment.Id.ToString() == id) sb.AppendFormat(" | {0}", comment.Email); } string confirmDelete = "Are you sure you want to delete the comment?"; sb.AppendFormat(" | {2}", id.ToString(), confirmDelete, "Delete"); return sb.ToString(); } return string.Empty; } /// /// Displays the Gravatar image that matches the specified email. /// protected string Gravatar(string email, string name, int size) { if (email.Contains("://")) return string.Format( "\"{1}\"", name, email); //http://www.artviper.net/screenshots/screener.php?&url={0}&h={1}&w={1} MD5 md5 = new MD5CryptoServiceProvider(); byte[] result = md5.ComputeHash(Encoding.ASCII.GetBytes(email)); StringBuilder hash = new StringBuilder(); for (int i = 0; i < result.Length; i++) hash.Append(result[i].ToString("x2")); StringBuilder image = new StringBuilder(); image.Append("\"\""); return image.ToString(); } /// /// Displays BBCodes dynamically loaded from settings. /// protected string BBCodes() { try { string retVal = string.Empty; string title = string.Empty; string code = string.Empty; ExtensionSettings settings = ExtensionManager.GetSettings("BBCode"); DataTable table = settings.GetDataTable(); foreach (DataRow row in table.Rows) { code = (string)row["Code"]; title = "[" + code + "][/" + code + "]"; retVal += "" + code + ""; } return retVal; } catch (Exception) { return string.Empty; } } private bool? _nestingSupported = null; public bool NestingSupported { get { if (!_nestingSupported.HasValue) { if (!BlogSettings.Instance.IsCommentNestingEnabled) _nestingSupported = false; else { string path = Utils.RelativeWebRoot + "themes/" + BlogSettings.Instance.Theme + "/CommentView.ascx"; // test comment control for nesting placeholder (for backwards compatibility with older themes) CommentViewBase commentTester = (CommentViewBase)LoadControl(path); PlaceHolder phSubComments = commentTester.FindControl("phSubComments") as PlaceHolder; _nestingSupported = phSubComments != null; } } return _nestingSupported.Value; } } /// /// If true, on comment save will show "awaiting moderation" message /// otherwise "comment saved, thank you.." will be displayd. /// public string ManualModeration { get { if (BlogSettings.Instance.EnableCommentsModeration && BlogSettings.Instance.ModerationType == 0) return "true"; return "false"; } } #endregion }