Group: Add pull request menu (#260)
@f8ed345f5b4a6f10ed362805f67a57850261074a
--- app/controllers/IssueApp.java
+++ app/controllers/IssueApp.java
... | ... | @@ -44,6 +44,7 @@ |
44 | 44 |
|
45 | 45 |
@AnonymousCheck(requiresLogin = false, displaysFlashMessage = true) |
46 | 46 |
public static Result organizationIssues(@Nonnull String organizationName, @Nonnull String state, @Nonnull String format, int pageNum) throws WriteException, IOException { |
47 |
+ |
|
47 | 48 |
// SearchCondition from param |
48 | 49 |
Form<models.support.SearchCondition> issueParamForm = new Form<>(models.support.SearchCondition.class); |
49 | 50 |
models.support.SearchCondition searchCondition = issueParamForm.bindFromRequest().get(); |
--- app/controllers/OrganizationApp.java
+++ app/controllers/OrganizationApp.java
... | ... | @@ -9,6 +9,8 @@ |
9 | 9 |
import com.avaje.ebean.Page; |
10 | 10 |
import controllers.annotation.AnonymousCheck; |
11 | 11 |
import controllers.annotation.GuestProhibit; |
12 |
+import controllers.PullRequestApp.SearchCondition; |
|
13 |
+import controllers.PullRequestApp.Category; |
|
12 | 14 |
import models.*; |
13 | 15 |
import models.enumeration.Operation; |
14 | 16 |
import models.enumeration.RequestState; |
... | ... | @@ -30,6 +32,7 @@ |
30 | 32 |
import views.html.organization.members; |
31 | 33 |
import views.html.organization.setting; |
32 | 34 |
import views.html.organization.view; |
35 |
+import views.html.organization.group_pullrequest_list; |
|
33 | 36 |
|
34 | 37 |
import javax.servlet.ServletException; |
35 | 38 |
import javax.validation.ConstraintViolation; |
... | ... | @@ -45,6 +48,26 @@ |
45 | 48 |
*/ |
46 | 49 |
@AnonymousCheck |
47 | 50 |
public class OrganizationApp extends Controller { |
51 |
+ |
|
52 |
+ @AnonymousCheck(requiresLogin = false, displaysFlashMessage = true) |
|
53 |
+ public static Result organizationPullRequests(String organizationName, String category) { |
|
54 |
+ |
|
55 |
+ Organization organization = Organization.findByName(organizationName); |
|
56 |
+ if (organization == null) { |
|
57 |
+ return notFound(ErrorViews.NotFound.render("error.notfound.organization")); |
|
58 |
+ } |
|
59 |
+ |
|
60 |
+ SearchCondition condition = Form.form(SearchCondition.class).bindFromRequest().get(); |
|
61 |
+ if (category == "open") |
|
62 |
+ condition.setOrganization(organization).setCategory(Category.OPEN); |
|
63 |
+ else |
|
64 |
+ condition.setOrganization(organization).setCategory(Category.CLOSED); |
|
65 |
+ Page<PullRequest> page = PullRequest.findPagingList(condition); |
|
66 |
+ |
|
67 |
+ return ok(group_pullrequest_list.render("title.pullrequest", organization, page, condition, category)); |
|
68 |
+ } |
|
69 |
+ |
|
70 |
+ |
|
48 | 71 |
/** |
49 | 72 |
* show New Group page |
50 | 73 |
* @return {@link Result} |
--- app/controllers/PullRequestApp.java
+++ app/controllers/PullRequestApp.java
... | ... | @@ -343,7 +343,7 @@ |
343 | 343 |
|
344 | 344 |
// Only members can access code? |
345 | 345 |
if(project.isCodeAccessibleMemberOnly && !project.hasMember(UserApp.currentUser())) { |
346 |
- return forbidden(ErrorViews.Forbidden.render("error.forbidden", project)); |
|
346 |
+ return forbidden(ErrorViews.Forbidden.render("error.forbidden", project)); |
|
347 | 347 |
} |
348 | 348 |
|
349 | 349 |
SearchCondition condition = Form.form(SearchCondition.class).bindFromRequest().get(); |
... | ... | @@ -697,6 +697,12 @@ |
697 | 697 |
public Long contributorId; |
698 | 698 |
public int pageNum = Constants.DEFAULT_PAGE; |
699 | 699 |
public Category category; |
700 |
+ public Organization organization; |
|
701 |
+ |
|
702 |
+ public SearchCondition setOrganization(Organization organization) { |
|
703 |
+ this.organization = organization; |
|
704 |
+ return this; |
|
705 |
+ } |
|
700 | 706 |
|
701 | 707 |
public SearchCondition setProject(Project project) { |
702 | 708 |
this.project = project; |
... | ... | @@ -731,6 +737,7 @@ |
731 | 737 |
clone.contributorId = this.contributorId; |
732 | 738 |
clone.pageNum = this.pageNum; |
733 | 739 |
clone.category = this.category; |
740 |
+ clone.organization = this.organization; |
|
734 | 741 |
return clone; |
735 | 742 |
} |
736 | 743 |
|
--- app/models/PullRequest.java
+++ app/models/PullRequest.java
... | ... | @@ -779,6 +779,15 @@ |
779 | 779 |
if (condition.project != null) { |
780 | 780 |
el.eq(condition.category.project(), condition.project); |
781 | 781 |
} |
782 |
+ if (condition.organization != null) { |
|
783 |
+ List<Project> projects = condition.organization.getVisibleProjects(UserApp.currentUser()); |
|
784 |
+ List<String> projectsIds = new ArrayList<>(); |
|
785 |
+ for (Project project : projects) { |
|
786 |
+ projectsIds.add(project.id.toString()); |
|
787 |
+ } |
|
788 |
+ el.in("to_project_id", projectsIds); |
|
789 |
+ el.in("from_project_id", projectsIds); |
|
790 |
+ } |
|
782 | 791 |
Expression state = createStateSearchExpression(condition.category.states()); |
783 | 792 |
if (state != null) { |
784 | 793 |
el.add(state); |
+++ app/views/organization/group_pullrequest_list.scala.html
... | ... | @@ -0,0 +1,76 @@ |
1 | +@** | |
2 | +* Yona, 21st Century Project Hosting SW | |
3 | +* | |
4 | +* Copyright Yona & Yobi Authors & NAVER Corp. | |
5 | +* https://yona.io | |
6 | +**@ | |
7 | +@(title: String, organization: Organization, currentPage: com.avaje.ebean.Page[PullRequest], | |
8 | + condition: controllers.PullRequestApp.SearchCondition, requestType: String) | |
9 | + | |
10 | +@import utils.AccessControl | |
11 | +@import controllers.PullRequestApp.Category | |
12 | +@import models.PushedBranch | |
13 | + | |
14 | +@conditionForOpen = @{condition.clone.setCategory(Category.OPEN)} | |
15 | +@conditionForClosed = @{condition.clone.setCategory(Category.CLOSED)} | |
16 | + | |
17 | +@searchFormAction(category: Category) = @{ | |
18 | + category match { | |
19 | + case Category.CLOSED => { | |
20 | + routes.OrganizationApp.organizationPullRequests(organization.name, "closed") | |
21 | + } | |
22 | + case Category.OPEN => { | |
23 | + routes.OrganizationApp.organizationPullRequests(organization.name, "open") | |
24 | + } | |
25 | + } | |
26 | +} | |
27 | + | |
28 | +@organizationLayout(organization.name, utils.MenuType.NONE, organization) { | |
29 | + @header(organization) | |
30 | + @menu(organization) | |
31 | + <div class="page-wrap-outer"> | |
32 | + <div class="project-page-wrap"> | |
33 | + <div pjax-container class="row-fluid cb"> | |
34 | + <div class="left-menu span2 search-wrap hide-in-mobile" style="padding-top:0;"> | |
35 | + <form id="search" name="search" action="@searchFormAction(condition.category)" method="get"> | |
36 | + <div class="search"> | |
37 | + <div class="search-bar"> | |
38 | + <input name="filter" class="textbox full" type="text" value="@condition.filter"> | |
39 | + <button type="submit" class="search-btn"><i class="yobicon-search"></i></button> | |
40 | + </div> | |
41 | + </div> | |
42 | + </form> | |
43 | + </div> | |
44 | + <div class="span10 span-hard-wrap" id="span10"> | |
45 | + <ul class="nav nav-tabs nm pullrequeset-tab-menu"> | |
46 | + <li @if(requestType.equals("open")){class="active"}> | |
47 | + <a href="#" data-url="@searchFormAction(Category.OPEN)" data-type="state"> | |
48 | + @Messages("pullRequest.state.open") | |
49 | + <span class="num-badge">@PullRequest.count(conditionForOpen)</span> | |
50 | + </a> | |
51 | + </li> | |
52 | + <li @if(requestType.equals("closed")){class="active"}> | |
53 | + <a href="#" data-url="@searchFormAction(Category.CLOSED)" data-type="state"> | |
54 | + @Messages("pullRequest.state.closed") | |
55 | + <span class="num-badge">@PullRequest.count(conditionForClosed)</span> | |
56 | + </a> | |
57 | + </li> | |
58 | + </ul> | |
59 | + <div class="tab-content" style="clear:both;padding-top:15px;"> | |
60 | + <div id="list" class="row-fluid tab-pane active"> | |
61 | + @views.html.organization.group_pullrequest_list_partial(organization, currentPage) | |
62 | + </div> | |
63 | + </div> | |
64 | + </div> | |
65 | + </div> | |
66 | + <script type="text/javascript"> | |
67 | + $(function(){ | |
68 | + $('.pullrequeset-tab-menu').on('click','[data-type="state"]',function() { | |
69 | + $('#search').attr('action', $(this).data('url')); | |
70 | + $('#search').submit(); | |
71 | + }); | |
72 | + }); | |
73 | + </script> | |
74 | + </div> | |
75 | + </div> | |
76 | +} |
+++ app/views/organization/group_pullrequest_list_partial.scala.html
... | ... | @@ -0,0 +1,101 @@ |
1 | +@** | |
2 | +* Yona, 21st Century Project Hosting SW | |
3 | +* | |
4 | +* Copyright Yona & Yobi Authors & NAVER Corp. | |
5 | +* https://yona.io | |
6 | +**@ | |
7 | +@(organization:Organization, page: com.avaje.ebean.Page[PullRequest]) | |
8 | + | |
9 | +@import utils.JodaDateUtil | |
10 | +@import utils.TemplateHelper._ | |
11 | +@import org.apache.commons.lang3.StringUtils | |
12 | + | |
13 | +<ul class="post-list-wrap"> | |
14 | +@if(page.getList.size> 0){ | |
15 | + @for(req <- page.getList.iterator) { | |
16 | + @defining(User.findByLoginId(req.contributor.loginId)){ user => | |
17 | + <li class="post-item title" href="@routes.PullRequestApp.pullRequest(req.toProject.owner, req.toProject.name, req.number)"> | |
18 | + <div class="span10 span-hard-wrap"> | |
19 | + <a href="@routes.UserApp.userInfo(user.loginId)" class="avatar-wrap mlarge" data-toggle="tooltip" data-placement="top" title="@user.loginId"> | |
20 | + @if(user.avatarUrl == UserApp.DEFAULT_AVATAR_URL) { | |
21 | + <img src="@urlToPicture(user.email, 32)"> | |
22 | + } else { | |
23 | + <img src="@user.avatarUrl" alt="@user.name" width="32" height="32"/> | |
24 | + } | |
25 | + </a> | |
26 | + <div class="title-wrap"> | |
27 | + <span class="post-id">@req.number</span> | |
28 | + @showHeaderWordsInBracketsIfExist(req.title) | |
29 | + <a href="@routes.PullRequestApp.pullRequest(req.toProject.owner, req.toProject.name, req.number)" class="title @if(req.isConflict == true) {conflict}"> | |
30 | + @removeHeaderWords(req.title) | |
31 | + </a> | |
32 | + </div> | |
33 | + <div class="infos"> | |
34 | + @if(user.name){ | |
35 | + <a href="@routes.UserApp.userInfo(user.loginId)" class="infos-item infos-link-item" data-toggle="tooltip" data-placement="top" title="@user.loginId"> | |
36 | + @user.name | |
37 | + </a> | |
38 | + } else { | |
39 | + <span class="infos-item">@Messages("issue.noAuthor")</span> | |
40 | + } | |
41 | + <span class="infos-item" title="@JodaDateUtil.getDateString(req.created)"> | |
42 | + @agoOrDateString(req.created) | |
43 | + </span> | |
44 | + <a href="@routes.ProjectApp.project(req.toProject.owner,req.toProject.name)" class="infos-link-item group-project-name"> | |
45 | + @req.toProject.name | |
46 | + </a> | |
47 | + | |
48 | + @if(!req.commentThreads.isEmpty){ | |
49 | + @defining(req.countCommentThreadsByState(CommentThread.ThreadState.CLOSED)){ countClosed => | |
50 | + <div class="infos-item" style="margin-right:20px;"> | |
51 | + <i class="infos-icon yobicon-post2 vmiddle"></i> | |
52 | + <div class="upload-progress"> | |
53 | + <div class="bar orange" style="width: @getPercent(countClosed.toDouble, req.commentThreads.size.toDouble)%;"></div> | |
54 | + </div> | |
55 | + <a href="@routes.PullRequestApp.pullRequestChanges(req.toProject.owner, req.toProject.name, req.number)" | |
56 | + data-toggle="tooltip" title="@Messages("pullRequest.review.closed") / @Messages("pullRequest.review.total")"> | |
57 | + <span>@countClosed</span> | |
58 | + <span class="gray-txt">/</span> | |
59 | + <span class="size total">@req.commentThreads.size</span> | |
60 | + </a> | |
61 | + </div> | |
62 | + } | |
63 | + } | |
64 | + </div> | |
65 | + </div> | |
66 | + <div class="span2 hide-in-mobile"> | |
67 | + <div class="mt5 pull-right hide-in-mobile"> | |
68 | + @if(req.receiver != null) { | |
69 | + <a href="@routes.UserApp.userInfo(req.receiver.loginId)" class="avatar-wrap assinee" data-toggle="tooltip" data-placement="top" title="" data-original-title="@req.receiver.name"> | |
70 | + <img src="@req.receiver.avatarUrl" width="32" height="32" alt="@req.receiver.name"> | |
71 | + </a> | |
72 | + } else { | |
73 | + <div class="empty-avatar-wrap"> </div> | |
74 | + } | |
75 | + </div> | |
76 | + <div class="state @if(req.isConflict == true) {conflict} else { @req.state.toString.toLowerCase} pull-right">@if(req.isConflict == true) {@Messages("pullRequest.state.conflict")} else {@Messages("pullRequest.state." + req.state.toString.toLowerCase)}</div> | |
77 | + </div> | |
78 | + </li> | |
79 | + } | |
80 | + } | |
81 | + <div id="pagination"></div> | |
82 | +} else { | |
83 | + <div class="error-wrap"> | |
84 | + <i class="ico ico-err1"></i> | |
85 | + <p>@Messages("pullRequest.is.empty")</p> | |
86 | + </div> | |
87 | +} | |
88 | +</ul> | |
89 | + | |
90 | +<script type="text/javascript"> | |
91 | + $(function() { | |
92 | + yobi.Pagination.update($("#pagination"), @page.getTotalPageCount); | |
93 | + (function _initImplicitTitlePrefix() { | |
94 | + $(".title-prefix").on("click", function() { | |
95 | + var filterInput = $("input[name*='filter']"); | |
96 | + filterInput.val($(this).text()); | |
97 | + filterInput.closest("form").submit(); | |
98 | + }); | |
99 | + })(); | |
100 | +} ); | |
101 | +</script> |
--- conf/messages
+++ conf/messages
... | ... | @@ -930,6 +930,7 @@ |
930 | 930 |
title.projectSetting = Project settings |
931 | 931 |
title.projectTransfer = Project Transfer |
932 | 932 |
title.projectWatchers = Watcher list |
933 |
+title.pullrequest = Pull Request |
|
933 | 934 |
title.recently.visited = Recently visited |
934 | 935 |
title.rememberMe = Stay logged in |
935 | 936 |
title.resetPassword = Reset password |
--- conf/routes
+++ conf/routes
... | ... | @@ -76,6 +76,7 @@ |
76 | 76 |
GET /organizations/:organizationName controllers.OrganizationApp.organization(organizationName: String) |
77 | 77 |
GET /organizations/:organizationName/issues controllers.IssueApp.organizationIssues(organizationName: String, state:String ?= "", format:String ?= "html", pageNum: Int ?= 1) |
78 | 78 |
GET /organizations/:organizationName/boards controllers.BoardApp.organizationBoards(organizationName: String, pageNum: Int ?= 1) |
79 |
+GET /organizations/:organizationName/pullrequests controllers.OrganizationApp.organizationPullRequests(organizationName: String, category: String ?= "open") |
|
79 | 80 |
GET /organizations/:organizationName/settingform controllers.OrganizationApp.settingForm(organizationName: String) |
80 | 81 |
GET /organizations/:organizationName/deleteForm controllers.OrganizationApp.deleteForm(organizationName: String) |
81 | 82 |
DELETE /organizations/:organizationName controllers.OrganizationApp.deleteOrganization(organizationName: String) |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?