Adding, Editing and Deleting Comments
12 Jun 2009 @ 08:53AM

Updated: 27 Jan 2010 @ 08:54AM
With the comment displaying code completed, we're wrapping up the last bits of our blogging app. We need to add the ability for people to post comments, and then the ability for admins to edit and delete comments. That should be fairly easy after all we've done up to this point. We'll begin with the code to catch add and edit requests.
int commentID = 0;
...
case "postComment":
case "editComment":
     if (getVariable("option", var.GET) == "editComment" && accessLevel < 255)
     {
          break;
     }
     try
     {
          blogID = Convert.ToInt16(getVariable("blogID", var.GET));
     }
     catch { }
     try
     {
          commentID = Convert.ToInt16(getVariable("commentID", var.GET));
     }
     catch { }
     showblogs = false;
     content += editComment(blogID, commentID, getVariable("option", var.GET));
     break;

Here we look for a blogID and a commentID, both of which may (or may not) be passed depending on what it is we're doing. I declated the commentID variable before our switch statement. We then call the editComment() method. This allows us to both add a new comment (either as a response to a blog or as a response to another comment) as well as edit an existing comment. Since the interface would be the same regardess of whether we're adding a new comment or editing an existing one, it makes sense to merge them into the same method.
Comments (0)
private string editComment(int blogID, int commentID, string type)
{
     string content = null, select = null;
     string commentDisplayName = (loggedIn) ? displayName : null;
     string commentText = null;
     string userEmail = (showEmail) ? email : null;
     DateTime commentDate = DateTime.Now;
     SqlDataReader dr;

     if (commentID == 0 && type == "postComment")
     {
          //replying to a blog, so spit out the blog as a reference
          content += showBlogs(blogID);
     }
     else if(type == "postComment")
     {
          //replying to a comment, so spit out the comment as a reference
          select = @"SELECT displayName, parentComment, name, commentText, commentDate,
                          CASE "
+
                    "WHEN showEmail = 1 THEN ' (<a href="mailto: ' + email + '">' + email + '</a>)' " +
                    "ELSE '' " +
               @"END AS 'email', a.userID
           FROM comments a
               LEFT JOIN users b
                    on a.userID = b.userID
           WHERE commentID = "
+ commentID + @"
           ORDER BY commentDate ASC"
;
          dr = query(select);
          if (dr.HasRows)
          {
               dr.Read();
               content = @"<div style='width: 72%;'><div class='commentContainer'>
                    <div class='comment'>
                         <div class='header'>"
;
               content += (Convert.ToInt16(dr["userID"]) > 0) ? dr["displayName"] : dr["name"];
               content += dr["email"] + " @ " + Convert.ToDateTime(dr["commentDate"]).ToString("hh:mmtt ddMMMyyyy") +
                    @"</div>
                         <div class='text'>"
+ Convert.ToString(dr["commentText"]).Replace("n", "<br>") + @"</div>
                    </div>
               </div></div>"
;
               dr.Dispose();
          }
          else
          {
               dr.Dispose();
               return "<div class='error'>Invalid Comment Specified</div>";
          }
     }

     if (type == "editComment")
     {
          //edit existing comment
          select = @"SELECT displayName, name, commentText, commentDate,
                          CASE "
+
                         "WHEN showEmail = 1 THEN ' (<a href="mailto: ' + email + '">' + email + '</a>)' " +
                         "ELSE '' " +
                    @"END AS 'email', a.userID, blogID
           FROM comments a
               LEFT JOIN users b
                    on a.userID = b.userID
           WHERE commentID="
+ commentID + @"
           ORDER BY commentDate ASC"
;
          dr = query(select);
          if (dr.HasRows)
          {
               dr.Read();
               commentDisplayName = (Convert.ToInt16(dr["userID"]) == 0) ? Convert.ToString(dr["name"]) : Convert.ToString(dr["displayName"]);
               commentText = Convert.ToString(dr["commentText"]);
               commentDate = Convert.ToDateTime(dr["commentDate"]);
               userEmail = Convert.ToString(dr["email"]);
     blogID = Convert.ToInt16(dr["blogID"]);
               dr.Dispose();
          }
          else
          {
               dr.Dispose();
               return "<div class='error'>Error: Invalid Comment Specified</div>";
          }
     }

     content += "<div style='width: 72%;'>";
     content += @"<form method='POST' action='default.aspx' style='display: inline;'><div class='commentContainer'>
          <div class='comment'>
               <div class='header'>"
;
     content += (loggedIn) ? commentDisplayName : "Name: <input name='name' value='" + commentDisplayName + "'>";
     content += userEmail + " @ " + commentDate.ToString("hh:mmtt ddMMMyyyy");
          content += @"</div>
               <div class='text'><textarea name='commentText' style='width: 100%;' rows=5>"
+ commentText + @"</textarea></div>
               <div class='reply'>
                         <input type='hidden' name='option' value='saveComment'>
                         <input type='hidden' name='blogID' value='"
+ blogID + @"'>";
     if(commentID > 0){ content += "<input type='hidden' name='commentID' value='" + commentID + "'>";}     
     content += @"<input type='submit' value='Save'>
                    </form>
                    <form action='default.aspx' method='GET' style='display: inline;'>
                         <input type='hidden' name='option' value='comments'>
                         <input type='hidden' name='blogID' value='"
+ blogID + @"'>
                         <input type='submit' value='Cancel'>
                    </form>
               </div>
          </div>"
;
     content += "</div>";
     return content;
}

First we just initialize all our variables and set some to their default values. We then check to see if the commentID is 0 and the type is postComment. This means we're responding to the root blog post. In that case, we just use our showBlogs() method to spit out the blog the user is responding to. This lets them re-read it as they're writing their comment.

Next we just see if it's a response. Since we handled the commentID=0 case in the first if branch, we know this is a response to a comment. So next we retrieve the comment and spit it out for the user to reference. If this were a real application, I'd write a method that spits out comments, then call it from both here and from the showComments() method. However, I don't want to waste a lot of tutorial space rewriting code we've already written, so let's leave it like this for now. Perhaps we'll address this in the future.

After we address any postComment options, we check to see if this is an editComment request and, if it is, we retrieve all the comment details. Finally we get to the actual comment form. This we pre-populate with either the default values or the edited value, depending on what's appopriate.
Comments (0)
o now it's just a matter of saving a comment after we edit it. For that, we start once more in the Page_Load() method where we catch the request.
case "saveComment":
     try
     {
          blogID = Convert.ToInt16(getVariable("blogID", var.POST));
     }
     catch { }
     try
     {
          commentID = Convert.ToInt16(getVariable("commentID", var.POST));
     }
     catch { }
     if (commentID > 0 && accessLevel < 255)
     {
          break;
     }
     showblogs = false;
     content += saveComment(blogID, commentID);
     content += comments(blogID);
     break;

So we catch the saveComment request in the POST switch/case statement, then try to grab a blogID and commentID. If there is a commentID, that means we're updating an existing comment, so we check to make sure the user is allowed to update comments. We then execute the saveComment() method, then redisplay the comments list for the specified blog.
Comments (0)
Next the saveComment() method.
private string saveComment(int blogID, int commentID)
{
     string content = null, select = null;
     string name = (loggedIn) ? "" : sanitize(getVariable("name", var.POST), clean.DB);
     string commentText = sanitize(getVariable("commentText", var.POST), clean.DB);
     int userUserid = (loggedIn) ? userid : 0;
     SqlDataReader dr;

     //verify the referenced blog exists
     select = "SELECT count(*) FROM blogs WHERE blogID=" + blogID + " AND visible=1";
     dr = query(select);
     dr.Read();
     if (Convert.ToInt16(dr.GetValue(0)) != 1)
     {
          dr.Dispose();
          return "<div class='error'>Invalid BlogID Specified</div>";
     }
     dr.Dispose();

     if (commentID == 0)
     {
          //insert a new comment
          select = @"INSERT INTO comments
               (userID, blogID, parentComment, name, visible, commentText, commentDate)
                    values
               ("
+ userUserid + ", " + blogID + ", " + commentID + ", '" + name + "', 1, '" + commentText + "', getDate())";
          dr = query(select);
          if (dr.RecordsAffected != 1)
          {
               return "<div class='error'>There has been an error adding the comment</div>";
          }
     }
     else
     {
          //update an existing comment
          select = @"UPDATE comments
               SET commentText='"
+ commentText + @"'
               WHERE commentID="
+ commentID;
          dr = query(select);
          if (dr.RecordsAffected != 1)
          {
               return "<div class='error'>There has been an error editing the comment</div>";
          }
     }
     return content;

We begin this method by initializing our variables and retrieving the POSTed values. We then verify that the blog the comment is attached to actually exists and is visible. We don't want people spoofing the blogID and commenting on blogs that don't exist or that aren't visible.

Next we see if it's a new comment or an edit. If it's new, we just insert it. If it's not, we update it. Note that when we update a comment, the only thing that can be changed is the text. What if the user wasn't logged in and chose to put something obscene in their name? At this point there's no way to deal with that except to delete the entire comment.
Comments (0)
So, let's do the deleteComment() method.
case "deleteComment":
     if (accessLevel < 255)
     {
          break;
     }
     try
     {
          blogID = Convert.ToInt16(getVariable("blogID", var.GET));
     }
     catch { }
     try
     {
          commentID = Convert.ToInt16(getVariable("commentID", var.GET));
     }
     catch { }
     showblogs = false;
     content += deleteComment(commentID);
     content += comments(blogID);
     break;

First, please note I made a small change to our mapcomment() method. I altered the delete link to also pass the blogID. That way when a comment is deleted, the screen can refresh to the current blog entry's comments, instead of to the general blog listing. I don't show this change because it was minor, but it will be in the files below. So here I just make sure the user is an admin, then execute the deleteComment() method, then the comments() method to show the comments for the current blog.
Comments (0)
private string deleteComment(int commentID)
{
     string select = "UPDATE comments SET visible=0 WHERE commentID=" + commentID;
     SqlDataReader dr = query(select);
     if (dr.RecordsAffected != 1)
     {
          dr.Dispose();
          return "<div class='error'>There was an error deleting the comment</div>";
     }
     dr.Dispose();
     return null;
}

ompared to some of the stuff we've written, this is ridiculously simple. We just make a select statement to set the visible=0 for the comment and, if we don't get a record affected, we return an error message. Having experimented with this, though, I realize that our comment-displaying method has an error in the select. We're showing all comments, not just visible ones. I've made that change as well... check the files below for the complete code files with basically everything working.

On that subject, I've removed the 'See New Entries' option from our menu. Perhaps we'll add some functionality like that at a later date.

Files To This Point

Next we're going to make some tweaks. There are some problems with our site the way it is that you may or may not have run into yet.
Comments (0)