[Notice] Announcing the End of Demo Server [Read me]
doortts doortts 2017-01-05
search: Support issue search with 'commented by me' option
@5a4aa6fce13af0f70348027b604876cbd0646250
app/controllers/IssueApp.java
--- app/controllers/IssueApp.java
+++ app/controllers/IssueApp.java
@@ -108,7 +108,8 @@
     }
 
     private static boolean hasNotConditions(models.support.SearchCondition searchCondition) {
-        return searchCondition.assigneeId == null && searchCondition.authorId == null && searchCondition.mentionId == null;
+        return searchCondition.assigneeId == null && searchCondition.authorId == null && searchCondition.mentionId == null
+                && searchCondition.commenterId == null;
     }
 
     @Transactional
app/models/support/SearchCondition.java
--- app/models/support/SearchCondition.java
+++ app/models/support/SearchCondition.java
@@ -53,6 +53,8 @@
     public Organization organization;
     public List<String> projectNames;
 
+    public Long commenterId;
+
     @Formats.DateTime(pattern = "yyyy-MM-dd")
     public Date dueDate;
 
@@ -72,6 +74,7 @@
         one.labelIds = new HashSet<>(this.labelIds);
         one.authorId = this.authorId;
         one.assigneeId = this.assigneeId;
+        one.commenterId = this.commenterId;
         one.mentionId = this.mentionId;
         one.dueDate = this.dueDate;
         one.projectNames = this.projectNames;
@@ -135,6 +138,11 @@
 
     public SearchCondition setAssigneeId(Long assigneeId) {
         this.assigneeId = assigneeId;
+        return this;
+    }
+
+    public SearchCondition setCommenterId(Long commenterId) {
+        this.commenterId = commenterId;
         return this;
     }
 
@@ -220,6 +228,23 @@
         }
     }
 
+    private void setCommenterIfExist(ExpressionList<Issue> el, Project project) {
+        // TODO: access control
+        if (commenterId != null) {
+            User commenter = User.find.byId(commenterId);
+            if(!commenter.isAnonymous()) {
+                List<Long> ids = getCommentedIssueIds(commenter, project);
+
+                if (ids.isEmpty()) {
+                    // No need to progress because the query matches nothing.
+                    el.idEq(-1);
+                } else {
+                    el.idIn(ids);
+                }
+            }
+        }
+    }
+
     private void setAssigneeIfExists(ExpressionList<Issue> el) {
         if (assigneeId != null) {
             if (assigneeId.equals(User.anonymous.id)) {
@@ -252,6 +277,7 @@
 
         setAssigneeIfExists(el);
         setAuthorIfExist(el);
+        setCommenterIfExist(el, null);
         setMentionedIssuesIfExist(el);
         setFilteredStringIfExist(el);
 
@@ -292,6 +318,33 @@
                 }
             }
         }
+    }
+
+    private List<Long> getCommentedIssueIds(User commenter, Project project) {
+        Set<Long> issueIds = new HashSet<>();
+
+        for (Comment comment : IssueComment.find.where()
+                .eq("authorId", commenter.id)
+                .findList()) {
+
+            switch (comment.asResource().getType()) {
+                case ISSUE_COMMENT:
+                    if(project == null) {
+                        issueIds.add(comment.getParent().id);
+                        break;
+                    } else {
+                        if(comment.getParent().project.id.equals(project.id)){
+                            issueIds.add(comment.getParent().id);
+                        }
+                    }
+                    break;
+                default:
+                    play.Logger.warn("'" + comment.asResource().getType() + "' is not supported.");
+                    break;
+            }
+        }
+
+        return new ArrayList<>(issueIds);
     }
 
     private List<Long> getMentioningIssueIds(User mentionUser) {
@@ -368,6 +421,8 @@
             }
         }
 
+        setCommenterIfExist(el, project);
+
         if (milestoneId != null) {
             if (milestoneId.equals(Milestone.NULL_MILESTONE_ID)) {
                 el.isNull("milestone");
app/views/issue/my_partial_list_quicksearch.scala.html
--- app/views/issue/my_partial_list_quicksearch.scala.html
+++ app/views/issue/my_partial_list_quicksearch.scala.html
@@ -26,6 +26,7 @@
         <a pjax-filter href="#"
             data-author-id=""
             data-assignee-id="@UserApp.currentUser.id"
+            data-commenter-id=""
             data-milestone-id="@param.milestoneId"
             data-mention-id="">
             @Messages("issue.list.assignedToMe")
@@ -35,15 +36,27 @@
         <a pjax-filter href="#"
             data-author-id="@UserApp.currentUser.id"
             data-assignee-id=""
+            data-commenter-id=""
             data-milestone-id="@param.milestoneId"
             data-mention-id="">
             @Messages("issue.list.authoredByMe")
+        </a>
+    </li>
+    <li @if(param.commenterId == UserApp.currentUser().id){ class="active"}>
+        <a pjax-filter href="#"
+        data-author-id=""
+        data-assignee-id=""
+        data-commenter-id="@UserApp.currentUser.id"
+        data-milestone-id="@param.milestoneId"
+        data-mention-id="">
+        @Messages("issue.list.commentedByMe")
         </a>
     </li>
     <li @if(param.mentionId == UserApp.currentUser().id){ class="active"}>
         <a pjax-filter href="#"
             data-author-id=""
             data-assignee-id=""
+            data-commenter-id=""
             data-milestone-id="@param.milestoneId"
             data-mention-id="@UserApp.currentUser.id">
             @Messages("issue.list.mentionedOfMe")
app/views/issue/my_partial_search.scala.html
--- app/views/issue/my_partial_search.scala.html
+++ app/views/issue/my_partial_search.scala.html
@@ -45,6 +45,7 @@
                 <input type="hidden" name="orderDir" value="@param.orderDir">
                 <input type="hidden" name="state" value="@param.state">
                 <input type="hidden" name="authorId" value="@param.authorId" data-search="authorId">
+                <input type="hidden" name="commenterId" value="@param.commenterId" data-search="commenterId">
                 <input type="hidden" name="assigneeId" value="@param.assigneeId" data-search="assigneeId">
                 <input type="hidden" name="mentionId" value="@param.mentionId" data-search="mentionId">
                 <div class="search">
app/views/issue/partial_list_quicksearch.scala.html
--- app/views/issue/partial_list_quicksearch.scala.html
+++ app/views/issue/partial_list_quicksearch.scala.html
@@ -20,38 +20,56 @@
 **@
 @(param:models.support.SearchCondition, project:Project)
 
-@paramForEveryone = @{ param.clone().setState(param.state).setAssigneeId(null).setAuthorId(null) }
-@paramForIssuesAssignedToMe = @{ param.clone().setState(param.state).setAssigneeId(UserApp.currentUser().id).setAuthorId(null) }
-@paramForIssuesAuthoredByMe = @{ param.clone().setState(param.state).setAssigneeId(null).setAuthorId(UserApp.currentUser().id) }
+@paramForEveryone = @{ param.clone().setState(param.state).setAssigneeId(null).setAuthorId(null).setCommenterId(null) }
+@paramForIssuesAssignedToMe = @{ param.clone().setState(param.state).setAssigneeId(UserApp.currentUser().id).setAuthorId(null).setCommenterId(null) }
+@paramForIssuesAuthoredByMe = @{ param.clone().setState(param.state).setAssigneeId(null).setAuthorId(UserApp.currentUser().id).setCommenterId(null) }
+@paramForIssuesCommentedByMe = @{ param.clone().setState(param.state).setAssigneeId(null).setAuthorId(null).setCommenterId(UserApp.currentUser().id) }
 
 <ul class="lst-stacked unstyled">
-    <li @if(param.assigneeId == null && param.authorId == null){class="active"}>
+    <li @if(param.assigneeId == null && param.authorId == null && param.commenterId == null){class="active"}>
         <a pjax-filter href="#"
-           data-assignee-id=""
-           data-author-id=""
-           data-milestone-id="@param.milestoneId">
-            @Messages("issue.list.all")
+            data-assignee-id=""
+            data-author-id=""
+            data-commenter-id=""
+            data-milestone-id="@param.milestoneId">
+            @if(param.state.equals("closed")){
+                @Messages("issue.list.all.closed")
+            } else {
+                @Messages("issue.list.all.open")
+            }
             <span class="num-badge pull-right">@Issue.countIssuesBy(project.id, paramForEveryone)</span>
         </a>
     </li>
     @if(!UserApp.currentUser().isAnonymous){
     <li @if(param.assigneeId == UserApp.currentUser().id){ class="active"}>
         <a pjax-filter href="#"
-           data-assignee-id="@UserApp.currentUser.id"
-           data-author-id=""
-           data-milestone-id="@param.milestoneId">
+            data-assignee-id="@UserApp.currentUser.id"
+            data-author-id=""
+            data-commenter-id=""
+            data-milestone-id="@param.milestoneId">
             @Messages("issue.list.assignedToMe")
             <span class="num-badge pull-right">@Issue.countIssuesBy(project.id, paramForIssuesAssignedToMe)</span>
         </a>
     </li>
     <li @if(param.authorId == UserApp.currentUser().id){ class="active"}>
         <a pjax-filter href="#"
-           data-assignee-id=""
-           data-author-id="@UserApp.currentUser.id"
-           data-milestone-id="@param.milestoneId">
+            data-assignee-id=""
+            data-author-id="@UserApp.currentUser.id"
+            data-commenter-id=""
+            data-milestone-id="@param.milestoneId">
             @Messages("issue.list.authoredByMe")
             <span class="num-badge pull-right">@Issue.countIssuesBy(project.id, paramForIssuesAuthoredByMe)</span>
         </a>
     </li>
+    <li @if(param.commenterId == UserApp.currentUser().id){ class="active"}>
+        <a pjax-filter href="#"
+            data-assignee-id=""
+            data-author-id=""
+            data-commenter-id="@UserApp.currentUser.id"
+            data-milestone-id="@param.milestoneId">
+            @Messages("issue.list.commentedByMe")
+            <span class="num-badge pull-right">@Issue.countIssuesBy(project.id, paramForIssuesCommentedByMe)</span>
+        </a>
+    </li>
     }
 </ul>
app/views/issue/partial_searchform.scala.html
--- app/views/issue/partial_searchform.scala.html
+++ app/views/issue/partial_searchform.scala.html
@@ -26,6 +26,7 @@
     <input type="hidden" name="orderBy" value="@param.orderBy">
     <input type="hidden" name="orderDir" value="@param.orderDir">
     <input type="hidden" name="state" value="@param.state">
+    <input type="hidden" name="commenterId" value="@param.commenterId" data-search="commenterId">
     <hr class="hide-in-mobile">
     <div class="search">
         <div class="search-bar">
conf/messages
--- conf/messages
+++ conf/messages
@@ -263,8 +263,11 @@
 issue.is.empty = No issue found
 issue.label = Issue Label
 issue.list.all = All issues
+issue.list.all.closed = All closed issues
+issue.list.all.open = All open issues
 issue.list.assignedToMe = Assigned to me
 issue.list.authoredByMe = Created by me
+issue.list.commentedByMe = Commented by me
 issue.list.mentionedOfMe = Mentioned of me
 issue.menu.new = New issue
 issue.myIssue = My issues
conf/messages.ko-KR
--- conf/messages.ko-KR
+++ conf/messages.ko-KR
@@ -263,8 +263,11 @@
 issue.is.empty = 등록된 이슈가 없습니다.
 issue.label = 이슈 라벨
 issue.list.all = 전체 이슈
+issue.list.all.closed = 전체 닫힌 이슈
+issue.list.all.open = 전체 열린 이슈
 issue.list.assignedToMe = 나에게 할당된 이슈
 issue.list.authoredByMe = 내가 작성한 이슈
+issue.list.commentedByMe = 내가 코멘트를 남긴 이슈
 issue.list.mentionedOfMe = 나를 언급한 이슈
 issue.menu.new = 새 이슈
 issue.myIssue = 내 이슈
Add a comment
List