Yi EungJun 2013-10-07
Remove duplication from CodeComment and PullRequestComment.
1. Rename CodeComment to CommitComment
2. Create new class "CodeComment" and make it CommitComment's superclass.
3. Pull up lots of members from CommitComment to CodeComment.
4. Make PullRequestComment to extend CodeComment.
5. Remove many members of PullRequestComment duplicated with CodeComment.
@e7e246e38c3beeadd382a4c061d08fc1f1aeed19
app/controllers/CodeHistoryApp.java
--- app/controllers/CodeHistoryApp.java
+++ app/controllers/CodeHistoryApp.java
@@ -163,7 +163,7 @@
             return notFound(ErrorViews.NotFound.render("error.notfound", project, null));
         }
 
-        List<CodeComment> comments = CodeComment.find.where().eq("commitId",
+        List<CommitComment> comments = CommitComment.find.where().eq("commitId",
                 commitId).eq("project.id", project.id).findList();
 
         String selectedBranch = request().getQueryString("branch");
@@ -173,7 +173,7 @@
 
     public static Result newComment(String ownerName, String projectName, String commitId)
             throws IOException, ServletException, SVNException {
-        Form<CodeComment> codeCommentForm = new Form<>(CodeComment.class)
+        Form<CommitComment> codeCommentForm = new Form<>(CommitComment.class)
                 .bindFromRequest();
 
         Project project = Project.findByOwnerAndProjectName(ownerName, projectName);
@@ -191,11 +191,11 @@
         }
 
         if (!AccessControl.isProjectResourceCreatable(UserApp.currentUser(), project,
-                ResourceType.CODE_COMMENT)) {
+                ResourceType.COMMIT_COMMENT)) {
             return forbidden(forbidden.render("error.forbidden", project));
         }
 
-        CodeComment codeComment = codeCommentForm.get();
+        CommitComment codeComment = codeCommentForm.get();
         codeComment.project = project;
         codeComment.commitId = commitId;
         codeComment.createdDate = new Date();
@@ -221,7 +221,7 @@
         return toView;
     }
 
-    private static void addNotificationEventForNewComment(Project project, CodeComment codeComment, Call toView) throws IOException, SVNException, ServletException {
+    private static void addNotificationEventForNewComment(Project project, CommitComment codeComment, Call toView) throws IOException, SVNException, ServletException {
         Commit commit = RepositoryService.getRepository(project).getCommit(codeComment.commitId);
         Set<User> watchers = commit.getWatchers(project);
         watchers.addAll(NotificationEvent.getMentionedUsers(codeComment.contents));
@@ -244,7 +244,7 @@
 
     public static Result deleteComment(String ownerName, String projectName, String commitId,
                                        Long id) {
-        CodeComment codeComment = CodeComment.find.byId(id);
+        CommitComment codeComment = CommitComment.find.byId(id);
 
         if (codeComment == null) {
             return notFound(notfound_default.render(request().path()));
 
app/controllers/CommentApp.java (added)
+++ app/controllers/CommentApp.java
@@ -0,0 +1,25 @@
+package controllers;
+
+import models.Project;
+import models.enumeration.ResourceType;
+import models.resource.Resource;
+import play.mvc.Result;
+import play.mvc.Controller;
+
+public class CommentApp extends Controller {
+    public static Result delete(String type, String id) {
+        Resource comment = Resource.get(ResourceType.getValue(type), id);
+
+        if (comment.getType().equals(ResourceType.COMMIT_COMMENT)) {
+            return CodeHistoryApp.deleteComment(comment.getProject().owner,
+                    comment.getProject().name, comment.getContainer().getId(),
+                    Long.valueOf(comment.getId()));
+        }
+
+        if (comment.getType().equals(ResourceType.PULL_REQUEST_COMMENT)) {
+            return PullRequestCommentApp.deleteComment(Long.valueOf(id));
+        }
+
+        return badRequest();
+    }
+}
app/controllers/ProjectApp.java
--- app/controllers/ProjectApp.java
+++ app/controllers/ProjectApp.java
@@ -531,10 +531,10 @@
     }
 
     private static void addCodeCommenters(String commitId, Long projectId, List<User> userList) {
-        List<CodeComment> comments = CodeComment.find.where().eq("commitId",
+        List<CommitComment> comments = CommitComment.find.where().eq("commitId",
                 commitId).eq("project.id", projectId).findList();
 
-        for (CodeComment codeComment : comments) {
+        for (CommitComment codeComment : comments) {
             User commentAuthor = User.findByLoginId(codeComment.authorLoginId);
             if( userList.contains(commentAuthor) ) {
                 userList.remove(commentAuthor);
app/controllers/PullRequestApp.java
--- app/controllers/PullRequestApp.java
+++ app/controllers/PullRequestApp.java
@@ -738,7 +738,7 @@
             return notFound(ErrorViews.NotFound.render("error.notfound", project, null));
         }
 
-        List<CodeComment> comments = CodeComment.find.where().eq("commitId",
+        List<CommitComment> comments = CommitComment.find.where().eq("commitId",
                 commitId).eq("project.id", project.id).findList();
 
         return ok(diff.render(pullRequest, commit, parentCommit, patch, comments));
app/models/CodeComment.java
--- app/models/CodeComment.java
+++ app/models/CodeComment.java
@@ -7,69 +7,42 @@
 import play.db.ebean.Model;
 
 import javax.persistence.Column;
-import javax.persistence.Entity;
 import javax.persistence.Id;
 import javax.persistence.ManyToOne;
-import javax.validation.constraints.Size;
+import javax.persistence.MappedSuperclass;
 import java.beans.Transient;
 import java.util.ArrayList;
 import java.util.Date;
-import java.util.List;
 
-@Entity
+import org.joda.time.Duration;
+import utils.JodaDateUtil;
+
+import javax.validation.constraints.Size;
+
+
+@MappedSuperclass
 public class CodeComment extends Model implements ResourceConvertible, TimelineItem {
     private static final long serialVersionUID = 1L;
     public static final Finder<Long, CodeComment> find = new Finder<>(Long.class, CodeComment.class);
 
     @Id
     public Long id;
-
     @ManyToOne
     public Project project;
-
     public String commitId;
     public String path;
-    public Long line;
+    public Integer line; // FIXME: DB엔 integer가 아닌 bigint로 되어있음.
     public String side;
-
     @Constraints.Required @Column(length = 4000) @Size(max=4000)
     public String contents;
-
     @Constraints.Required
     public Date createdDate;
-
     public Long authorId;
     public String authorLoginId;
     public String authorName;
 
     public CodeComment() {
         createdDate = new Date();
-    }
-
-    public static int count(Project project, String commitId, String path){
-        if(path != null){
-            return CodeComment.find.where()
-                    .eq("project.id", project.id)
-                    .eq("commitId", commitId)
-                    .eq("path", path)
-                    .findRowCount();
-        } else {
-            return CodeComment.find.where()
-                    .eq("project.id", project.id)
-                    .eq("commitId", commitId)
-                    .findRowCount();
-        }
-    }   
-
-    public static int countByCommits(Project project, List<PullRequestCommit> commits) {
-        int count = 0;
-        for(PullRequestCommit commit: commits) {
-            count += CodeComment.find.where().eq("project.id", project.id)
-                                .eq("commitId", commit.getCommitId())
-                                .findRowCount();
-        }
-        
-        return count;
     }
 
     @Transient
@@ -80,40 +53,13 @@
     }
 
     @Override
-    public Resource asResource() {
-        return new Resource() {
-            @Override
-            public String getId() {
-                return id.toString();
-            }
-
-            @Override
-            public Project getProject() {
-                return project;
-            }
-
-            @Override
-            public ResourceType getType() {
-                return ResourceType.CODE_COMMENT;
-            }
-
-            @Override
-            public Long getAuthorId() {
-                return authorId;
-            }
-        };
-    }
-
-    @Override
     public Date getDate() {
         return createdDate;
     }
 
-    public static List<CodeComment> findByCommits(Project project, List<PullRequestCommit> commits) {
-        List<CodeComment> list = new ArrayList<>();
-        for(PullRequestCommit commit: commits) {
-            list.addAll(CodeComment.find.where().eq("project.id", project.id).eq("commitId", commit.getCommitId()).findList());
-        }
-        return list;
+    public Duration ago() {
+        return JodaDateUtil.ago(this.createdDate);
     }
+
+    abstract public Resource asResource();
 }
 
app/models/CommitComment.java (added)
+++ app/models/CommitComment.java
@@ -0,0 +1,82 @@
+package models;
+
+import models.enumeration.ResourceType;
+import models.resource.Resource;
+
+import java.util.List;
+
+import javax.persistence.Entity;
+import javax.validation.constraints.Size;
+
+@Entity
+public class CommitComment extends CodeComment {
+    private static final long serialVersionUID = 1L;
+    public static final Finder<Long, CommitComment> find = new Finder<>(Long.class, CommitComment.class);
+
+    public CommitComment() {
+        super();
+    }
+
+    @Override
+    public Resource asResource() {
+        return new Resource() {
+            @Override
+            public String getId() {
+                return id.toString();
+            }
+
+            @Override
+            public Project getProject() {
+                return project;
+            }
+
+            @Override
+            public ResourceType getType() {
+                return ResourceType.COMMIT_COMMENT;
+            }
+
+            @Override
+            public Long getAuthorId() {
+                return authorId;
+            }
+
+            public void delete() {
+                CommitComment.this.delete();
+            }
+        };
+    }
+
+    public static int count(Project project, String commitId, String path){
+        if(path != null){
+            return CommitComment.find.where()
+                    .eq("project.id", project.id)
+                    .eq("commitId", commitId)
+                    .eq("path", path)
+                    .findRowCount();
+        } else {
+            return CommitComment.find.where()
+                    .eq("project.id", project.id)
+                    .eq("commitId", commitId)
+                    .findRowCount();
+        }
+    }
+
+    public static int countByCommits(Project project, List<PullRequestCommit> commits) {
+        int count = 0;
+        for(PullRequestCommit commit: commits) {
+            count += CommitComment.find.where().eq("project.id", project.id)
+                                .eq("commitId", commit.getCommitId())
+                                .findRowCount();
+        }
+
+        return count;
+    }
+
+    public static List<CodeComment> findByCommits(Project project, List<PullRequestCommit> commits) {
+        List<CodeComment> list = new ArrayList<>();
+        for(PullRequestCommit commit: commits) {
+            list.addAll(CodeComment.find.where().eq("project.id", project.id).eq("commitId", commit.getCommitId()).findList());
+        }
+        return list;
+    }
+}
app/models/Project.java
--- app/models/Project.java
+++ app/models/Project.java
@@ -3,11 +3,8 @@
 import com.avaje.ebean.Ebean;
 import com.avaje.ebean.ExpressionList;
 import com.avaje.ebean.Page;
-import com.avaje.ebean.RawSql;
-import com.avaje.ebean.RawSqlBuilder;
 
 import controllers.EnrollProjectApp;
-import controllers.ProjectApp;
 import controllers.routes;
 import models.enumeration.RequestState;
 import models.enumeration.ResourceType;
@@ -19,7 +16,6 @@
 import org.eclipse.jgit.api.errors.NoHeadException;
 import org.joda.time.Duration;
 import org.tmatesoft.svn.core.SVNException;
-import play.Logger;
 import play.data.validation.Constraints;
 import play.db.ebean.Model;
 import play.db.ebean.Transactional;
@@ -109,7 +105,7 @@
     public List<User> enrolledUsers;
 
     @OneToMany(cascade = CascadeType.REMOVE)
-    public List<CodeComment> codeComments;
+    public List<CommitComment> codeComments;
 
     /**
      * 신규 프로젝트를 생성한다.
app/models/PullRequestComment.java
--- app/models/PullRequestComment.java
+++ app/models/PullRequestComment.java
@@ -7,9 +7,6 @@
 import org.eclipse.jgit.blame.BlameGenerator;
 import org.eclipse.jgit.blame.BlameResult;
 import org.eclipse.jgit.lib.Repository;
-import org.joda.time.Duration;
-import play.data.validation.Constraints;
-import play.db.ebean.Model;
 import playRepository.FileDiff;
 import playRepository.GitRepository;
 import playRepository.RepositoryService;
@@ -23,51 +20,17 @@
 import java.util.List;
 
 @Entity
-public class PullRequestComment extends Model implements ResourceConvertible, TimelineItem  {
+public class PullRequestComment extends CodeComment implements ResourceConvertible, TimelineItem  {
 
     private static final long serialVersionUID = 1L;
     public static final Finder<Long, PullRequestComment> find = new Finder<>(Long.class, PullRequestComment.class);
-
-    @Id
-    public Long id;
 
     @ManyToOne
     public PullRequest pullRequest;
 
     public String commitA;
     public String commitB;
-    public String path;
-    public String side;
-    public Integer line;
 
-    @Constraints.Required @Column(length = 4000) @Size(max=4000)
-    public String contents;
-
-    @Constraints.Required
-    public Date createdDate;
-
-    public Long authorId;
-    public String authorLoginId;
-    public String authorName;
-
-    /**
-     * 어떤 리소스에 대한 댓글인지 나타내는 값.
-     *
-     * 리소스타입과 해당 리소스의 PK 값을 조합하여 되도록이면 바뀌지 않으며
-     * 댓글을 달 수 있는 리소스당 유일한 값이어야 한다.
-     *
-     * ex) pull_request_1, pull_request_2, profile_1, milestone_1
-     *
-     */
-    public String resourceKey;
-
-    public PullRequestComment() {
-        createdDate = new Date();
-    }
-
-    public Duration ago() {
-        return JodaDateUtil.ago(this.createdDate);
-    }
 
     public void authorInfos(User user) {
         this.authorId = user.id;
@@ -84,22 +47,9 @@
                 ", authorId=" + authorId +
                 ", authorLoginId='" + authorLoginId + '\'' +
                 ", authorName='" + authorName + '\'' +
-                ", resourceKey='" + resourceKey + '\'' +
                 '}';
     }
 
-    public static List<PullRequestComment> findByResourceKey(String resourceKey) {
-        return find.where()
-                .eq("resourceKey", resourceKey)
-                .orderBy().asc("createdDate")
-                .findList();
-    }
-
-    public static int countByResourceKey(String resourceKey) {
-        return find.where()
-                .eq("resourceKey", resourceKey)
-                .findRowCount();
-    }
 
     @Override
     public Resource asResource(){
@@ -122,6 +72,10 @@
             @Override
             public Long getAuthorId() {
                 return authorId;
+            }
+
+            public void delete() {
+                PullRequestComment.this.delete();
             }
         };
     }
@@ -152,7 +106,7 @@
         blame.push(null, gitRepo.resolve(pullRequest.mergedCommitIdTo));
         BlameResult blameResult = blame.computeBlameResult();
 
-        return !blameResult.getSourceCommit(line - 1).getName().equals(commitB);
+        return !blameResult.getSourceCommit(line.intValue() - 1).getName().equals(commitB);
     }
 
     @Transient
app/models/enumeration/ResourceType.java
--- app/models/enumeration/ResourceType.java
+++ app/models/enumeration/ResourceType.java
@@ -25,7 +25,7 @@
     LABEL("label"),
     PROJECT_LABELS("project_labels"),
     FORK("fork"),
-    CODE_COMMENT("code_comment"),
+    COMMIT_COMMENT("code_comment"),
     PULL_REQUEST("pull_request"),
     PULL_REQUEST_COMMENT("pull_request_comment"),
     COMMIT("commit"),
app/models/resource/Resource.java
--- app/models/resource/Resource.java
+++ app/models/resource/Resource.java
@@ -43,8 +43,8 @@
             case MILESTONE:
                 finder = Milestone.find;
                 break;
-            case CODE_COMMENT:
-                finder = CodeComment.find;
+            case COMMIT_COMMENT:
+                finder = CommitComment.find;
                 break;
             case PULL_REQUEST:
                 finder = PullRequest.finder;
@@ -117,8 +117,8 @@
             case MILESTONE:
                 resource = Milestone.find.byId(longId).asResource();
                 break;
-            case CODE_COMMENT:
-                resource = CodeComment.find.byId(longId).asResource();
+            case COMMIT_COMMENT:
+                resource = CommitComment.find.byId(longId).asResource();
                 break;
             case PULL_REQUEST:
                 return PullRequest.finder.byId(longId).asResource();
@@ -140,4 +140,5 @@
     abstract public ResourceType getType();
     public Resource getContainer() { return null; }
     public Long getAuthorId() { return null; }
+    public void delete() { throw new UnsupportedOperationException(); }
 }
app/playRepository/Commit.java
--- app/playRepository/Commit.java
+++ app/playRepository/Commit.java
@@ -35,9 +35,9 @@
         }
 
         // Add every user who comments on this commit
-        List<CodeComment> comments = CodeComment.find.where()
+        List<CommitComment> comments = CommitComment.find.where()
                 .eq("project.id", project.id).eq("commitId", getId()).findList();
-        for (CodeComment c : comments) {
+        for (CommitComment c : comments) {
             User user = User.find.byId(c.authorId);
             if (user != null) {
                 actualWatchers.add(user);
app/playRepository/Hunk.java
--- app/playRepository/Hunk.java
+++ app/playRepository/Hunk.java
@@ -5,13 +5,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-/**
- * Created with IntelliJ IDEA.
- * User: nori
- * Date: 13. 9. 11
- * Time: 오후 4:48
- * To change this template use File | Settings | File Templates.
- */
 public class Hunk extends EditList {
     public int beginA;
     public int endA;
app/utils/AccessControl.java
--- app/utils/AccessControl.java
+++ app/utils/AccessControl.java
@@ -58,7 +58,7 @@
                 case ISSUE_COMMENT:
                 case NONISSUE_COMMENT:
                 case FORK:
-                case CODE_COMMENT:
+                case COMMIT_COMMENT:
                 case PULL_REQUEST_COMMENT:
                     return true;
                 default:
@@ -227,7 +227,7 @@
         case ISSUE_COMMENT:
         case NONISSUE_COMMENT:
         case BOARD_POST:
-        case CODE_COMMENT:
+        case COMMIT_COMMENT:
         case PULL_REQUEST:
         case PULL_REQUEST_COMMENT:
             return resource.getAuthorId().equals(user.id);
app/views/code/diff.scala.html
--- app/views/code/diff.scala.html
+++ app/views/code/diff.scala.html
@@ -1,4 +1,4 @@
-@(project: Project, commit:playRepository.Commit, parentCommit:playRepository.Commit, patch: String, comments:List[CodeComment], selectedBranch:String, diff: List[playRepository.FileDiff])
+@(project: Project, commit:playRepository.Commit, parentCommit:playRepository.Commit, patch: String, comments:List[CommitComment], selectedBranch:String, diff: List[playRepository.FileDiff])
 
 @import playRepository.RepositoryService
 @import java.net.URLEncoder
@@ -134,14 +134,14 @@
                         
                         <div class="comment-body markdown-wrap markdown-before" markdown="true">@comment.contents</div>
                         
-                        <div class="attachments" data-resourceType="@ResourceType.CODE_COMMENT" data-resourceId="@comment.id"></div>
+                        <div class="attachments" data-resourceType="@ResourceType.COMMIT_COMMENT" data-resourceId="@comment.id"></div>
                     </div>
                 </li>
                 }
             </ul>
             }
             
-            @common.commentForm(project, ResourceType.CODE_COMMENT, routes.CodeHistoryApp.newComment(project.owner, project.name, commit.getId).toString())
+            @common.commentForm(project, ResourceType.COMMIT_COMMENT, routes.CodeHistoryApp.newComment(project.owner, project.name, commit.getId).toString())
         </div>
         @** // Comment **@        
     </div>
@@ -181,7 +181,7 @@
     $(document).ready(function(){
         $yobi.loadModule("code.Diff", {
             "welDiff": $("#commit"),
-            "bCommentable"   : @if(isProjectResourceCreatable(UserApp.currentUser, project, ResourceType.CODE_COMMENT)){true}else{false},
+            "bCommentable"   : @if(isProjectResourceCreatable(UserApp.currentUser, project, ResourceType.COMMIT_COMMENT)){true}else{false},
             "sWatchUrl"     : "@routes.WatchApp.watch(commit.asResource(project).asParameter)",
             "sUnwatchUrl"   : "@routes.WatchApp.unwatch(commit.asResource(project).asParameter)",
             "sParentCommitId": "@if(parentCommit != null){@parentCommit.getId}",
app/views/code/history.scala.html
--- app/views/code/history.scala.html
+++ app/views/code/history.scala.html
@@ -113,7 +113,7 @@
                                 </a>
                             </td>
                             <td class="messages">
-                                @defining(CodeComment.count(project, commit.getId, if(path != null){"/"+path}else{null})){ numOfComment =>
+                                @defining(CommitComment.count(project, commit.getId, if(path != null){"/"+path}else{null})){ numOfComment =>
                                 @if(numOfComment > 0) {
                                 <span class="number-of-comments"><i class="yobicon-comments"></i> @numOfComment</span>
                                 }
app/views/code/view.scala.html
--- app/views/code/view.scala.html
+++ app/views/code/view.scala.html
@@ -129,7 +129,7 @@
             <span id="revisionNo" class="revision">
                 <a href="@routes.CodeHistoryApp.show(project.owner, project.name, revId)@if(branch){?branch=@URLEncoder.encode(branch, "UTF-8")}#@path">
                     @if(project.vcs.equals("GIT")){ @revId.substring(0,7) } else { Revision @revId }
-                    @defining(CodeComment.count(project, fieldText(files, "commitId"), "/"+path)){ numOfComment =>
+                    @defining(CommitComment.count(project, fieldText(files, "commitId"), "/"+path)){ numOfComment =>
                     @if(numOfComment > 0) {
                         <span class="number-of-comments ml5"><i class="yobicon-comments"></i> @numOfComment</span>
                     }
app/views/git/diff.scala.html
--- app/views/git/diff.scala.html
+++ app/views/git/diff.scala.html
@@ -1,4 +1,4 @@
-@(pull: PullRequest, commit:playRepository.Commit, parentCommit:playRepository.Commit, patch: String, comments:List[CodeComment])
+@(pull: PullRequest, commit:playRepository.Commit, parentCommit:playRepository.Commit, patch: String, comments:List[CommitComment])
 
 @import playRepository.RepositoryService
 @import java.net.URLEncoder
@@ -119,14 +119,14 @@
                     
                     <div class="comment-body markdown-wrap markdown-before" markdown="true">@comment.contents</div>
                     
-                    <div class="attachments" data-resourceType="@ResourceType.CODE_COMMENT" data-resourceId="@comment.id"></div>
+                    <div class="attachments" data-resourceType="@ResourceType.COMMIT_COMMENT" data-resourceId="@comment.id"></div>
                 </div>
             </li>
             }
         </ul>
         }
         
-        @common.commentForm(pull.fromProject, ResourceType.CODE_COMMENT, routes.CodeHistoryApp.newComment(pull.fromProject.owner, pull.fromProject.name, commit.getId).toString())
+        @common.commentForm(pull.fromProject, ResourceType.COMMIT_COMMENT, routes.CodeHistoryApp.newComment(pull.fromProject.owner, pull.fromProject.name, commit.getId).toString())
     </div>
     @** // Comment **@
     
@@ -160,7 +160,7 @@
 <script type="text/javascript">
     $(document).ready(function(){
         $yobi.loadModule("code.Diff", {
-            "bCommentable"   : @if(isProjectResourceCreatable(UserApp.currentUser, pull.fromProject, ResourceType.CODE_COMMENT)){true}else{false},
+            "bCommentable"   : @if(isProjectResourceCreatable(UserApp.currentUser, pull.fromProject, ResourceType.COMMIT_COMMENT)){true}else{false},
             "sWatchUrl"      : "@routes.WatchApp.watch(commit.asResource(pull.fromProject).asParameter)",
             "sUnwatchUrl"    : "@routes.WatchApp.unwatch(commit.asResource(pull.fromProject).asParameter)",
             "sParentCommitId": "@if(parentCommit != null){@parentCommit.getId}",
app/views/git/partial_diff.scala.html
--- app/views/git/partial_diff.scala.html
+++ app/views/git/partial_diff.scala.html
@@ -83,7 +83,7 @@
 				            </td>
 				
 				            <td class="messages">
-				                @defining(CodeComment.count(pullRequest.fromProject, commit.getId, null)){ numOfComment =>
+				                @defining(CommitComment.count(pullRequest.fromProject, commit.getId, null)){ numOfComment =>
 				                    @if(numOfComment > 0) {
 				                    <span class="number-of-comments"><i class="yobicon-comments"></i> @numOfComment</span>
 				                    }
app/views/git/partial_list.scala.html
--- app/views/git/partial_list.scala.html
+++ app/views/git/partial_list.scala.html
@@ -24,7 +24,7 @@
                     <div class="infos nm">
                         <a href="@routes.UserApp.userInfo(req.contributor.loginId)" class="author">@req.contributor.name</a>
                         <span class="date ml10">@agoString(req.createdAgo())</span>
-                        @defining(PullRequestComment.countByResourceKey(req.getResourceKey()) + CodeComment.countByCommits(req.fromProject, req.pullRequestCommits)) { count =>
+                        @defining(req.comments.size + CommitComment.countByCommits(req.fromProject, req.pullRequestCommits)) { count =>
                             @if(count > 0) {
                             <a href="@routes.PullRequestApp.pullRequest(req.toProject.owner, req.toProject.name, req.number)#comments" class="comment-wrap">
                                 <i class="icon yobicon-comments"></i>
app/views/git/view.scala.html
--- app/views/git/view.scala.html
+++ app/views/git/view.scala.html
@@ -327,7 +327,7 @@
 					}
 					</ul>
 					}
-					@common.commentForm(project, ResourceType.SIMPLE_COMMENT, routes.PullRequestCommentApp.newComment(project.owner, project.name, pull.id).toString())
+					@common.commentForm(project, ResourceType.PULL_REQUEST_COMMENT, routes.PullRequestCommentApp.newComment(project.owner, project.name, pull.id).toString())
 				</div>
             </div>
 
@@ -354,7 +354,7 @@
 
                             <td class="messages">
 
-                                @defining(CodeComment.count(pull.fromProject, commit.getCommitId, null)){ numOfComment =>
+                                @defining(CommitComment.count(pull.fromProject, commit.getCommitId, null)){ numOfComment =>
                                     @if(numOfComment > 0) {
                                     <span class="number-of-comments"><i class="yobicon-comments"></i> @numOfComment</span>
                                     }
app/views/partial_diff.scala.html
--- app/views/partial_diff.scala.html
+++ app/views/partial_diff.scala.html
@@ -1,108 +1,122 @@
-@(fileDiffs: List[playRepository.FileDiff], comments:List[CommonComment])
+@(fileDiffs: java.util.List[playRepository.FileDiff], comments:java.util.List[_ <: CodeComment])
 
 @import playRepository.DiffLineType
 @import org.eclipse.jgit.diff.DiffEntry
+@import scala.collection.JavaConversions._
 
-@render(fileDiffs: List[playRepository.FileDiff], comments:List[CodeComment]) = {
-    render2(fileDiffs, comments map { [comment.path, comment.line, comment.side] => comment })
+@render(fileDiffs, comments.toList.groupBy((comment: CodeComment) => commentKey(comment.path, comment.side, comment.line)))
+
+@writeLine(klass: String, prefix: String, numA: Integer, numB: Integer, content: String, commentsOnLine: scala.collection.immutable.List[_ <: CodeComment]) = {
+    <tr class="@klass" data-line="@Option(numA).getOrElse(numB)" data-type="@klass"><td class="linenum"><i class="icon-comment"></i>@Option(numA).getOrElse("")</td><td class="linenum">@Option(numB).getOrElse("")</td><td><span>@prefix</span>@content</td></tr>
+    @if(commentsOnLine != null) { @writeCommentsOnLine(commentsOnLine) }
 }
 
-@writeLine(klass: String, prefix: String, numA: Integer, numB: Integer, content: String) = {
-    @writeCodeLine(klass, prefix, numA, numB, content)
-    @writeComment(comment)
-}
+@writeCommentsOnLine(comments: scala.collection.immutable.List[CodeComment]) = {
+    <tr class="comments board-comment-wrap">
+        <td colspan=3>
+            <ul class="comments">
+                @for(comment: CodeComment <- comments) {
+                <li id="comment-@comment.id" data-path="@comment.path" data-side="@comment.side" data-line="@comment.line" class="comment">
+                    <div class="comment-avatar">
+                        <a href="@routes.UserApp.userInfo(comment.authorLoginId)" class="avatar-wrap" data-toggle="tooltip" data-placement="top" title="@comment.authorName">
+                            <img src="@User.findByLoginId(comment.authorLoginId).avatarUrl" width="32" height="32" alt="@comment.authorLoginId">
+                        </a>
+                    </div>
+                    <div class="media-body">
+                        <div class="meta-info">
+                            <span class="comment_author pull-left">
+                                <i class="yobicon-comment"></i>
+                                <a href="@routes.UserApp.userInfo(comment.authorLoginId)" data-toggle="tooltip" data-placement="top" title="@comment.authorName">
+                                    <strong>@comment.authorLoginId </strong>
+                                </a>
+                            </span>
+                            <span class="ago"><a href="#comment-@comment.id">@utils.TemplateHelper.agoString(utils.JodaDateUtil.ago(comment.createdDate))</a></span>
+                            @if(utils.AccessControl.isAllowed(UserApp.currentUser(), comment.asResource(), Operation.DELETE)){
+                            <span class="edit pull-right">
+                                <!-- FIXME: Delete comment? pull request하고 commit comment 구분해야 함 -->
+                                <button class="btn-transparent pull-right close" data-request-method="delete" data-request-uri="@routes.CommentApp.delete(comment.asResource.getType.resource, comment.asResource.getId)"><i class="yobicon-trash"></i></button>
+                            </span>
+                            }
+                        </div>
 
-@writeLine(klass: String, prefix: String, numA: Integer, numB: Integer, content: String, comment: CodeComment) = {
-    @writeCodeLine(klass, prefix, numA, numB, content)
-}
+                        <div class="comment-body markdown-wrap markdown-before" markdown="true">@comment.contents</div>
 
-@writeCodeLine(klass: String, prefix: String, numA: Integer, numB: Integer, content: String, comment: CodeComment) = { <tr class="@klass" data-line="@Option(numA).getOrElse(numB)" data-type="@klass"><td class="linenum"><i class="icon-comment"></i>@Option(numA).getOrElse("")</td><td class="linenum">@Option(numB).getOrElse("")</td><td><span>@prefix</span>@content</td></tr>
-}
-
-@writeComment(comment: CodeComment) = {
-    <li id="comment-@comment.id" data-path="@comment.path" data-side="@comment.side" data-line="@comment.line" class="comment">
-        <div class="comment-avatar">
-            <a href="@routes.UserApp.userInfo(comment.authorLoginId)" class="avatar-wrap" data-toggle="tooltip" data-placement="top" title="@comment.authorName">
-                <img src="@User.findByLoginId(comment.authorLoginId).avatarUrl" width="32" height="32" alt="@comment.authorLoginId">
-            </a>
-        </div>
-        <div class="media-body">
-            <div class="meta-info">
-                <span class="comment_author pull-left">
-                    <i class="yobicon-comment"></i>
-                    <a href="@routes.UserApp.userInfo(comment.authorLoginId)" data-toggle="tooltip" data-placement="top" title="@comment.authorName">
-                        <strong>@comment.authorLoginId </strong>
-                    </a>
-                </span>
-                <span class="ago"><a href="#comment-@comment.id">@utils.TemplateHelper.agoString(ago(comment.createdDate))</a></span>
-                @if(isAllowed(UserApp.currentUser(), comment.asResource(), Operation.DELETE)){
-                <span class="edit pull-right">
-                    <button class="btn-transparent pull-right close" data-request-method="delete" data-request-uri="@routes.CodeHistoryApp.deleteComment(project.owner, project.name, commit.getId, comment.id)"><i class="yobicon-trash"></i></button>
-                </span>
+                        <div class="attachments" resourceType="@comment.asResource.getType" resourceId="@comment.id"></div>
+                    </div>
+                </li>
                 }
-            </div>
-
-            <div class="comment-body markdown-wrap markdown-before" markdown="true">@comment.contents</div>
-
-            <div class="attachments" resourceType="@ResourceType.CODE_COMMENT" resourceId="@comment.id"></div>
-        </div>
-    </li>
+                <button class="nbtn medium btn-thread">댓글창 열기</button>
+                <button class="nbtn medium btn-thread" style="display: none;">댓글창 닫기</button>
+            </ul>
+        </td>
+    </tr>
 }
 
-@renderHunks(hunks: List[Hunk], comments: List[CodeComment]) = {
+@commentKey(path: String, side: String, lineNum: Integer) = @{ path + ":" + side + ":" + lineNum }
+
+@commentsOrNull(comments: Map[String, scala.collection.immutable.List[_ <: CodeComment]], key: String) = @{
+    if(comments.contains(key)) {
+        comments(key)
+    } else {
+        null
+    }
+}
+
+@renderFileDiff(diff: playRepository.FileDiff, comments:scala.collection.immutable.Map[String, scala.collection.immutable.List[_ <: CodeComment]]) = {
     @for(hunk <- diff.getHunks) {
         <tr class="range"><td>...</td><td>...</td><td>-@(hunk.beginA + 1),@(hunk.endA - hunk.beginA) +@(hunk.beginB + 1),@(hunk.endB - hunk.beginB)</td></tr>
         @for(line <- hunk.lines){
             @line.kind match {
-            case DiffLineType.ADD => { @writeLine(line.kind.toString.toLowerCase, "+", null, line.numB + 1, line.content, comments(List(diff.pathB, "add", line.numB + 1)) }
-            case DiffLineType.REMOVE => { @writeLine(line.kind.toString.toLowerCase, "-", line.numA + 1, null, line.content, comments(List(diff.pathA, "remove", line.numA + 1))) }
-            case _ => { @writeLine(line.kind.toString.toLowerCase, " ", line.numA + 1, line.numB + 1, line.content, comments(List(diff.pathA, "base", line.numA + 1))) }
+            case DiffLineType.ADD => { @writeLine(line.kind.toString.toLowerCase, "+", null, line.numB + 1, line.content, commentsOrNull(comments, commentKey(diff.pathB, "add", line.numB + 1))) }
+            case DiffLineType.REMOVE => { @writeLine(line.kind.toString.toLowerCase, "-", line.numA + 1, null, line.content, commentsOrNull(comments, commentKey(diff.pathA, "remove", line.numA + 1))) }
+            case _ => { @writeLine(line.kind.toString.toLowerCase, " ", line.numA + 1, line.numB + 1, line.content, commentsOrNull(comments, commentKey(diff.pathA, "base", line.numA + 1))) }
             }
         }
     }
 }
 
-@render2(fileDiffs: List[playRepository.FileDiff], comments:Map[List, CodeComment]) = {
+@render(fileDiffs: java.util.List[playRepository.FileDiff], comments:scala.collection.immutable.Map[String, scala.collection.immutable.List[_ <: CodeComment]]) = {
 @for(diff <- fileDiffs) {
     @diff.changeType match {
     case DiffEntry.ChangeType.MODIFY => {
-        <table class="diff-body" data-path="@diff.pathB"><tbody>
+        <table class="diff-body show-comments" data-path="@diff.pathB"><tbody>
         <tr class="file">
             <td colspan=3>@diff.pathB</td>
         </tr>
-        @renderHunks(diff.getHunks, comments)
+        @renderFileDiff(diff, comments)
         </tbody></table>
     }
+
     case DiffEntry.ChangeType.ADD => {
-        <table class="diff-body" data-path="@diff.pathB"><tbody>
+        <table class="diff-body show-comments" data-path="@diff.pathB"><tbody>
         <tr class="file">
             <td colspan=3>@diff.pathB (Added)</td>
         </tr>
         @for(i <- 0 until diff.b.size) {
-            @writeLine("add", "+", null, i + 1, diff.b.getString(i))
+            @writeLine("add", "+", null, i + 1, diff.b.getString(i), commentsOrNull(comments, diff.pathB + ":add:" + (i + 1)))
         }
         </tbody></table>
     }
     case DiffEntry.ChangeType.DELETE => {
-        <table class="diff-body" data-path="@diff.pathA"><tbody>
+        <table class="diff-body show-comments" data-path="@diff.pathA"><tbody>
         <tr class="file">
             <td colspan=3>@diff.pathA (Deleted)</td>
         </tr>
         @for(i <- 0 until diff.a.size) {
-            @writeLine("remove", "-", null, i + 1, diff.a.getString(i))
+            @writeLine("remove", "-", null, i + 1, diff.a.getString(i), commentsOrNull(comments, diff.pathA + ":add:" + (i + 1)))
         }
         </tbody></table>
     }
     case DiffEntry.ChangeType.RENAME => {
-        <table class="diff-body" data-path="@diff.pathB"><tbody>
+        <table class="diff-body show-comments" data-path="@diff.pathB"><tbody>
         <tr class="file">
             <td colspan=3>@diff.pathB -> @diff.pathB</td>
         </tr>
-        @renderHunks(diff.getHunks, comments)
+        @renderFileDiff(diff, comments)
         </tbody></table>
     }
     case DiffEntry.ChangeType.COPY => {
-        <table class="diff-body" data-path="@diff.pathB"><tbody>
+        <table class="diff-body show-comments" data-path="@diff.pathB"><tbody>
         <tr class="file">
             <td colspan=3>Copy @diff.pathA to @diff.pathB</td>
         </tr>
conf/evolutions/default/41.sql
--- conf/evolutions/default/41.sql
+++ conf/evolutions/default/41.sql
@@ -1,53 +1,99 @@
 # --- !Ups
 
 ALTER TABLE simple_comment RENAME TO pull_request_comment;
+DROP SEQUENCE IF EXISTS pull_request_comment_seq;
 CREATE SEQUENCE pull_request_comment_seq START WITH simple_comment_seq.nextval;
 DROP SEQUENCE IF EXISTS simple_comment_seq;
+ALTER TABLE pull_request_comment ADD COLUMN project_id bigint;
+ALTER TABLE pull_request_comment ADD COLUMN commit_a varchar(40);
+ALTER TABLE pull_request_comment ADD COLUMN commit_b varchar(40);
+ALTER TABLE pull_request_comment ADD COLUMN path varchar(255);
+ALTER TABLE pull_request_comment ADD COLUMN line integer;
+ALTER TABLE pull_request_comment ADD COLUMN side varchar(16);
+ALTER TABLE pull_request_comment ADD COLUMN pull_request_id bigint;
+
+ALTER TABLE code_comment RENAME TO commit_comment;
+DROP SEQUENCE IF EXISTS commit_comment_seq;
+CREATE SEQUENCE commit_comment_seq START WITH code_comment_seq.nextval;
+DROP SEQUENCE IF EXISTS code_commit_seq;
 
 ALTER TABLE notification_event DROP CONSTRAINT IF EXISTS ck_notification_event_resource_type;
 ALTER TABLE notification_event ALTER COLUMN resource_type TYPE varchar(255);
+UPDATE notification_event SET resource_type='COMMIT_COMMENT' WHERE resource_type='CODE_COMMENT';
+UPDATE notification_event SET event_type='NEW_COMMIT_COMMENT' WHERE event_type='NEW_CODE_COMMENT';
 UPDATE notification_event SET resource_type='PULL_REQUEST_COMMENT' WHERE resource_type='SIMPLE_COMMENT';
 UPDATE notification_event SET event_type='NEW_PULL_REQUEST_COMMENT' WHERE event_type='NEW_SIMPLE_COMMENT';
-ALTER TABLE notification_event ADD constraint ck_notification_event_resource_type check (resource_type in ('ISSUE_POST','ISSUE_ASSIGNEE','ISSUE_STATE','ISSUE_CATEGORY','ISSUE_MILESTONE','ISSUE_LABEL','BOARD_POST','BOARD_CATEGORY','BOARD_NOTICE','CODE','MILESTONE','WIKI_PAGE','PROJECT_SETTING','SITE_SETTING','USER','USER_AVATAR','PROJECT','ATTACHMENT','ISSUE_COMMENT','NONISSUE_COMMENT','LABEL','PROJECT_LABELS','FORK','CODE_COMMENT','PULL_REQUEST','PULL_REQUEST_COMMENT'));
+ALTER TABLE notification_event ADD constraint ck_notification_event_resource_type check (resource_type in ('ISSUE_POST','ISSUE_ASSIGNEE','ISSUE_STATE','ISSUE_CATEGORY','ISSUE_MILESTONE','ISSUE_LABEL','BOARD_POST','BOARD_CATEGORY','BOARD_NOTICE','CODE','MILESTONE','WIKI_PAGE','PROJECT_SETTING','SITE_SETTING','USER','USER_AVATAR','PROJECT','ATTACHMENT','ISSUE_COMMENT','NONISSUE_COMMENT','LABEL','PROJECT_LABELS','FORK','COMMIT_COMMENT','PULL_REQUEST','PULL_REQUEST_COMMENT'));
 
 ALTER TABLE watch DROP CONSTRAINT IF EXISTS ck_watch_resource_type;
 ALTER TABLE watch ALTER COLUMN resource_type TYPE varchar(255);
+UPDATE watch SET resource_type='COMMIT_COMMENT' WHERE resource_type='CODE_COMMENT';
 UPDATE watch SET resource_type='PULL_REQUEST_COMMENT' WHERE resource_type='SIMPLE_COMMENT';
-ALTER TABLE watch ADD CONSTRAINT ck_watch_resource_type check (resource_type in ('ISSUE_POST','ISSUE_ASSIGNEE','ISSUE_STATE','ISSUE_CATEGORY','ISSUE_MILESTONE','ISSUE_LABEL','BOARD_POST','BOARD_CATEGORY','BOARD_NOTICE','CODE','MILESTONE','WIKI_PAGE','PROJECT_SETTING','SITE_SETTING','USER','USER_AVATAR','PROJECT','ATTACHMENT','ISSUE_COMMENT','NONISSUE_COMMENT','LABEL','PROJECT_LABELS','FORK','CODE_COMMENT','PULL_REQUEST','PULL_REQUEST_COMMENT', 'COMMIT'));
+ALTER TABLE watch ADD CONSTRAINT ck_watch_resource_type check (resource_type in ('ISSUE_POST','ISSUE_ASSIGNEE','ISSUE_STATE','ISSUE_CATEGORY','ISSUE_MILESTONE','ISSUE_LABEL','BOARD_POST','BOARD_CATEGORY','BOARD_NOTICE','CODE','MILESTONE','WIKI_PAGE','PROJECT_SETTING','SITE_SETTING','USER','USER_AVATAR','PROJECT','ATTACHMENT','ISSUE_COMMENT','NONISSUE_COMMENT','LABEL','PROJECT_LABELS','FORK','COMMIT_COMMENT','PULL_REQUEST','PULL_REQUEST_COMMENT', 'COMMIT'));
 
 ALTER TABLE unwatch DROP CONSTRAINT IF EXISTS ck_unwatch_resource_type;
 ALTER TABLE unwatch ALTER COLUMN resource_type TYPE varchar(255);
+UPDATE unwatch SET resource_type='COMMIT_COMMENT' WHERE resource_type='CODE_COMMENT';
 UPDATE unwatch SET resource_type='PULL_REQUEST_COMMENT' WHERE resource_type='SIMPLE_COMMENT';
-ALTER TABLE unwatch ADD CONSTRAINT ck_unwatch_resource_type check (resource_type in ('ISSUE_POST','ISSUE_ASSIGNEE','ISSUE_STATE','ISSUE_CATEGORY','ISSUE_MILESTONE','ISSUE_LABEL','BOARD_POST','BOARD_CATEGORY','BOARD_NOTICE','CODE','MILESTONE','WIKI_PAGE','PROJECT_SETTING','SITE_SETTING','USER','USER_AVATAR','PROJECT','ATTACHMENT','ISSUE_COMMENT','NONISSUE_COMMENT','LABEL','PROJECT_LABELS','FORK','CODE_COMMENT','PULL_REQUEST','PULL_REQUEST_COMMENT', 'COMMIT'));
+ALTER TABLE unwatch ADD CONSTRAINT ck_unwatch_resource_type check (resource_type in ('ISSUE_POST','ISSUE_ASSIGNEE','ISSUE_STATE','ISSUE_CATEGORY','ISSUE_MILESTONE','ISSUE_LABEL','BOARD_POST','BOARD_CATEGORY','BOARD_NOTICE','CODE','MILESTONE','WIKI_PAGE','PROJECT_SETTING','SITE_SETTING','USER','USER_AVATAR','PROJECT','ATTACHMENT','ISSUE_COMMENT','NONISSUE_COMMENT','LABEL','PROJECT_LABELS','FORK','COMMIT_COMMENT','PULL_REQUEST','PULL_REQUEST_COMMENT', 'COMMIT'));
 
 ALTER TABLE issue_event DROP CONSTRAINT IF EXISTS ck_issue_event_event_type;
-ALTER TABLE issue_event ALTER COLUMN resource_type TYPE varchar(255);
+ALTER TABLE issue_event ALTER COLUMN event_type TYPE varchar(255);
+UPDATE issue_event SET event_type='NEW_COMMIT_COMMENT' WHERE event_type='NEW_CODE_COMMENT';
 UPDATE issue_event SET event_type='NEW_PULL_REQUEST_COMMENT' WHERE event_type='NEW_SIMPLE_COMMENT';
 ALTER TABLE issue_event ADD CONSTRAINT ck_issue_event_event_type check (event_type in ('NEW_ISSUE','NEW_POSTING','ISSUE_ASSIGNEE_CHANGED','ISSUE_STATE_CHANGED','NEW_COMMENT','NEW_PULL_REQUEST','NEW_PULL_REQUEST_COMMENT','PULL_REQUEST_STATE_CHANGED'));
 
+ALTER TABLE attachment DROP CONSTRAINT IF EXISTS ck_attachment_container_type;
+UPDATE attachment SET container_type='COMMIT_COMMENT' WHERE container_type='CODE_COMMENT';
+UPDATE attachment SET container_type='PULL_REQUEST_COMMENT' WHERE container_type='SIMPLE_COMMENT';
+ALTER TABLE attachment ADD CONSTRAINT ck_attachment_container_type check (container_type in ('ISSUE_POST','ISSUE_ASSIGNEE','ISSUE_STATE','ISSUE_CATEGORY','ISSUE_MILESTONE','ISSUE_LABEL','BOARD_POST','BOARD_CATEGORY','BOARD_NOTICE','CODE','MILESTONE','WIKI_PAGE','PROJECT_SETTING','SITE_SETTING','USER','USER_AVATAR','PROJECT','ATTACHMENT','ISSUE_COMMENT','NONISSUE_COMMENT','COMMIT_COMMENT', 'PULL_REQUEST_COMMENT', 'PULL_REQUEST'));
+
 # --- !Downs
 
+ALTER TABLE pull_request_comment DROP COLUMN IF EXISTS project_id;
+ALTER TABLE pull_request_comment DROP COLUMN IF EXISTS commit_a;
+ALTER TABLE pull_request_comment DROP COLUMN IF EXISTS commit_b;
+ALTER TABLE pull_request_comment DROP COLUMN IF EXISTS path;
+ALTER TABLE pull_request_comment DROP COLUMN IF EXISTS line;
+ALTER TABLE pull_request_comment DROP COLUMN IF EXISTS side;
+ALTER TABLE pull_request_comment DROP COLUMN IF EXISTS pull_request_id;
 ALTER TABLE pull_request_comment RENAME TO simple_comment;
+DROP SEQUENCE IF EXISTS simple_comment_seq;
 CREATE SEQUENCE simple_comment_seq START WITH pull_request_comment_seq.nextval;
 DROP SEQUENCE IF EXISTS pull_request_comment_seq;
 
+ALTER TABLE commit_comment RENAME TO code_comment;
+DROP SEQUENCE IF EXISTS code_comment_seq;
+CREATE SEQUENCE code_comment_seq START WITH commit_comment_seq.nextval;
+DROP SEQUENCE IF EXISTS commit_comment_seq;
+
 ALTER TABLE notification_event DROP CONSTRAINT IF EXISTS ck_notification_event_resource_type;
+UPDATE notification_event SET resource_type='CODE_COMMENT' WHERE resource_type='COMMIT_COMMENT';
+UPDATE notification_event SET event_type='NEW_CODE_COMMENT' WHERE event_type='NEW_COMMIT_COMMENT';
 UPDATE notification_event SET resource_type='SIMPLE_COMMENT' WHERE resource_type='PULL_REQUEST_COMMENT';
 UPDATE notification_event SET event_type='NEW_SIMPLE_COMMENT' WHERE event_type='NEW_PULL_REQUEST_COMMENT';
 ALTER TABLE notification_event ADD constraint ck_notification_event_resource_type check (resource_type in ('ISSUE_POST','ISSUE_ASSIGNEE','ISSUE_STATE','ISSUE_CATEGORY','ISSUE_MILESTONE','ISSUE_LABEL','BOARD_POST','BOARD_CATEGORY','BOARD_NOTICE','CODE','MILESTONE','WIKI_PAGE','PROJECT_SETTING','SITE_SETTING','USER','USER_AVATAR','PROJECT','ATTACHMENT','ISSUE_COMMENT','NONISSUE_COMMENT','LABEL','PROJECT_LABELS','FORK','CODE_COMMENT','PULL_REQUEST','SIMPLE_COMMENT'));
 ALTER TABLE notification_event ALTER COLUMN resource_type TYPE varchar(16);
 
 ALTER TABLE watch DROP CONSTRAINT IF EXISTS ck_watch_resource_type;
+UPDATE watch SET resource_type='CODE_COMMENT' WHERE resource_type='COMMIT_COMMENT';
 UPDATE watch SET resource_type='SIMPLE_COMMENT' WHERE resource_type='PULL_REQUEST_COMMENT';
 ALTER TABLE watch ADD CONSTRAINT ck_watch_resource_type check (resource_type in ('ISSUE_POST','ISSUE_ASSIGNEE','ISSUE_STATE','ISSUE_CATEGORY','ISSUE_MILESTONE','ISSUE_LABEL','BOARD_POST','BOARD_CATEGORY','BOARD_NOTICE','CODE','MILESTONE','WIKI_PAGE','PROJECT_SETTING','SITE_SETTING','USER','USER_AVATAR','PROJECT','ATTACHMENT','ISSUE_COMMENT','NONISSUE_COMMENT','LABEL','PROJECT_LABELS','FORK','CODE_COMMENT','PULL_REQUEST','SIMPLE_COMMENT', 'COMMIT'));
 ALTER TABLE watch ALTER COLUMN resource_type TYPE varchar(16);
 
 ALTER TABLE unwatch DROP CONSTRAINT IF EXISTS ck_unwatch_resource_type;
+UPDATE unwatch SET resource_type='CODE_COMMENT' WHERE resource_type='COMMIT_COMMENT';
 UPDATE unwatch SET resource_type='SIMPLE_COMMENT' WHERE resource_type='PULL_REQUEST_COMMENT';
 ALTER TABLE unwatch ADD CONSTRAINT ck_unwatch_resource_type check (resource_type in ('ISSUE_POST','ISSUE_ASSIGNEE','ISSUE_STATE','ISSUE_CATEGORY','ISSUE_MILESTONE','ISSUE_LABEL','BOARD_POST','BOARD_CATEGORY','BOARD_NOTICE','CODE','MILESTONE','WIKI_PAGE','PROJECT_SETTING','SITE_SETTING','USER','USER_AVATAR','PROJECT','ATTACHMENT','ISSUE_COMMENT','NONISSUE_COMMENT','LABEL','PROJECT_LABELS','FORK','CODE_COMMENT','PULL_REQUEST','SIMPLE_COMMENT', 'COMMIT'));
 ALTER TABLE unwatch ALTER COLUMN resource_type TYPE varchar(16);
 
 ALTER TABLE issue_event DROP CONSTRAINT IF EXISTS ck_issue_event_event_type;
+UPDATE issue_event SET event_type='NEW_CODE_COMMENT' WHERE event_type='NEW_COMMIT_COMMENT';
 UPDATE issue_event SET event_type='NEW_SIMPLE_COMMENT' WHERE event_type='NEW_PULL_REQUEST_COMMENT';
-ALTER TABLE issue_event ADD CONSTRAINT ck_issue_event_event_type check (event_type in ('NEW_ISSUE','NEW_POSTING','ISSUE_ASSIGNEE_CHANGED','ISSUE_STATE_CHANGED','NEW_COMMENT','NEW_PULL_REQUEST','NEW_SIMPLE_COMMENT','PULL_REQUEST_STATE_CHANGED')),
-ALTER TABLE issue_event ALTER COLUMN resource_type TYPE varchar(16);
+ALTER TABLE issue_event ADD CONSTRAINT ck_issue_event_event_type check (event_type in ('NEW_ISSUE','NEW_POSTING','ISSUE_ASSIGNEE_CHANGED','ISSUE_STATE_CHANGED','NEW_COMMENT','NEW_PULL_REQUEST','NEW_SIMPLE_COMMENT','PULL_REQUEST_STATE_CHANGED'));
+ALTER TABLE issue_event ALTER COLUMN event_type TYPE varchar(16);
+
+ALTER TABLE attachment DROP CONSTRAINT IF EXISTS ck_attachment_container_type;
+UPDATE attachment SET container_type='CODE_COMMENT' WHERE container_type='COMMIT_COMMENT';
+UPDATE attachment SET container_type='SIMPLE_COMMENT' WHERE container_type='PULL_REQUEST_COMMENT';
+ALTER TABLE attachment ADD CONSTRAINT ck_attachment_container_type check (container_type in ('ISSUE_POST','ISSUE_ASSIGNEE','ISSUE_STATE','ISSUE_CATEGORY','ISSUE_MILESTONE','ISSUE_LABEL','BOARD_POST','BOARD_CATEGORY','BOARD_NOTICE','CODE','MILESTONE','WIKI_PAGE','PROJECT_SETTING','SITE_SETTING','USER','USER_AVATAR','PROJECT','ATTACHMENT','ISSUE_COMMENT','NONISSUE_COMMENT','CODE_COMMENT', 'PULL_REQUEST'));
conf/routes
--- conf/routes
+++ conf/routes
@@ -219,3 +219,5 @@
 # remove trailing slash
 GET     /*paths                                         controllers.Application.removeTrailer(paths)
 
+# Comment
+DELETE  /comments/:type/:id                             controllers.CommentApp.delete(type, id)
Add a comment
List