채수원 2014-02-24
Merge branch `pullrequest/search` of kjkmadness/yobi
from pull request 617
@54b7db7bdadb0154941750e6f5c9091e0bc75bce
app/controllers/PullRequestApp.java
--- app/controllers/PullRequestApp.java
+++ app/controllers/PullRequestApp.java
@@ -51,6 +51,8 @@
 
 import javax.servlet.ServletException;
 import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.List;
@@ -184,7 +186,7 @@
         } catch (Exception e) {
             play.Logger.error(MessageFormat.format("Failed to fork \"{0}\"", originalProject), e);
             result.put(status, failed);
-            result.put(url, routes.PullRequestApp.pullRequests(originalProject.owner, originalProject.name, 1).url());
+            result.put(url, routes.PullRequestApp.pullRequests(originalProject.owner, originalProject.name).url());
             return ok(result);
         }
     }
@@ -350,10 +352,8 @@
      * @return
      */
     @IsAllowed(Operation.READ)
-    public static Result pullRequests(String userName, String projectName, int pageNum) {
-        Project project = Project.findByOwnerAndProjectName(userName, projectName);
-        Page<PullRequest> page = PullRequest.findPagingList(State.OPEN, project, pageNum - 1);
-        return ok(list.render(project, page, "opened"));
+    public static Result pullRequests(String userName, String projectName) {
+        return pullRequests(userName, projectName, Category.OPEN);
     }
 
     /**
@@ -364,10 +364,8 @@
      * @return
      */
     @IsAllowed(Operation.READ)
-    public static Result closedPullRequests(String userName, String projectName, int pageNum) {
-        Project project = Project.findByOwnerAndProjectName(userName, projectName);
-        Page<PullRequest> page = PullRequest.findClosedPagingList(project, pageNum - 1);
-        return ok(list.render(project, page, "closed"));
+    public static Result closedPullRequests(String userName, String projectName) {
+        return pullRequests(userName, projectName, Category.CLOSED);
     }
 
     /**
@@ -378,10 +376,28 @@
      * @return
      */
     @IsAllowed(Operation.READ)
-    public static Result sentPullRequests(String userName, String projectName, int pageNum) {
+    public static Result sentPullRequests(String userName, String projectName) {
+        return pullRequests(userName, projectName, Category.SENT);
+    }
+
+    /**
+     * 코드-주고받기 목록을 조회한다.
+     *
+     * @param userName
+     * @param projectName
+     * @param category
+     * @return
+     */
+    private static Result pullRequests(String userName, String projectName, Category category) {
         Project project = Project.findByOwnerAndProjectName(userName, projectName);
-        Page<PullRequest> page = PullRequest.findSentPullRequests(project, pageNum - 1);
-        return ok(list.render(project, page, "sent"));
+        SearchCondition condition = Form.form(SearchCondition.class).bindFromRequest().get();
+        condition.setProject(project).setCategory(category);
+        Page<PullRequest> page = PullRequest.findPagingList(condition);
+        if (HttpUtil.isPJAXRequest(request())) {
+            return ok(partial_search.render(project, page, condition, category.code));
+        } else {
+            return ok(list.render(project, page, condition, category.code));
+        }
     }
 
     /**
@@ -791,4 +807,106 @@
         }
 
     }
+
+    /**
+     * 코드-주고받기 검색 조건
+     */
+    public static class SearchCondition {
+        public Project project;
+        public String filter;
+        public Long contributorId;
+        public int pageNum = Constants.DEFAULT_PAGE;
+        public Category category;
+
+        public SearchCondition setProject(Project project) {
+            this.project = project;
+            return this;
+        }
+
+        public SearchCondition setFilter(String filter) {
+            this.filter = filter;
+            return this;
+        }
+
+        public SearchCondition setContributorId(Long contributorId) {
+            this.contributorId = contributorId;
+            return this;
+        }
+
+        public SearchCondition setPageNum(int pageNum) {
+            this.pageNum = pageNum;
+            return this;
+        }
+
+        public SearchCondition setCategory(Category category) {
+            this.category = category;
+            return this;
+        }
+
+        @Override
+        public SearchCondition clone() throws CloneNotSupportedException {
+            SearchCondition clone = new SearchCondition();
+            clone.project = this.project;
+            clone.filter = this.filter;
+            clone.contributorId = this.contributorId;
+            clone.pageNum = this.pageNum;
+            clone.category = this.category;
+            return clone;
+        }
+
+        public String queryString() throws UnsupportedEncodingException {
+            List<String> queryStrings = new ArrayList<>();
+            if (StringUtils.isNotBlank(filter)) {
+                queryStrings.add("filter=" + URLEncoder.encode(filter, "UTF-8"));
+            }
+            if (category != Category.SENT && contributorId != null) {
+                queryStrings.add("contributorId=" + contributorId);
+            }
+            if (pageNum != Constants.DEFAULT_PAGE) {
+                queryStrings.add("pageNum=" + pageNum);
+            }
+            if (queryStrings.isEmpty()) {
+                return StringUtils.EMPTY;
+            }
+            return "?" + StringUtils.join(queryStrings, "&");
+        }
+    }
+
+    /**
+     * 코드-주고받기 카테고리
+     */
+    public enum Category {
+        OPEN("open", "toProject", "number", State.OPEN),
+        CLOSED("closed", "toProject", "received", State.CLOSED, State.MERGED),
+        SENT("sent", "fromProject", "created"),
+        ACCEPTED("accepted", "fromProject", "created", State.MERGED);
+
+        private Category(String code, String project, String order, State... states) {
+            this.code = code;
+            this.project = project;
+            this.order = order;
+            this.states = states;
+        }
+
+        private String code;
+        private String project;
+        private String order;
+        private State[] states;
+
+        public String code() {
+            return code;
+        }
+
+        public String project() {
+            return project;
+        }
+
+        public String order() {
+            return order;
+        }
+
+        public State[] states() {
+            return states;
+        }
+    }
 }
app/models/PullRequest.java
--- app/models/PullRequest.java
+++ app/models/PullRequest.java
@@ -3,8 +3,12 @@
 import actors.RelatedPullRequestMergingActor;
 import akka.actor.Props;
 import com.avaje.ebean.Expr;
+import com.avaje.ebean.Expression;
 import com.avaje.ebean.ExpressionList;
+import com.avaje.ebean.Junction;
 import com.avaje.ebean.Page;
+
+import controllers.PullRequestApp.SearchCondition;
 import controllers.UserApp;
 import controllers.routes;
 import models.enumeration.EventType;
@@ -12,6 +16,8 @@
 import models.enumeration.State;
 import models.resource.Resource;
 import models.resource.ResourceConvertible;
+
+import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.api.MergeResult;
@@ -44,7 +50,7 @@
 import java.io.IOException;
 import java.util.*;
 
-import static com.avaje.ebean.Expr.eq;
+import static com.avaje.ebean.Expr.*;
 
 @Entity
 public class PullRequest extends Model implements ResourceConvertible {
@@ -577,55 +583,78 @@
     }
 
     /**
-     * {@code project}에서 {@code state}에 해당하는 풀리퀘 목록 중 한 페이지를 가져온다.
+     * {@code condition} 에 해당하는 코드-주고받기 목록 중 한 페이지를 가져온다.
+     * {@code SearchCondition.pageNum}은 0부터 시작하고, 한 페이지당 {@code ITEMS_PER_PAGE} 만큼 가져온다.
      *
-     * {@code pageNum}은 0부터 시작하고, 한 페이지당 {@code ITEMS_PER_PAGE} 만큼 가져온다.
-     *
-     * @param state
-     * @param project
-     * @param pageNum
+     * @param condition
      * @return
      */
-    public static Page<PullRequest> findPagingList(State state, Project project, int pageNum) {
-        return finder.where()
-                .eq("toProject", project)
-                .eq("state", state)
-                .order().desc("number")
+    public static Page<PullRequest> findPagingList(SearchCondition condition) {
+        return createSearchExpressionList(condition)
+                .order().desc(condition.category.order())
                 .findPagingList(ITEMS_PER_PAGE)
-                .getPage(pageNum);
+                .getPage(condition.pageNum - 1);
     }
 
     /**
-     * 받은 코드 목록을 반환한다.
+     * {@code condition} 에 해당하는 코드-주고받기 갯수를 가져온다.
      *
-     * 받은 코드는 닫혔(Closed)거나 병합(Merged)된 코드이다.
-     * @param project
-     * @param pageNum
+     * @param condition
      * @return
      */
-    public static Page<PullRequest> findClosedPagingList(Project project, int pageNum) {
-        return finder.where()
-                .eq("toProject",  project)
-                .or(eq("state", State.CLOSED), eq("state", State.MERGED))
-                .order().desc("received")
-                .findPagingList(ITEMS_PER_PAGE)
-                .getPage(pageNum);
+    public static int count(SearchCondition condition) {
+        return createSearchExpressionList(condition).findRowCount();
     }
-    /**
-     * {@code project}에서 보낸 풀리퀘 목록 중 한 페이지를 가져온다.
-     *
-     * {@code pageNum}은 0부터 시작하고, 한 페이지당 {@code ITEMS_PER_PAGE} 만큼 가져온다.
-     *
-     * @param project
-     * @param pageNum
-     * @return
+
+    /*
+     * 지정된 검색조건에 따라서 검색 표현식을 만든다.
      */
-    public static Page<PullRequest> findSentPullRequests(Project project, int pageNum) {
-        return finder.where()
-                .eq("fromProject", project)
-                .order().desc("created")
-                .findPagingList(ITEMS_PER_PAGE)
-                .getPage(pageNum);
+    private static ExpressionList<PullRequest> createSearchExpressionList(SearchCondition condition) {
+        ExpressionList<PullRequest> el = finder.where();
+        if (condition.project != null) {
+            el.eq(condition.category.project(), condition.project);
+        }
+        Expression state = createStateSearchExpression(condition.category.states());
+        if (state != null) {
+            el.add(state);
+        }
+        if (condition.contributorId != null) {
+            el.eq("contributor.id", condition.contributorId);
+        }
+        if (StringUtils.isNotBlank(condition.filter)) {
+            Set<Object> ids = new HashSet<>();
+            ids.addAll(el.query().copy().where()
+                    .icontains("comments.contents", condition.filter).findIds());
+            ids.addAll(el.query().copy().where()
+                    .eq("pullRequestCommits.state", PullRequestCommit.State.CURRENT)
+                    .or(
+                            icontains("pullRequestCommits.commitMessage", condition.filter),
+                            icontains("pullRequestCommits.commitId", condition.filter))
+                    .findIds());
+            Junction<PullRequest> junction = el.disjunction();
+            junction.icontains("title", condition.filter).icontains("body", condition.filter)
+                    .icontains("mergedCommitIdTo", condition.filter);
+            if (!ids.isEmpty()) {
+                junction.in("id", ids);
+            }
+            junction.endJunction();
+        }
+        return el;
+    }
+
+    /*
+     * 지정된 states 에 따라서 검색 표현식을 만든다.
+     */
+    private static Expression createStateSearchExpression(State[] states) {
+        int stateCount = ArrayUtils.getLength(states);
+        switch (stateCount) {
+            case 0:
+                return null;
+            case 1:
+                return eq("state", states[0]);
+            default:
+                return in("state", states);
+        }
     }
 
     /**
@@ -978,5 +1007,4 @@
     public int getLackingReviewerCount() {
         return toProject.defaultReviewerCount - reviewers.size();
     }
-
 }
app/models/User.java
--- app/models/User.java
+++ app/models/User.java
@@ -487,11 +487,35 @@
         }
     }
 
+    /**
+     * {@code projectId} 에 해당하는 project 에 이슈를 작성한 모든 사용자 조회
+     *
+     * @param projectId
+     * @return
+     */
     public static List<User> findIssueAuthorsByProjectId(long projectId) {
         String sql = "select user.id, user.name, user.login_id from issue issue, n4user user where issue.author_id = user.id group by issue.author_id";
-        RawSql rawSql = RawSqlBuilder.parse(sql).columnMapping("user.login_id", "loginId").create();
+        return createUserSearchQueryWithRawSql(sql).where()
+                .eq("issue.project_id", projectId)
+                .findList();
+    }
 
-        return User.find.setRawSql(rawSql).where().eq("issue.project_id", projectId).findList();
+    /**
+     * {@code projectId} 에 해당하는 project 에 코드-주고받기를 보낸 모든 사용자 조회
+     *
+     * @param projectId
+     * @return
+     */
+    public static List<User> findPullRequestContributorsByProjectId(long projectId) {
+        String sql = "SELECT user.id, user.name, user.login_id FROM pull_request pullrequest, n4user user WHERE pullrequest.contributor_id = user.id GROUP BY pullrequest.contributor_id";
+        return createUserSearchQueryWithRawSql(sql).where()
+                .eq("pullrequest.to_project_id", projectId)
+                .findList();
+    }
+
+    private static com.avaje.ebean.Query<User> createUserSearchQueryWithRawSql(String sql) {
+        RawSql rawSql = RawSqlBuilder.parse(sql).columnMapping("user.login_id", "loginId").create();
+        return User.find.setRawSql(rawSql);
     }
 
     /**
app/utils/Constants.java
--- app/utils/Constants.java
+++ app/utils/Constants.java
@@ -12,4 +12,6 @@
 
     public static final String DEFAULT_LANGUAGE = "en";
     public static final String NEW_LINE_DELIMETER = "\n";
+
+    public static final int DEFAULT_PAGE = 1;
 }
app/views/git/list.scala.html
--- app/views/git/list.scala.html
+++ app/views/git/list.scala.html
@@ -1,59 +1,42 @@
-@(project: Project, page: com.avaje.ebean.Page[PullRequest], requestType: String)
-@import utils.AccessControl
+@(project: Project, page: com.avaje.ebean.Page[PullRequest],
+    condition: controllers.PullRequestApp.SearchCondition, requestType: String)
 
 @projectLayout(Messages("menu.pullRequest"), project, utils.MenuType.PULL_REQUEST) {
 @projectMenu(project, utils.MenuType.PULL_REQUEST, "main-menu-only")
 <div class="page-wrap-outer">
     <div class="project-page-wrap">
-        @if(project.isForkedFromOrigin) {
-            @partial_recently_pushed_branches(project)
-        } else {
-            @defining(Project.findByOwnerAndOriginalProject(UserApp.currentUser().loginId, project)){ myForkProject =>
-                @if(myForkProject != null && AccessControl.isProjectResourceCreatable(UserApp.currentUser(), project, ResourceType.PULL_REQUEST)){
-                    @partial_recently_pushed_branches(myForkProject)
-                }
-            }
-        }
-        <div class="pull-right">
-            <a href="@routes.PullRequestApp.newPullRequestForm(project.owner, project.name)" class="ybtn ybtn-success">
-                <i class="yobicon-merge yobicon-large"></i>
-                @Messages("pullRequest")
-            </a>
-        </div>
-        <ul class="nav nav-tabs nm">
-            <li @if(requestType.equals("opened")){class="active"}>
-                <a href="@routes.PullRequestApp.pullRequests(project.owner, project.name)">
-                    @Messages("pullRequest.opened.code")
-                    <span class="num-badge">@PullRequest.findOpendPullRequests(project).size</span>
-                </a>
-            </li>
-            <li @if(requestType.equals("closed")){class="active"}>
-                <a href="@routes.PullRequestApp.closedPullRequests(project.owner, project.name)">
-                    @Messages("pullRequest.accepted.code")
-                    <span class="num-badge">@PullRequest.findClosedPullRequests(project).size</span>
-                </a>
-            </li>
-        @if(project.isForkedFromOrigin()){
-            <li @if(requestType.equals("sent")){class="active"}>
-                <a href="@routes.PullRequestApp.sentPullRequests(project.owner, project.name)">
-                    @Messages("pullRequest.sent.code")
-                    <span class="num-badge">@PullRequest.findAcceptedPullRequests(project).size / @PullRequest.findSentPullRequests(project).size</span>
-                </a>
-            </li>
-            }
-        </ul>
-
-        <div class="tab-content" style="clear:both;padding-top:15px;">
-            <div id="list" class="row-fluid tab-pane active">
-                @partial_list(project, page)
-            </div>
-        </div>
+    @views.html.git.partial_search(project, page, condition, requestType)
     </div>
 </div>
 <script type="text/javascript">
-    $(function(){
-        $('span[data-toggle="tooltip"]').tooltip({
-            placement : "bottom"
+    $(document).ready(function(){
+        var htPjaxOptions = {
+            "fragment": "div[pjax-container]",
+            "timeout" : 3000
+        };
+
+        if($.support.pjax) {
+            $.pjax.defaults.maxCacheLength = 0;
+        }
+
+        // on click pagination
+        $(document).on("click", "a[pjax-page]", function(weEvt) {
+            $.pjax.click(weEvt, "div[pjax-container]", htPjaxOptions);
+        });
+
+        // on submit search form
+        $(document).on("submit", "form[name='search']", function(weEvt) {
+            $.pjax.submit(weEvt, "div[pjax-container]", htPjaxOptions);
+        });
+
+        // show spinners
+        $(document).on({
+            "pjax:send": function(){
+                yobi.ui.Spinner.show();
+            },
+            "pjax:complete": function(){
+                yobi.ui.Spinner.hide();
+            }
         });
     });
 </script>
app/views/git/partial_list.scala.html
--- app/views/git/partial_list.scala.html
+++ app/views/git/partial_list.scala.html
@@ -72,9 +72,7 @@
 
 <script type="text/javascript">
     $(function(){
-        $('span[data-toggle="tooltip"]').tooltip({
-            placement : "bottom"
-        });
+        $('[data-toggle="tooltip"]').tooltip();
         var welPagination = $("#pagination");
         yobi.Pagination.update(welPagination, @page.getTotalPageCount());
     });
 
app/views/git/partial_search.scala.html (added)
+++ app/views/git/partial_search.scala.html
@@ -0,0 +1,141 @@
+@**
+* Yobi, Project Hosting SW
+*
+* Copyright 2013 NAVER Corp.
+* http://yobi.io
+*
+* @Author kjkmadness
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+**@
+@(project: Project, page: com.avaje.ebean.Page[PullRequest],
+    condition: controllers.PullRequestApp.SearchCondition, requestType: String)
+
+@import utils.AccessControl
+@import controllers.PullRequestApp.Category
+
+@conditionForOpen = @{condition.clone.setCategory(Category.OPEN)}
+@conditionForClosed = @{condition.clone.setCategory(Category.CLOSED)}
+@conditionForAccepted = @{condition.clone.setCategory(Category.ACCEPTED)}
+@conditionForSent = @{condition.clone.setCategory(Category.SENT)}
+
+@searchFormAction(category: Category) = @{
+  category match {
+    case Category.SENT => {
+      routes.PullRequestApp.sentPullRequests(project.owner, project.name)
+    }
+    case Category.CLOSED => {
+      routes.PullRequestApp.closedPullRequests(project.owner, project.name)
+    }
+    case _ => {
+      routes.PullRequestApp.pullRequests(project.owner, project.name)
+    }
+  }
+}
+
+  <div pjax-container class="row-fluid cb">
+    <div class="span2 search-wrap">
+      <form id="search" name="search" action="@searchFormAction(condition.category)" method="get">
+        <div class="search">
+          <div class="search-bar">
+            <input name="filter" class="textbox full" type="text" value="@condition.filter">
+            <button type="submit" class="search-btn"><i class="yobicon-search"></i></button>
+          </div>
+        </div>
+        @if(condition.category != Category.SENT){
+        <div id="advanced-search-form" class="srch-advanced">
+          <dl class="issue-option">
+            <dt>@Messages("pullRequest.sender")</dt>
+            <dd>
+              <div class="btn-group" data-name="contributorId">
+                <button class="btn dropdown-toggle auto" data-toggle="dropdown">
+                  <span class="d-label">@Messages("common.order.all")</span>
+                  <span class="d-caret"><span class="caret"></span></span>
+                </button>
+                <ul class="dropdown-menu">
+                  <li data-value=""><a>@Messages("common.order.all")</a></li>
+                  <li class="divider"></li>
+                  @for(contributor <- User.findPullRequestContributorsByProjectId(project.id)) {
+                  <li data-value="@contributor.id" @if(condition.contributorId != null && condition.contributorId == contributor.id){data-selected="true" class="active"}>
+                    <a class="usf-group">
+                      <span class="avatar-wrap smaller">
+                        <img src="@contributor.avatarUrl" width="20" height="20">
+                      </span>
+                      <strong class="name">@contributor.name</strong>
+                      <span class="loginid"> <strong>@{"@"}</strong>@contributor.loginId</span>
+                    </a>
+                  </li>
+                  }
+                </ul>
+              </div>
+            </dd>
+          </dl>
+        </div>
+        }
+      </form>
+    </div>
+    <div class="span10" id="span10">
+      @if(project.isForkedFromOrigin) {
+        @partial_recently_pushed_branches(project)
+      } else {
+        @defining(Project.findByOwnerAndOriginalProject(UserApp.currentUser().loginId, project)){ myForkProject =>
+          @if(myForkProject != null && AccessControl.isProjectResourceCreatable(UserApp.currentUser(), project, ResourceType.PULL_REQUEST)){
+            @partial_recently_pushed_branches(myForkProject)
+          }
+        }
+      }
+      <div class="pull-right">
+        <a href="@routes.PullRequestApp.newPullRequestForm(project.owner, project.name)" class="ybtn ybtn-success">
+          <i class="yobicon-merge yobicon-large"></i>
+            @Messages("pullRequest")
+        </a>
+      </div>
+      <ul class="nav nav-tabs nm">
+        <li @if(requestType.equals("open")){class="active"}>
+          <a href="@routes.PullRequestApp.pullRequests(project.owner, project.name)@conditionForOpen.queryString" pjax-page>
+            @Messages("pullRequest.opened.code")
+            <span class="num-badge">@PullRequest.count(conditionForOpen)</span>
+          </a>
+        </li>
+        <li @if(requestType.equals("closed")){class="active"}>
+          <a href="@routes.PullRequestApp.closedPullRequests(project.owner, project.name)@conditionForClosed.queryString" pjax-page>
+            @Messages("pullRequest.accepted.code")
+            <span class="num-badge">@PullRequest.count(conditionForClosed)</span>
+          </a>
+        </li>
+        @if(project.isForkedFromOrigin()){
+        <li @if(requestType.equals("sent")){class="active"}>
+          <a href="@routes.PullRequestApp.sentPullRequests(project.owner, project.name)@conditionForSent.queryString" pjax-page>
+            @Messages("pullRequest.sent.code")
+            <span class="num-badge">@PullRequest.count(conditionForAccepted) / @PullRequest.count(conditionForSent)</span>
+          </a>
+        </li>
+        }
+    </ul>
+    <div class="tab-content" style="clear:both;padding-top:15px;">
+      <div id="list" class="row-fluid tab-pane active">
+        @partial_list(project, page)
+      </div>
+    </div>
+    </div>
+    <script type="text/javascript">
+        $(document).ready(function(){
+            var oSearchContributor = new yobi.ui.Dropdown({
+                'elContainer': $('div[data-name="contributorId"]'),
+                'fOnChange' : function() {
+                    $('#search').submit();
+                }
+            });
+        });
+    </script>
+  </div>
conf/messages
--- conf/messages
+++ conf/messages
@@ -513,6 +513,7 @@
 pullRequest.restore.frombranch.message = can be restored.
 pullRequest.reviewed = Reviewed
 pullRequest.select.branch = Select Branch
+pullRequest.sender = Sender
 pullRequest.sent.code = Sent Code
 pullRequest.state.closed = Closed
 pullRequest.state.conflict = Conflict
conf/messages.ko
--- conf/messages.ko
+++ conf/messages.ko
@@ -514,6 +514,7 @@
 pullRequest.restore.frombranch.message = 브랜치를 복구할 수 있습니다.
 pullRequest.reviewed = 리뷰 완료
 pullRequest.select.branch = 브랜치를 선택하세요.
+pullRequest.sender = 보낸 사람
 pullRequest.sent.code = 보낸 코드
 pullRequest.state.closed = 닫힘
 pullRequest.state.conflict = 충돌
conf/routes
--- conf/routes
+++ conf/routes
@@ -156,10 +156,10 @@
 GET            /:ownerName/:project/newFork                                           controllers.PullRequestApp.newFork(ownerName:String, project:String)
 POST           /:ownerName/:project/fork                                              controllers.PullRequestApp.fork(ownerName:String, project:String)
 POST           /:ownerName/:project/clone                                             controllers.PullRequestApp.doClone(ownerName:String, project:String, name:String, isPublic:Boolean)
-GET            /:ownerName/:project/pullRequests                                      controllers.PullRequestApp.pullRequests(ownerName, project, pageNum: Int ?= 1)
+GET            /:ownerName/:project/pullRequests                                      controllers.PullRequestApp.pullRequests(ownerName, project)
 
-GET            /:ownerName/:project/closedPullRequests                                controllers.PullRequestApp.closedPullRequests(ownerName, project, pageNum: Int ?= 1)
-GET            /:ownerName/:project/sentPullRequests                                  controllers.PullRequestApp.sentPullRequests(ownerName, project, pageNum: Int ?= 1)
+GET            /:ownerName/:project/closedPullRequests                                controllers.PullRequestApp.closedPullRequests(ownerName, project)
+GET            /:ownerName/:project/sentPullRequests                                  controllers.PullRequestApp.sentPullRequests(ownerName, project)
 GET            /:ownerName/:project/pullRequest/:id                                   controllers.PullRequestApp.pullRequest(ownerName, project, id: Long)
 GET            /:ownerName/:project/pullRequest/:id/commits                           controllers.PullRequestApp.pullRequestCommits(ownerName, project, id: Long)
 GET            /:ownerName/:project/pullRequest/:id/changes                           controllers.PullRequestApp.pullRequestChanges(ownerName, project, id: Long)
Add a comment
List