
issue: Apply new UI and add Label feature.
And remove State and rename StateType to State.
@ed636e734dbff6075601fd352d793a27c5734602
--- app/controllers/IssueApp.java
+++ app/controllers/IssueApp.java
... | ... | @@ -14,6 +14,7 @@ |
14 | 14 |
import views.html.issue.*; |
15 | 15 |
|
16 | 16 |
import java.io.*; |
17 |
+import java.util.Set; |
|
17 | 18 |
|
18 | 19 |
public class IssueApp extends Controller { |
19 | 20 |
|
... | ... | @@ -22,16 +23,16 @@ |
22 | 23 |
* |
23 | 24 |
* @param projectName |
24 | 25 |
* 프로젝트 이름 |
25 |
- * @param statusType |
|
26 |
+ * @param state |
|
26 | 27 |
* 이슈 해결 상태 |
27 | 28 |
* @return |
28 | 29 |
*/ |
29 |
- public static Result issues(String userName, String projectName, String stateType) { |
|
30 |
+ public static Result issues(String userName, String projectName, String state) { |
|
30 | 31 |
Project project = ProjectApp.getProject(userName, projectName); |
31 | 32 |
Form<SearchCondition> issueParamForm = new Form<SearchCondition>(SearchCondition.class); |
32 | 33 |
SearchCondition issueParam = issueParamForm.bindFromRequest().get(); |
33 |
- Page<Issue> issues = Issue.find(project.name, issueParam.pageNum, |
|
34 |
- StateType.getValue(stateType), issueParam.sortBy, |
|
34 |
+ Page<Issue> issues = Issue.find(project.id, issueParam.pageNum, |
|
35 |
+ State.getValue(state), issueParam.sortBy, |
|
35 | 36 |
Direction.getValue(issueParam.orderBy), issueParam.filter, issueParam.milestone, |
36 | 37 |
issueParam.commentedCheck); |
37 | 38 |
return ok(issueList.render("title.issueList", issues, issueParam, project)); |
... | ... | @@ -43,6 +44,9 @@ |
43 | 44 |
if (issueInfo == null) { |
44 | 45 |
return ok(notExistingPage.render("title.post.notExistingPage", project)); |
45 | 46 |
} else { |
47 |
+ for (IssueLabel label: issueInfo.labels) { |
|
48 |
+ label.refresh(); |
|
49 |
+ } |
|
46 | 50 |
Form<IssueComment> commentForm = new Form<IssueComment>(IssueComment.class); |
47 | 51 |
Issue targetIssue = Issue.findById(issueId); |
48 | 52 |
Form<Issue> editForm = new Form<Issue>(Issue.class).fill(targetIssue); |
... | ... | @@ -65,15 +69,22 @@ |
65 | 69 |
newIssue.authorId = UserApp.currentUser().id; |
66 | 70 |
newIssue.authorName = UserApp.currentUser().name; |
67 | 71 |
newIssue.project = project; |
68 |
- newIssue.state = IssueState.ENROLLED; |
|
69 |
- newIssue.updateStateType(newIssue); |
|
72 |
+ newIssue.state = State.OPEN; |
|
73 |
+ |
|
74 |
+ String[] labelIds = request().body().asMultipartFormData().asFormUrlEncoded().get("labelIds[]"); |
|
75 |
+ if (labelIds != null) { |
|
76 |
+ for (String labelId: labelIds) { |
|
77 |
+ newIssue.labels.add(IssueLabel.findById(Long.parseLong(labelId))); |
|
78 |
+ } |
|
79 |
+ } |
|
80 |
+ |
|
70 | 81 |
Long issueId = Issue.create(newIssue); |
71 | 82 |
|
72 | 83 |
// Attach all of the files in the current user's temporary storage. |
73 | 84 |
Attachment.attachFiles(UserApp.currentUser().id, project.id, Resource.ISSUE_POST, issueId); |
74 | 85 |
} |
75 | 86 |
return redirect(routes.IssueApp.issues(project.owner, project.name, |
76 |
- StateType.ALL.stateType())); |
|
87 |
+ State.ALL.state())); |
|
77 | 88 |
} |
78 | 89 |
|
79 | 90 |
public static Result editIssue(String userName, String projectName, Long id) { |
... | ... | @@ -93,13 +104,12 @@ |
93 | 104 |
issue.id = id; |
94 | 105 |
issue.date = Issue.findById(id).date; |
95 | 106 |
issue.project = project; |
96 |
- issue.updateState(issue); |
|
97 | 107 |
Issue.edit(issue); |
98 | 108 |
|
99 | 109 |
// Attach the files in the current user's temporary storage. |
100 | 110 |
Attachment.attachFiles(UserApp.currentUser().id, project.id, Resource.ISSUE_POST, id); |
101 | 111 |
} |
102 |
- return redirect(routes.IssueApp.issues(project.owner, project.name, StateType.ALL.name())); |
|
112 |
+ return redirect(routes.IssueApp.issues(project.owner, project.name, State.ALL.name())); |
|
103 | 113 |
} |
104 | 114 |
|
105 | 115 |
public static Result deleteIssue(String userName, String projectName, Long issueId) { |
... | ... | @@ -107,7 +117,7 @@ |
107 | 117 |
|
108 | 118 |
Issue.delete(issueId); |
109 | 119 |
return redirect(routes.IssueApp.issues(project.owner, project.name, |
110 |
- StateType.ALL.stateType())); |
|
120 |
+ State.ALL.state())); |
|
111 | 121 |
} |
112 | 122 |
|
113 | 123 |
public static Result saveComment(String userName, String projectName, Long issueId) throws IOException { |
... | ... | @@ -140,16 +150,16 @@ |
140 | 150 |
return redirect(routes.IssueApp.issue(project.owner, project.name, issueId)); |
141 | 151 |
} |
142 | 152 |
|
143 |
- public static Result extractExcelFile(String userName, String projectName, String stateType) |
|
153 |
+ public static Result extractExcelFile(String userName, String projectName, String state) |
|
144 | 154 |
throws Exception { |
145 | 155 |
Project project = ProjectApp.getProject(userName, projectName); |
146 | 156 |
Form<SearchCondition> issueParamForm = new Form<SearchCondition>(SearchCondition.class); |
147 | 157 |
SearchCondition issueParam = issueParamForm.bindFromRequest().get(); |
148 |
- Page<Issue> issues = Issue.find(project.name, issueParam.pageNum, |
|
149 |
- StateType.getValue(stateType), issueParam.sortBy, |
|
158 |
+ Page<Issue> issues = Issue.find(project.id, issueParam.pageNum, |
|
159 |
+ State.getValue(state), issueParam.sortBy, |
|
150 | 160 |
Direction.getValue(issueParam.orderBy), issueParam.filter, issueParam.milestone, |
151 | 161 |
issueParam.commentedCheck); |
152 |
- Issue.excelSave(issues.getList(), project.name + "_" + stateType + "_filter_" |
|
162 |
+ Issue.excelSave(issues.getList(), project.name + "_" + state + "_filter_" |
|
153 | 163 |
+ issueParam.filter + "_milestone_" + issueParam.milestone); |
154 | 164 |
return ok(issueList.render("title.issueList", issues, issueParam, project)); |
155 | 165 |
} |
... | ... | @@ -162,4 +172,4 @@ |
162 | 172 |
public static Result getIssueDatil(){ |
163 | 173 |
return TODO; |
164 | 174 |
} |
165 |
-}(No newline at end of file) |
|
175 |
+} |
+++ app/controllers/IssueLabelApp.java
... | ... | @@ -0,0 +1,89 @@ |
1 | +package controllers; | |
2 | + | |
3 | +import java.util.List; | |
4 | +import java.util.Map; | |
5 | + | |
6 | +import controllers.ProjectApp; | |
7 | +import models.IssueLabel; | |
8 | +import models.Project; | |
9 | +import models.Issue; | |
10 | +import models.enumeration.Operation; | |
11 | +import models.enumeration.Resource; | |
12 | +import play.mvc.Controller; | |
13 | +import play.mvc.Result; | |
14 | +import utils.AccessControl; | |
15 | +import utils.RequestUtil; | |
16 | +import static play.libs.Json.toJson; | |
17 | +import play.Logger; | |
18 | + | |
19 | +import play.data.*; | |
20 | +import java.util.ArrayList; | |
21 | +import java.util.HashMap; | |
22 | + | |
23 | +public class IssueLabelApp extends Controller { | |
24 | + | |
25 | + public static Result getAll(String userName, String projectName) { | |
26 | + Project project = ProjectApp.getProject(userName, projectName); | |
27 | + | |
28 | + List<Map<String, String>> labels = new ArrayList<Map<String, String>>(); | |
29 | + for (IssueLabel label : IssueLabel.findByProjectId(project.id)) { | |
30 | + Map<String, String> labelPropertyMap = new HashMap<String, String>(); | |
31 | + labelPropertyMap.put("id", "" + label.id); | |
32 | + labelPropertyMap.put("category", label.category); | |
33 | + labelPropertyMap.put("color", label.color); | |
34 | + labelPropertyMap.put("name", label.name); | |
35 | + labels.add(labelPropertyMap); | |
36 | + } | |
37 | + | |
38 | + response().setHeader("Content-Type", "application/json"); | |
39 | + return ok(toJson(labels)); | |
40 | + } | |
41 | + | |
42 | + public static Result post(String userName, String projectName) { | |
43 | + Form<IssueLabel> labelForm = new Form<IssueLabel>(IssueLabel.class).bindFromRequest(); | |
44 | + | |
45 | + IssueLabel label = labelForm.get(); | |
46 | + label.project = ProjectApp.getProject(userName, projectName); | |
47 | + | |
48 | + if (label.exists()) { | |
49 | + return ok(); | |
50 | + } else { | |
51 | + label.save(); | |
52 | + | |
53 | + response().setHeader("Content-Type", "application/json"); | |
54 | + | |
55 | + Map<String, String> labelPropertyMap = new HashMap<String, String>(); | |
56 | + labelPropertyMap.put("id", "" + label.id); | |
57 | + labelPropertyMap.put("name", label.name); | |
58 | + labelPropertyMap.put("color", label.color); | |
59 | + labelPropertyMap.put("category", label.category); | |
60 | + | |
61 | + return created(toJson(labelPropertyMap)); | |
62 | + } | |
63 | + } | |
64 | + | |
65 | + public static Result delete(String userName, String projectName, Long id) { | |
66 | + // _method must be 'delete' | |
67 | + DynamicForm bindedForm = form().bindFromRequest(); | |
68 | + if (!bindedForm.get("_method").toLowerCase() | |
69 | + .equals("delete")) { | |
70 | + return badRequest("_method must be 'delete'."); | |
71 | + } | |
72 | + | |
73 | + IssueLabel label = IssueLabel.findById(id); | |
74 | + | |
75 | + if (label == null) { | |
76 | + Logger.debug(id.toString()); | |
77 | + return notFound(); | |
78 | + } | |
79 | + | |
80 | + if (!AccessControl.isAllowed(UserApp.currentUser().id, label.project.id, | |
81 | + Resource.ISSUE_LABEL, Operation.DELETE, label.id)) { | |
82 | + return forbidden(); | |
83 | + } | |
84 | + | |
85 | + label.delete(); | |
86 | + | |
87 | + return ok(); | |
88 | + } | |
89 | +} |
--- app/controllers/MilestoneApp.java
+++ app/controllers/MilestoneApp.java
... | ... | @@ -32,7 +32,7 @@ |
32 | 32 |
MilestoneCondition mCondition = form(MilestoneCondition.class).bindFromRequest().get(); |
33 | 33 |
|
34 | 34 |
List<Milestone> milestones = Milestone.findMilestones(project.id, |
35 |
- StateType.getValue(mCondition.state), |
|
35 |
+ State.getValue(mCondition.state), |
|
36 | 36 |
mCondition.sort, |
37 | 37 |
Direction.getValue(mCondition.direction)); |
38 | 38 |
return ok(list.render("title.milestoneList", milestones, project, mCondition)); |
... | ... | @@ -70,7 +70,7 @@ |
70 | 70 |
} |
71 | 71 |
MilestoneCondition mCondition = form(MilestoneCondition.class).bindFromRequest().get(); |
72 | 72 |
List<Milestone> milestones = Milestone.findMilestones(project.id, |
73 |
- StateType.ALL, |
|
73 |
+ State.ALL, |
|
74 | 74 |
mCondition.sort, |
75 | 75 |
Direction.getValue(mCondition.direction)); |
76 | 76 |
return ok(manage.render("title.milestoneManage", milestones, project, mCondition)); |
--- app/models/Issue.java
+++ app/models/Issue.java
... | ... | @@ -27,20 +27,14 @@ |
27 | 27 |
* @param id 이슈 ID |
28 | 28 |
* @param title 이슈 제목 |
29 | 29 |
* @param body 이슈 내용 |
30 |
- * @param state 이슈 상태(등록, 진행중, 해결, 닫힘) |
|
31 |
- * @param statusType 이슈 상태, 등록 및 진행중 => 미해결, 해결 및 닫힘 => 해결 |
|
30 |
+ * @param state 이슈 상태(열림, 닫힘) |
|
32 | 31 |
* @param date 이슈 등록 날짜 |
33 | 32 |
* @param authorId 이슈 작성자 ID |
34 | 33 |
* @param project 이슈가 등록된 프로젝트 |
35 | 34 |
* @param issueType 이슈 상세정보의 유형 |
36 | 35 |
* @param assigneeId 이슈에 배정된 담당자 Id |
37 |
- * @param componentName 컴포넌트 |
|
38 | 36 |
* @param milestone 이슈가 등록된 마일스톤 |
39 | 37 |
* @param importance 이슈 상세정보의 중요도 |
40 |
- * @param diagnosisResult 이슈 상세정보의 진단유형 |
|
41 |
- * @param osType 이슈 상세정보의 OS 유형 |
|
42 |
- * @param browserType 이슈 상세정보의 브라우저 유형 |
|
43 |
- * @param dbmsType 이슈 상세정보의 DBMS 유형 |
|
44 | 38 |
* @author Taehyun Park |
45 | 39 |
* <p/> |
46 | 40 |
* Issue entity mangaed by Ebean |
... | ... | @@ -75,16 +69,7 @@ |
75 | 69 |
public Long assigneeId; |
76 | 70 |
public Long authorId; |
77 | 71 |
public String authorName; |
78 |
- public IssueState state; |
|
79 |
- public StateType stateType; |
|
80 |
- public String issueType; |
|
81 |
- public String componentName; |
|
82 |
- public String osType; |
|
83 |
- public String browserType; |
|
84 |
- public String dbmsType; |
|
85 |
- public String importance; |
|
86 |
- public String diagnosisResult; |
|
87 |
- |
|
72 |
+ public State state; |
|
88 | 73 |
@OneToMany |
89 | 74 |
public List<IssueDetail> issueDetails; |
90 | 75 |
|
... | ... | @@ -94,12 +79,11 @@ |
94 | 79 |
@OneToMany(mappedBy = "issue", cascade = CascadeType.ALL) |
95 | 80 |
public List<IssueComment> comments; |
96 | 81 |
|
82 |
+ @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL) |
|
83 |
+ public Set<IssueLabel> labels; |
|
84 |
+ |
|
97 | 85 |
public Issue(String title) { |
98 | 86 |
this.date = JodaDateUtil.now(); |
99 |
- } |
|
100 |
- |
|
101 |
- public String issueTypeLabel() { |
|
102 |
- return issueTypes().get(issueType); |
|
103 | 87 |
} |
104 | 88 |
|
105 | 89 |
public Duration ago() { |
... | ... | @@ -117,116 +101,6 @@ |
117 | 101 |
public String assigneeName() { |
118 | 102 |
|
119 | 103 |
return (this.assigneeId != null ? User.findNameById(this.assigneeId) : "issue.noAssignee"); |
120 |
- } |
|
121 |
- |
|
122 |
- /** |
|
123 |
- * 이슈의 오픈 상태를 확인한다. |
|
124 |
- * |
|
125 |
- * @return boolean |
|
126 |
- */ |
|
127 |
- public boolean isOpen() { |
|
128 |
- return StateType.OPEN.equals(this.stateType); |
|
129 |
- } |
|
130 |
- |
|
131 |
- /** |
|
132 |
- * View에서 스트링값으로 변환하도록 한다. |
|
133 |
- * |
|
134 |
- * @return |
|
135 |
- */ |
|
136 |
- |
|
137 |
- /** |
|
138 |
- * 해당 이슈의 상태(state) 따라서 탭 기능에서 구분 짖는(stateType) 것이 해결인지 미해결인지 값을 결정해준다. |
|
139 |
- * diagnosisResult가 2인 경우는 현 진단결과 선택 란에서 "수정완료"를 의미하므로, 이슈 정상 해결을 의미한다. |
|
140 |
- * |
|
141 |
- * @param state |
|
142 |
- */ |
|
143 |
- public void updateStateType(Issue issue) { |
|
144 |
- |
|
145 |
- if (this.state == null || this.state.equals(IssueState.ASSIGNED) |
|
146 |
- || this.state.equals(IssueState.ENROLLED)) { |
|
147 |
- this.stateType = StateType.OPEN; |
|
148 |
- } else { |
|
149 |
- this.stateType = StateType.CLOSED; |
|
150 |
- } |
|
151 |
- } |
|
152 |
- |
|
153 |
- /** |
|
154 |
- * 이슈의 담당자(assignee) 배정과 이슈의 진단결과(diagnosisResult)에 따라 이슈의 상태를 정해진 로직에 따라 |
|
155 |
- * 변경한다. |
|
156 |
- * |
|
157 |
- * @param issue |
|
158 |
- */ |
|
159 |
- public void updateState(Issue issue) { |
|
160 |
- |
|
161 |
- if (isEnrolled(issue)) { |
|
162 |
- this.state = IssueState.ENROLLED; |
|
163 |
- } else if (isFinished(issue)) { |
|
164 |
- this.state = IssueState.FINISHED; |
|
165 |
- } else if (isAssigned(issue)) { |
|
166 |
- this.state = IssueState.ASSIGNED; |
|
167 |
- } else if (isSolved(issue)) { |
|
168 |
- this.state = IssueState.SOLVED; |
|
169 |
- } else |
|
170 |
- this.state = IssueState.ENROLLED; |
|
171 |
- |
|
172 |
- updateStateType(issue); |
|
173 |
- } |
|
174 |
- |
|
175 |
- /** |
|
176 |
- * 이슈가 담당자와 진단결과가 없는 경우에는 "등록" 상태임을 확인해준다. |
|
177 |
- * |
|
178 |
- * @param issue |
|
179 |
- * @return boolean |
|
180 |
- */ |
|
181 |
- public boolean isEnrolled(Issue issue) { |
|
182 |
- |
|
183 |
- if (issue.assigneeId == null && issue.diagnosisResult.equals("")) { |
|
184 |
- return true; |
|
185 |
- } else |
|
186 |
- return false; |
|
187 |
- } |
|
188 |
- |
|
189 |
- /** |
|
190 |
- * 이슈가 담당자는 배정받고, 진단결과가 없는 경우에는 "진행중" 상태임을 확인해준다. |
|
191 |
- * |
|
192 |
- * @param issue |
|
193 |
- * @return |
|
194 |
- */ |
|
195 |
- public boolean isAssigned(Issue issue) { |
|
196 |
- |
|
197 |
- if (issue.assigneeId != null && issue.diagnosisResult.equals("")) { |
|
198 |
- return true; |
|
199 |
- } else |
|
200 |
- return false; |
|
201 |
- } |
|
202 |
- |
|
203 |
- /** |
|
204 |
- * 이슈가 담당자를 배정받고 진단결과가 2번째 수정완료(추후 변경 가능) 인 경우에는 "해결" 상태임을 확인해준다. |
|
205 |
- * |
|
206 |
- * @param issue |
|
207 |
- * @return |
|
208 |
- */ |
|
209 |
- public boolean isSolved(Issue issue) { |
|
210 |
- |
|
211 |
- if (issue.assigneeId != null && issue.diagnosisResult.equals("2")) { |
|
212 |
- return true; |
|
213 |
- } else |
|
214 |
- return false; |
|
215 |
- } |
|
216 |
- |
|
217 |
- /** |
|
218 |
- * 이슈가 담당자의 유무와 상관 없이, 진단결과가 2번째 수정완료가 아닌 다른 사유가 존재한 경우에는 "닫힘" 상태임을 확인해준다. |
|
219 |
- * |
|
220 |
- * @param issue |
|
221 |
- * @return |
|
222 |
- */ |
|
223 |
- |
|
224 |
- public boolean isFinished(Issue issue) { |
|
225 |
- |
|
226 |
- if (!issue.diagnosisResult.equals("2") && !issue.diagnosisResult.equals("")) { |
|
227 |
- return true; |
|
228 |
- } else |
|
229 |
- return false; |
|
230 | 104 |
} |
231 | 105 |
|
232 | 106 |
/** |
... | ... | @@ -329,7 +203,6 @@ |
329 | 203 |
Milestone milestone = Milestone.findById(issue.milestoneId); |
330 | 204 |
milestone.add(issue); |
331 | 205 |
} |
332 |
- issue.updateStateType(issue); |
|
333 | 206 |
return issue.id; |
334 | 207 |
} |
335 | 208 |
|
... | ... | @@ -353,28 +226,35 @@ |
353 | 226 |
* @param issue |
354 | 227 |
*/ |
355 | 228 |
public static void edit(Issue issue) { |
356 |
- issue.updateStateType(issue); |
|
357 | 229 |
issue.update(); |
230 |
+ } |
|
231 |
+ |
|
232 |
+ public static int countIssues(Long projectId, State state) { |
|
233 |
+ if (state == State.ALL) { |
|
234 |
+ return finder.where().eq("project.id", projectId).findRowCount(); |
|
235 |
+ } else { |
|
236 |
+ return finder.where().eq("project.id", projectId).eq("state", state).findRowCount(); |
|
237 |
+ } |
|
358 | 238 |
} |
359 | 239 |
|
360 | 240 |
/** |
361 | 241 |
* 미해결 탭을 눌렀을 때, open 상태의 이슈들을 찾아준다.. |
362 | 242 |
* |
363 |
- * @param projectName |
|
243 |
+ * @param projectId |
|
364 | 244 |
* @return |
365 | 245 |
*/ |
366 |
- public static Page<Issue> findOpenIssues(String projectName) { |
|
367 |
- return Issue.findIssues(projectName, StateType.OPEN); |
|
246 |
+ public static Page<Issue> findOpenIssues(Long projectId) { |
|
247 |
+ return Issue.findIssues(projectId, State.OPEN); |
|
368 | 248 |
} |
369 | 249 |
|
370 | 250 |
/** |
371 | 251 |
* 해결 탭을 눌렀을 때, closed 상태의 이슈들을 찾아준다. |
372 | 252 |
* |
373 |
- * @param projectName |
|
253 |
+ * @param projectId |
|
374 | 254 |
* @return |
375 | 255 |
*/ |
376 |
- public static Page<Issue> findClosedIssues(String projectName) { |
|
377 |
- return Issue.findIssues(projectName, StateType.CLOSED); |
|
256 |
+ public static Page<Issue> findClosedIssues(Long projectId) { |
|
257 |
+ return Issue.findIssues(projectId, State.CLOSED); |
|
378 | 258 |
} |
379 | 259 |
|
380 | 260 |
/** |
... | ... | @@ -384,54 +264,54 @@ |
384 | 264 |
* @param state |
385 | 265 |
* @return |
386 | 266 |
*/ |
387 |
- public static Page<Issue> findIssues(String projectName, StateType state) { |
|
388 |
- return find(projectName, FIRST_PAGE_NUMBER, state, DEFAULT_SORTER, Direction.DESC, |
|
267 |
+ public static Page<Issue> findIssues(Long projectId, State state) { |
|
268 |
+ return find(projectId, FIRST_PAGE_NUMBER, state, DEFAULT_SORTER, Direction.DESC, |
|
389 | 269 |
"", null, false); |
390 | 270 |
} |
391 | 271 |
|
392 | 272 |
/** |
393 | 273 |
* 검색창에서 제공된 query(filter)와 댓글과 파일첨부된 이슈만 찾아주는 체크박스의 값에 따라 필터링된 이슈들을 찾아준다. |
394 | 274 |
* |
395 |
- * @param projectName |
|
275 |
+ * @param projectId |
|
396 | 276 |
* @param filter |
397 | 277 |
* @param state |
398 | 278 |
* @param commentedCheck |
399 | 279 |
* @return |
400 | 280 |
*/ |
401 |
- public static Page<Issue> findFilteredIssues(String projectName, String filter, |
|
402 |
- StateType state, boolean commentedCheck) { |
|
403 |
- return find(projectName, FIRST_PAGE_NUMBER, state, DEFAULT_SORTER, Direction.DESC, |
|
281 |
+ public static Page<Issue> findFilteredIssues(Long projectId, String filter, |
|
282 |
+ State state, boolean commentedCheck) { |
|
283 |
+ return find(projectId, FIRST_PAGE_NUMBER, state, DEFAULT_SORTER, Direction.DESC, |
|
404 | 284 |
filter, null, commentedCheck); |
405 | 285 |
} |
406 | 286 |
|
407 | 287 |
/** |
408 | 288 |
* 댓글이 달린 이슈들만 찾아준다. |
409 | 289 |
* |
410 |
- * @param projectName |
|
290 |
+ * @param projectId |
|
411 | 291 |
* @param filter |
412 | 292 |
* @return |
413 | 293 |
*/ |
414 |
- public static Page<Issue> findCommentedIssues(String projectName, String filter) { |
|
415 |
- return find(projectName, FIRST_PAGE_NUMBER, StateType.ALL, DEFAULT_SORTER, |
|
294 |
+ public static Page<Issue> findCommentedIssues(Long projectId, String filter) { |
|
295 |
+ return find(projectId, FIRST_PAGE_NUMBER, State.ALL, DEFAULT_SORTER, |
|
416 | 296 |
Direction.DESC, filter, null, true); |
417 | 297 |
} |
418 | 298 |
|
419 | 299 |
/** |
420 | 300 |
* 마일스톤 Id에 의거해서 해당 마일스톤에 속한 이슈들을 찾아준다. |
421 | 301 |
* |
422 |
- * @param projectName |
|
302 |
+ * @param projectId |
|
423 | 303 |
* @param milestoneId |
424 | 304 |
* @return |
425 | 305 |
*/ |
426 |
- public static Page<Issue> findIssuesByMilestoneId(String projectName, Long milestoneId) { |
|
427 |
- return find(projectName, FIRST_PAGE_NUMBER, StateType.ALL, DEFAULT_SORTER, |
|
306 |
+ public static Page<Issue> findIssuesByMilestoneId(Long projectId, Long milestoneId) { |
|
307 |
+ return find(projectId, FIRST_PAGE_NUMBER, State.ALL, DEFAULT_SORTER, |
|
428 | 308 |
Direction.DESC, "", milestoneId, false); |
429 | 309 |
} |
430 | 310 |
|
431 | 311 |
/** |
432 | 312 |
* 이슈들을 아래의 parameter들의 조건에 의거하여 Page형태로 반환한다. |
433 | 313 |
* |
434 |
- * @param projectName project ID to finder issues |
|
314 |
+ * @param projectId project ID to finder issues |
|
435 | 315 |
* @param pageNumber Page to display |
436 | 316 |
* @param state state type of issue(OPEN or CLOSED |
437 | 317 |
* @param sortBy Issue property used for sorting, but, it might be fixed to |
... | ... | @@ -441,11 +321,11 @@ |
441 | 321 |
* @param commentedCheck filter applied on the commetedCheck column, 댓글이 존재하는 이슈만 필터링 |
442 | 322 |
* @return 위의 조건에 따라 필터링된 이슈들을 Page로 반환. |
443 | 323 |
*/ |
444 |
- public static Page<Issue> find(String projectName, int pageNumber, StateType state, |
|
324 |
+ public static Page<Issue> find(Long projectId, int pageNumber, State state, |
|
445 | 325 |
String sortBy, Direction order, String filter, Long milestoneId, |
446 | 326 |
boolean commentedCheck) { |
447 | 327 |
OrderParams orderParams = new OrderParams().add(sortBy, order); |
448 |
- SearchParams searchParams = new SearchParams().add("project.name", projectName, |
|
328 |
+ SearchParams searchParams = new SearchParams().add("project.id", projectId, |
|
449 | 329 |
Matching.EQUALS); |
450 | 330 |
|
451 | 331 |
if (filter != null && !filter.isEmpty()) { |
... | ... | @@ -459,14 +339,14 @@ |
459 | 339 |
} |
460 | 340 |
|
461 | 341 |
if (state == null) { |
462 |
- state = StateType.ALL; |
|
342 |
+ state = State.ALL; |
|
463 | 343 |
} |
464 | 344 |
switch (state) { |
465 | 345 |
case OPEN: |
466 |
- searchParams.add("stateType", StateType.OPEN, Matching.EQUALS); |
|
346 |
+ searchParams.add("state", State.OPEN, Matching.EQUALS); |
|
467 | 347 |
break; |
468 | 348 |
case CLOSED: |
469 |
- searchParams.add("stateType", StateType.CLOSED, Matching.EQUALS); |
|
349 |
+ searchParams.add("state", State.CLOSED, Matching.EQUALS); |
|
470 | 350 |
break; |
471 | 351 |
default: |
472 | 352 |
} |
... | ... | @@ -488,7 +368,7 @@ |
488 | 368 |
.findPagingList(condition.pageSize).getPage(condition.page - 1); |
489 | 369 |
} |
490 | 370 |
|
491 |
- public static Long findAssigneeIdByIssueId(String projectName, Long issueId) { |
|
371 |
+ public static Long findAssigneeIdByIssueId(Long issueId) { |
|
492 | 372 |
return finder.byId(issueId).assigneeId; |
493 | 373 |
} |
494 | 374 |
|
... | ... | @@ -598,4 +478,12 @@ |
598 | 478 |
} |
599 | 479 |
this.issueDetails.add(issueDetail); |
600 | 480 |
} |
481 |
+ |
|
482 |
+ public boolean isOpen() { |
|
483 |
+ return this.state == State.OPEN; |
|
484 |
+ } |
|
485 |
+ |
|
486 |
+ public boolean isClosed() { |
|
487 |
+ return this.state == State.CLOSED; |
|
488 |
+ } |
|
601 | 489 |
} |
+++ app/models/IssueLabel.java
... | ... | @@ -0,0 +1,55 @@ |
1 | +package models; | |
2 | + | |
3 | +import java.util.List; | |
4 | + | |
5 | +import javax.persistence.Id; | |
6 | +import javax.persistence.ManyToMany; | |
7 | +import javax.persistence.ManyToOne; | |
8 | + | |
9 | +import play.data.validation.Constraints.Required; | |
10 | +import play.data.validation.*; | |
11 | +import play.db.ebean.Model; | |
12 | + | |
13 | +import play.data.validation.*; | |
14 | +import play.db.ebean.*; | |
15 | + | |
16 | +import javax.persistence.*; | |
17 | +import java.util.*; | |
18 | + | |
19 | +@Entity | |
20 | +public class IssueLabel extends Model { | |
21 | + | |
22 | + /** | |
23 | + * | |
24 | + */ | |
25 | + private static final long serialVersionUID = -35487506476718498L; | |
26 | + private static Finder<Long, IssueLabel> finder = new Finder<Long, IssueLabel>(Long.class, IssueLabel.class); | |
27 | + | |
28 | + @Id | |
29 | + public Long id; | |
30 | + | |
31 | + @Required | |
32 | + public String category; | |
33 | + | |
34 | + @Required | |
35 | + public String color; | |
36 | + | |
37 | + @Required | |
38 | + public String name; | |
39 | + | |
40 | + @ManyToOne | |
41 | + public Project project; | |
42 | + | |
43 | + public static List<IssueLabel> findByProjectId(Long projectId) { | |
44 | + return finder.where().eq("project.id", projectId).findList(); | |
45 | + } | |
46 | + | |
47 | + public static IssueLabel findById(Long id) { | |
48 | + return finder.byId(id); | |
49 | + } | |
50 | + | |
51 | + public boolean exists() { | |
52 | + return finder.where().eq("project.id", project.id) | |
53 | + .eq("name", name).eq("color", color).findRowCount() > 0; | |
54 | + } | |
55 | +} |
--- app/models/Milestone.java
+++ app/models/Milestone.java
... | ... | @@ -78,7 +78,7 @@ |
78 | 78 |
* @return |
79 | 79 |
*/ |
80 | 80 |
public static List<Milestone> findByProjectId(Long projectId) { |
81 |
- return Milestone.findMilestones(projectId, StateType.ALL); |
|
81 |
+ return Milestone.findMilestones(projectId, State.ALL); |
|
82 | 82 |
} |
83 | 83 |
|
84 | 84 |
/** |
... | ... | @@ -88,7 +88,7 @@ |
88 | 88 |
* @return |
89 | 89 |
*/ |
90 | 90 |
public static List<Milestone> findClosedMilestones(Long projectId) { |
91 |
- return Milestone.findMilestones(projectId, StateType.CLOSED); |
|
91 |
+ return Milestone.findMilestones(projectId, State.CLOSED); |
|
92 | 92 |
} |
93 | 93 |
|
94 | 94 |
/** |
... | ... | @@ -98,7 +98,7 @@ |
98 | 98 |
* @return |
99 | 99 |
*/ |
100 | 100 |
public static List<Milestone> findOpenMilestones(Long projectId) { |
101 |
- return Milestone.findMilestones(projectId, StateType.OPEN); |
|
101 |
+ return Milestone.findMilestones(projectId, State.OPEN); |
|
102 | 102 |
} |
103 | 103 |
|
104 | 104 |
/** |
... | ... | @@ -119,7 +119,7 @@ |
119 | 119 |
* @return |
120 | 120 |
*/ |
121 | 121 |
public static List<Milestone> findMilestones(Long projectId, |
122 |
- StateType state) { |
|
122 |
+ State state) { |
|
123 | 123 |
return findMilestones(projectId, state, DEFAULT_SORTER, Direction.ASC); |
124 | 124 |
} |
125 | 125 |
|
... | ... | @@ -133,11 +133,11 @@ |
133 | 133 |
* @return |
134 | 134 |
*/ |
135 | 135 |
public static List<Milestone> findMilestones(Long projectId, |
136 |
- StateType state, String sort, Direction direction) { |
|
136 |
+ State state, String sort, Direction direction) { |
|
137 | 137 |
OrderParams orderParams = new OrderParams().add(sort, direction); |
138 | 138 |
SearchParams searchParams = new SearchParams().add("project.id", projectId, Matching.EQUALS); |
139 | 139 |
if (state == null) { |
140 |
- state = StateType.ALL; |
|
140 |
+ state = State.ALL; |
|
141 | 141 |
} |
142 | 142 |
switch (state) { |
143 | 143 |
case OPEN: |
... | ... | @@ -163,7 +163,7 @@ |
163 | 163 |
*/ |
164 | 164 |
public static Map<String, String> options(Long projectId) { |
165 | 165 |
LinkedHashMap<String, String> options = new LinkedHashMap<String, String>(); |
166 |
- for (Milestone milestone : findMilestones(projectId, StateType.ALL, "title", Direction.ASC)) { |
|
166 |
+ for (Milestone milestone : findMilestones(projectId, State.ALL, "title", Direction.ASC)) { |
|
167 | 167 |
options.put(milestone.id.toString(), milestone.title); |
168 | 168 |
} |
169 | 169 |
return options; |
--- app/models/enumeration/IssueState.java
... | ... | @@ -1,24 +0,0 @@ |
1 | -package models.enumeration; | |
2 | - | |
3 | -public enum IssueState { | |
4 | - ENROLLED("issue.state.enrolled"), ASSIGNED("issue.state.assigned"), SOLVED("issue.state.solved"), FINISHED("issue.state.finished"); | |
5 | - private String state; | |
6 | - | |
7 | - IssueState(String state) { | |
8 | - this.state = state; | |
9 | - } | |
10 | - | |
11 | - public String state() { | |
12 | - return this.state; | |
13 | - } | |
14 | - | |
15 | - public static IssueState getValue(String value) { | |
16 | - for (IssueState issueState : IssueState.values()) { | |
17 | - if (issueState.state().equals(value)) { | |
18 | - return issueState; | |
19 | - } | |
20 | - } | |
21 | - return IssueState.ENROLLED; | |
22 | - } | |
23 | - | |
24 | -} |
--- app/models/enumeration/Resource.java
+++ app/models/enumeration/Resource.java
... | ... | @@ -3,15 +3,12 @@ |
3 | 3 |
public enum Resource { |
4 | 4 |
ISSUE_POST("issue_post"), |
5 | 5 |
ISSUE_COMMENT("issue_comment"), |
6 |
- ISSUE_ENVIRONMENT("issue_environment"), |
|
7 | 6 |
ISSUE_ASSIGNEE("issue_assignee"), |
8 | 7 |
ISSUE_STATE("issue_state"), |
9 |
- ISSUE_IMPORTANCE("issue_importance"), |
|
10 | 8 |
ISSUE_CATEGORY("issue_category"), |
11 | 9 |
ISSUE_MILESTONE("issue_milestone"), |
12 |
- ISSUE_COMPONENT("issue_component"), |
|
13 |
- ISSUE_DIAGNOSISRESULT("issue_diagnosisResult"), |
|
14 | 10 |
ISSUE_NOTICE("issue_notice"), |
11 |
+ ISSUE_LABEL("issue_label"), |
|
15 | 12 |
BOARD_POST("board_post"), |
16 | 13 |
BOARD_COMMENT("board_comment"), |
17 | 14 |
BOARD_CATEGORY("board_category"), |
... | ... | @@ -24,15 +21,15 @@ |
24 | 21 |
USER("user"); |
25 | 22 |
|
26 | 23 |
private String resource; |
27 |
- |
|
24 |
+ |
|
28 | 25 |
Resource(String resource) { |
29 | 26 |
this.resource = resource; |
30 | 27 |
} |
31 |
- |
|
28 |
+ |
|
32 | 29 |
public String resource() { |
33 | 30 |
return this.resource; |
34 | 31 |
} |
35 |
- |
|
32 |
+ |
|
36 | 33 |
public static Resource getValue(String value) { |
37 | 34 |
for (Resource resource : Resource.values()) { |
38 | 35 |
if (resource.resource().equals(value)) { |
+++ app/models/enumeration/State.java
... | ... | @@ -0,0 +1,25 @@ |
1 | +package models.enumeration; | |
2 | + | |
3 | +public enum State { | |
4 | + ALL("all"), OPEN("open"), CLOSED("closed"); | |
5 | + private String state; | |
6 | + | |
7 | + State(String state) { | |
8 | + this.state = state; | |
9 | + } | |
10 | + | |
11 | + public String state() { | |
12 | + return this.state; | |
13 | + } | |
14 | + | |
15 | + public static State getValue(String value) { | |
16 | + for (State issueState : State.values()) { | |
17 | + if (issueState.state().equals(value)) { | |
18 | + return issueState; | |
19 | + } | |
20 | + } | |
21 | + return State.OPEN; | |
22 | + } | |
23 | +} | |
24 | + | |
25 | + |
--- app/models/enumeration/StateType.java
... | ... | @@ -1,25 +0,0 @@ |
1 | -package models.enumeration; | |
2 | - | |
3 | -public enum StateType { | |
4 | - ALL("all"), OPEN("open"), CLOSED("closed"); | |
5 | - private String stateType; | |
6 | - | |
7 | - StateType(String stateType) { | |
8 | - this.stateType = stateType; | |
9 | - } | |
10 | - | |
11 | - public String stateType() { | |
12 | - return this.stateType; | |
13 | - } | |
14 | - | |
15 | - public static StateType getValue(String value) { | |
16 | - for (StateType issueStateType : StateType.values()) { | |
17 | - if (issueStateType.stateType().equals(value)) { | |
18 | - return issueStateType; | |
19 | - } | |
20 | - } | |
21 | - return StateType.OPEN; | |
22 | - } | |
23 | -} | |
24 | - | |
25 | - |
--- app/models/support/SearchCondition.java
+++ app/models/support/SearchCondition.java
... | ... | @@ -2,7 +2,7 @@ |
2 | 2 |
|
3 | 3 |
import models.enumeration.*; |
4 | 4 |
/** |
5 |
- * |
|
5 |
+ * |
|
6 | 6 |
* @author Taehyun Park |
7 | 7 |
* |
8 | 8 |
*/ |
... | ... | @@ -12,8 +12,8 @@ |
12 | 12 |
public String sortBy; |
13 | 13 |
public String orderBy; |
14 | 14 |
public int pageNum; |
15 |
- |
|
16 |
- public String stateType; |
|
15 |
+ |
|
16 |
+ public String state; |
|
17 | 17 |
public Boolean commentedCheck; |
18 | 18 |
public Boolean fileAttachedCheck; |
19 | 19 |
public Long milestone; |
... | ... | @@ -24,9 +24,9 @@ |
24 | 24 |
orderBy = Direction.DESC.direction(); |
25 | 25 |
pageNum = 0; |
26 | 26 |
milestone = null; |
27 |
- stateType = StateType.OPEN.name(); |
|
27 |
+ state = State.OPEN.name(); |
|
28 | 28 |
commentedCheck = false; |
29 | 29 |
fileAttachedCheck = false; |
30 |
- |
|
30 |
+ |
|
31 | 31 |
} |
32 | 32 |
} |
--- app/utils/AccessControl.java
+++ app/utils/AccessControl.java
... | ... | @@ -73,6 +73,8 @@ |
73 | 73 |
break; |
74 | 74 |
case USER: |
75 | 75 |
return userId == resourceId; |
76 |
+ case ISSUE_LABEL: |
|
77 |
+ return ProjectUser.isMember(userId, projectId); |
|
76 | 78 |
default: |
77 | 79 |
isAuthorEditible = false; |
78 | 80 |
break; |
--- app/views/issue/editIssue.scala.html
+++ app/views/issue/editIssue.scala.html
... | ... | @@ -88,16 +88,6 @@ |
88 | 88 |
'_default -> Messages("issue.new.selectDefault.assignee"), |
89 | 89 |
'_showConstraints -> false) |
90 | 90 |
} |
91 |
- <!-- FIXME 컴포넌트 관련 스펙이 정해지면 수정할것 --> |
|
92 |
- @isVisible(models.enumeration.Resource.ISSUE_COMPONENT) { |
|
93 |
- @select( |
|
94 |
- issueForm("componentName"), |
|
95 |
- options = options( |
|
96 |
- "component_1"->"Component id 1"), |
|
97 |
- '_label-> Messages("issue.new.detailInfo.component"), |
|
98 |
- '_default -> Messages("issue.new.selectDefault.component"), |
|
99 |
- '_showConstraints -> false) |
|
100 |
- } |
|
101 | 91 |
@isVisible(models.enumeration.Resource.ISSUE_MILESTONE) { |
102 | 92 |
@select( |
103 | 93 |
issueForm("milestoneId"), |
... | ... | @@ -106,29 +96,6 @@ |
106 | 96 |
'_default -> Messages("issue.new.selectDefault.milestone"), |
107 | 97 |
'_showConstraints -> false) |
108 | 98 |
} |
109 |
- </div> |
|
110 |
- </div> |
|
111 |
- </fieldset> |
|
112 |
- <fieldset> |
|
113 |
- <div class="well"> |
|
114 |
- <legend><b>@Messages("issue.new.result")</b></legend> |
|
115 |
- <div class="well form-inline"> |
|
116 |
- @isVisible(models.enumeration.Resource.ISSUE_IMPORTANCE) { |
|
117 |
- @selectEx( |
|
118 |
- issueForm("importance"), |
|
119 |
- options(Issue.importances), |
|
120 |
- '_label-> Messages("issue.new.result.importance"), |
|
121 |
- '_default -> Messages("issue.new.selectDefault.importance"), |
|
122 |
- '_showConstraints -> false) |
|
123 |
- } |
|
124 |
- @isVisible(models.enumeration.Resource.ISSUE_DIAGNOSISRESULT) { |
|
125 |
- @selectEx( |
|
126 |
- issueForm("diagnosisResult"), |
|
127 |
- options(Issue.diagnosisResults), |
|
128 |
- '_label-> Messages("issue.new.result.diagnosisResult"), |
|
129 |
- '_default -> Messages("issue.new.selectDefault.diagnosisResult"), |
|
130 |
- '_showConstraints -> false) |
|
131 |
- } |
|
132 | 99 |
</div> |
133 | 100 |
</div> |
134 | 101 |
</fieldset> |
--- app/views/issue/issue.scala.html
+++ app/views/issue/issue.scala.html
... | ... | @@ -11,6 +11,9 @@ |
11 | 11 |
} |
12 | 12 |
} |
13 | 13 |
@main(Messages(title),project) { |
14 |
+<style> |
|
15 |
+@@IMPORT url("/assets/stylesheets/issue.css"); |
|
16 |
+</style> |
|
14 | 17 |
|
15 | 18 |
<div class="row"> |
16 | 19 |
<div class="span2 offset10"> |
... | ... | @@ -33,6 +36,7 @@ |
33 | 36 |
|
34 | 37 |
<div class="span11"> |
35 | 38 |
<h1>@issue.title</h1> |
39 |
+ <p>@for(label <- issue.labels) { <button class="issue-label" labelId="@label.id">@label.name</button> } </p> |
|
36 | 40 |
<div> |
37 | 41 |
<span>@issue.reporterName이 @agoString(issue.ago())에 작성함</span> | |
38 | 42 |
<span>@issue.date</span> |
... | ... | @@ -51,39 +55,26 @@ |
51 | 55 |
<a class="btn pull-right" href=""><i class="icon-ok"></i>@Messages("button.autoNotification")</a> |
52 | 56 |
</div> |
53 | 57 |
</br></br> |
58 |
+ |
|
54 | 59 |
@if(session.get("userId") != null && ProjectUser.isMember(session.get("userId").toLong, project.id)){ |
55 | 60 |
|
56 | 61 |
@form(action = routes.IssueApp.updateIssue(project.owner, project.name, issue.id), |
57 | 62 |
'enctype -> "multipart/form-data", |
58 | 63 |
'class -> "form-horizontal") { |
64 |
+ |
|
65 |
+ <!-- issue.label module make this as label selector --> |
|
66 |
+ <fieldset class="labels"></fieldset> |
|
67 |
+ |
|
59 | 68 |
<fieldset> |
60 | 69 |
<div class="well"> |
61 | 70 |
<legend><b>@Messages("issue.new.detailInfo")</b></legend> |
62 | 71 |
<div class="well form-inline"> |
63 |
- @isVisible(models.enumeration.Resource.ISSUE_CATEGORY) { |
|
64 |
- @selectEx( |
|
65 |
- issueForm("issueType"), |
|
66 |
- options(Issue.issueTypes), |
|
67 |
- '_label-> Messages("issue.new.detailInfo.issueType"), |
|
68 |
- '_default -> Messages("issue.new.selectDefault.issueType"), |
|
69 |
- '_showConstraints -> false) |
|
70 |
- } |
|
71 | 72 |
@isVisible(models.enumeration.Resource.ISSUE_ASSIGNEE) { |
72 | 73 |
@select( |
73 | 74 |
issueForm("assigneeId"), |
74 | 75 |
options(ProjectUser.options(project.id)), |
75 | 76 |
'_label-> Messages("issue.new.detailInfo.assignee"), |
76 | 77 |
'_default -> Messages("issue.new.selectDefault.assignee"), |
77 |
- '_showConstraints -> false) |
|
78 |
- } |
|
79 |
- <!-- FIXME 컴포넌트 관련 스펙이 정해지면 수정할것 --> |
|
80 |
- @isVisible(models.enumeration.Resource.ISSUE_COMPONENT) { |
|
81 |
- @select( |
|
82 |
- issueForm("componentName"), |
|
83 |
- options = options( |
|
84 |
- "component_1"->"Component id 1"), |
|
85 |
- '_label-> Messages("issue.new.detailInfo.component"), |
|
86 |
- '_default -> Messages("issue.new.selectDefault.component"), |
|
87 | 78 |
'_showConstraints -> false) |
88 | 79 |
} |
89 | 80 |
@isVisible(models.enumeration.Resource.ISSUE_MILESTONE) { |
... | ... | @@ -97,30 +88,7 @@ |
97 | 88 |
</div> |
98 | 89 |
</div> |
99 | 90 |
</fieldset> |
100 |
- <fieldset> |
|
101 |
- <div class="well"> |
|
102 |
- <legend><b>@Messages("issue.new.result")</b></legend> |
|
103 |
- <div class="well form-inline"> |
|
104 |
- @isVisible(models.enumeration.Resource.ISSUE_IMPORTANCE) { |
|
105 |
- @selectEx( |
|
106 |
- issueForm("importance"), |
|
107 |
- options(Issue.importances), |
|
108 |
- '_label-> Messages("issue.new.result.importance"), |
|
109 |
- '_default -> Messages("issue.new.selectDefault.importance"), |
|
110 |
- '_showConstraints -> false) |
|
111 |
- } |
|
112 |
- @isVisible(models.enumeration.Resource.ISSUE_DIAGNOSISRESULT) { |
|
113 |
- @selectEx( |
|
114 |
- issueForm("diagnosisResult"), |
|
115 |
- options(Issue.diagnosisResults), |
|
116 |
- '_label-> Messages("issue.new.result.diagnosisResult"), |
|
117 |
- '_default -> Messages("issue.new.selectDefault.diagnosisResult"), |
|
118 |
- '_showConstraints -> false) |
|
119 |
- } |
|
120 |
- </div> |
|
121 |
- </div> |
|
122 |
- </fieldset> |
|
123 |
- <input type="hidden" value="@issue.title" name="title"/> |
|
91 |
+ <input type="hidden" value="@issue.title" name="title"/> |
|
124 | 92 |
<input type="hidden" value="@issue.body" name="body"/> |
125 | 93 |
<input type="hidden" value="@issue.authorId" name="authorId"/> |
126 | 94 |
<div class = "btn pull-right"> |
... | ... | @@ -205,5 +173,10 @@ |
205 | 173 |
@views.html.markdown() |
206 | 174 |
<script type="text/javascript"> |
207 | 175 |
nforge.require('shortcut.submit'); |
176 |
+ @for(label <- issue.labels) { |
|
177 |
+ $('button.issue-label[labelId="@label.id"]').css('background-color', '@label.color'); |
|
178 |
+ } |
|
179 |
+ nforge.require('nforge.issue.view'); |
|
180 |
+ nforge.require('issue.label', '@routes.IssueLabelApp.getAll(project.owner, project.name)', '@routes.IssueLabelApp.post(project.owner, project.name)', {editable: false}); |
|
208 | 181 |
</script> |
209 | 182 |
} |
--- app/views/issue/issueList.scala.html
+++ app/views/issue/issueList.scala.html
... | ... | @@ -5,7 +5,7 @@ |
5 | 5 |
|
6 | 6 |
@header(label:String, sortBy:String) = { |
7 | 7 |
<th> |
8 |
- <a class="th-sort" href="@routes.IssueApp.issues(project.owner, project.name, param.stateType)" data-sort-by="@sortBy">@label</a> |
|
8 |
+ <a class="th-sort" href="@routes.IssueApp.issues(project.owner, project.name, param.state)" data-sort-by="@sortBy">@label</a> |
|
9 | 9 |
@if(sortBy == param.sortBy){ |
10 | 10 |
@if(param.orderBy == "desc"){ |
11 | 11 |
<i class="icon-chevron-down"></i> |
... | ... | @@ -14,105 +14,90 @@ |
14 | 14 |
} |
15 | 15 |
} |
16 | 16 |
</th> |
17 |
+ |
|
17 | 18 |
} |
18 | 19 |
|
19 | 20 |
@main(Messages(title), project){ |
20 |
-<div id = "issue_page"> |
|
21 |
- <div class="row"> |
|
22 |
- <div span="span6"> |
|
23 |
- <div class="pull-right"> |
|
24 |
- <form id="searchForm" class="form-search form-inline" action="@routes.IssueApp.issues(project.owner, project.name, param.stateType)" method="GET" > |
|
25 |
- <select id="milestone" name="milestone"> |
|
26 |
- <option class="blank" value ="">@Messages("issue.menu.milestoneSelectDefault")</option>@Milestone.options(project.id).map { v => <option id="changeMilestone" name ="milestone" value="@v._1" >@v._2</option>} |
|
27 |
- </select> |
|
28 |
- <input type="hidden" name="sortBy" value="@param.sortBy" class="h-value sort"> |
|
29 |
- <input type="hidden" name="orderBy" value="@param.orderBy" class="h-value order"> |
|
30 |
- <input type="hidden" name="stateType" value="@param.stateType"> |
|
31 |
- <input type="text" name="filter" value="@param.filter" placeholder=@Messages("issue.menu.searchDefault")> |
|
32 |
- <button type="submit" id="searchSubmit" class="btn"> <i class="icon-search"> </i></button> |
|
33 |
- |
|
34 |
- <a class="btn btn-primary" href="@routes.IssueApp.newIssue(project.owner, project.name)" >@Messages("issue.menu.new")</a> |
|
35 |
- </form> |
|
36 |
- </div> |
|
37 |
- </div> |
|
38 |
- </div> |
|
39 |
- <div> |
|
40 |
- <ul class="nav nav-tabs" id="issue_list"> |
|
41 |
- <li class="@if(param.stateType.equals("all")){active}"> |
|
42 |
- <a href="@routes.IssueApp.issues(project.owner, project.name,"all")">@Messages("issue.stateType.all")</a></li> |
|
43 |
- <li class="@if(param.stateType.equals("open")){active}"> |
|
44 |
- <a href="@routes.IssueApp.issues(project.owner, project.name, "open")">@Messages("issue.stateType.open")</a></li> |
|
45 |
- <li class="@if(param.stateType.equals("closed")){active}"> |
|
46 |
- <a href="@routes.IssueApp.issues(project.owner, project.name, "closed")">@Messages("issue.stateType.closed")</a></li> |
|
47 |
- </ul> |
|
48 |
- |
|
49 |
- @if(currentPage.getTotalRowCount == 0){ |
|
50 |
- <div class="well span11"> |
|
51 |
- <em>@Messages("issue.is.empty")</em> |
|
52 |
- </div> |
|
53 |
- }else{ |
|
54 |
- <div class="pull-right"> |
|
55 |
- <form class="form-inline" id="checkboxForm" action="@routes.IssueApp.issues(project.owner, project.name, param.stateType)" method="GET"> |
|
56 |
- |
|
57 |
- <input type="hidden" name="filter" value="@param.filter"> |
|
58 |
- <input type="hidden" name="stateType" value="@param.stateType"> |
|
59 |
- <div class="control-group"> |
|
60 |
- <label class="control-label" for="inlineCheckboxes"></label> |
|
61 |
- <div class="controls inline"> |
|
62 |
- <fieldset> |
|
63 |
-<!--FIXME view 전문가님이 아래의 if-else문 안의 중복된 코드를 업그레이드 해주시길 바랍니다. --> |
|
64 |
- <input class="filters" type="checkbox" name="commentedCheck" id="commentCheckId" value="1" @if(param.commentedCheck==true){checked="checked"}> |
|
65 |
- <label class="checkbox inline">@Messages("checkbox.commented")</label> |
|
66 |
- <input class="filters" type="checkbox" name="fileAttachedCheck" id="fileAttachedCheckId" value="1" @if(param.fileAttachedCheck==true){checked="checked"}> |
|
67 |
- <label class="checkbox inline">@Messages("checkbox.fileAttached")</label> |
|
68 |
-<!--// --> |
|
69 |
- <a href="@routes.IssueApp.extractExcelFile(project.owner, project.name, param.stateType)"><i class ="icon-download-alt"></i>@Messages("button.excelDownload")</a> |
|
70 |
- </fieldset> |
|
71 |
- </div> |
|
72 |
- </div> |
|
73 |
- </form> |
|
74 |
- </div> |
|
75 |
- <table class="table"> |
|
76 |
- <thead> |
|
77 |
- <tr> |
|
78 |
- @header(Messages("label.id"),"id") |
|
79 |
- @header(Messages("label.state"), "state") |
|
80 |
- @header(Messages("label.title"), "title") |
|
81 |
- @header(Messages("label.assignee"), "assigneeId") |
|
82 |
- @header(Messages("label.date"), "date") |
|
83 |
- </tr> |
|
84 |
- </thead> |
|
85 |
- <tbody> |
|
86 |
- @for(issue <- currentPage.getList()){ |
|
87 |
- <tr> |
|
88 |
- <td>@issue.id</td> |
|
89 |
- <td>@Messages(issue.state.state)</td> |
|
90 |
- <td><a href="@routes.IssueApp.issue(project.owner, project.name, issue.id)">@issue.title</a></td> |
|
91 |
- <td><a href="@routes.IssueApp.issue(project.owner, project.name, issue.id)"></a></td> |
|
92 |
- <td> |
|
93 |
- @if(issue.assigneeId == null){ |
|
94 |
- <em>@Messages("issue.noAssignee")</em> |
|
95 |
- } else { |
|
96 |
- <em>@issue.assigneeName</em> |
|
97 |
- } |
|
98 |
- </td> |
|
99 |
- <td>@agoString(issue.ago())</td> |
|
21 |
+<style> |
|
22 |
+@@IMPORT url("/assets/stylesheets/issue.css"); |
|
23 |
+</style> |
|
24 |
+<div class="page-padding"> |
|
25 |
+ <ul class="breadcrumb page-navi pull-right"> |
|
26 |
+ <li><a href="#" class="icon-home"></a><span class="divider">></span></li> |
|
27 |
+ <li><a href="#">project home</a><span class="divider">></span></li> |
|
28 |
+ <li class="active">Issue</li> |
|
29 |
+ </ul> |
|
100 | 30 |
|
101 |
- </tr> |
|
102 |
- } |
|
103 |
- </tbody> |
|
104 |
- </table> |
|
31 |
+ <ul class="breadcrumb project-name"> |
|
32 |
+ <li><a href="#">@project.owner</a><span class="divider">/</span></li> |
|
33 |
+ <li class="active"><a href="#">@project.name</a></li> |
|
34 |
+ </ul> |
|
105 | 35 |
|
106 |
- <div class="row"> |
|
107 |
- <a class="btn pull-right" href="@routes.IssueApp.enrollAutoNotification(project.owner, project.name)"><i class="icon-ok"></i>@Messages("button.autoNotification")</a> |
|
108 |
- @pagination(currentPage, 5, "pagination") |
|
109 |
- </div> |
|
110 |
- |
|
36 |
+ <div class="dashboard"> |
|
37 |
+ <dl class="row-fluid"> |
|
38 |
+ <div class="span4"><dt><b>All</b> issues</dt><dd class="all inner-shadow pull-right" title="312,549">@Issue.countIssues(project.id, State.ALL)</dd></div> |
|
39 |
+ <div class="span4"><dt><b>Open</b> issues</dt><dd class="open inner-shadow pull-right" title="32,047">@Issue.countIssues(project.id, State.OPEN)</dd></div> |
|
40 |
+ <div class="span4"><dt><b>Closed</b> issues</dt><dd class="closed inner-shadow pull-right" title="4,086">@Issue.countIssues(project.id, State.CLOSED)</dd></div> |
|
41 |
+ </dl> |
|
111 | 42 |
|
112 |
- </div> |
|
43 |
+ <hr/> |
|
44 |
+ <form class="form-search" action="@routes.IssueApp.issues(project.owner, project.name, param.state)" method="GET"> |
|
45 |
+ <button type="button" id="advanced-search" class="btn btn-small btn-flat" data-toggle="button">Advanced Search</button> |
|
46 |
+ <a href="@routes.IssueApp.extractExcelFile(project.owner, project.name, param.state)" class="btn btn-small btn-flat">Download Excel file</a> |
|
47 |
+ <div class="input-append pull-right"> |
|
48 |
+ <input type="hidden" name="orderBy" value="@param.orderBy" class="h-value order"> |
|
49 |
+ <input type="hidden" name="state" value="@param.state"> |
|
50 |
+ <input name="filter" class="span2" id="appendedInputButton" size="16" type="text"><button class="btn" type="button" placeholder="현재 게시글에서 검색">SEARCH</button> |
|
51 |
+ </div> |
|
52 |
+ </form> |
|
53 |
+ |
|
54 |
+ <form class="form-horizontal" id="labels-form"> |
|
55 |
+ <!-- issue.label module make this as label filter and editor --> |
|
56 |
+ <fieldset class="labels"></fieldset> |
|
57 |
+ </form> |
|
58 |
+ |
|
59 |
+ </div> |
|
60 |
+ |
|
61 |
+ <p class="ordering"> |
|
62 |
+ <span>▲상태순</span> |
|
63 |
+ <span>▲날짜순</span> |
|
64 |
+ <span selected>▼댓글순</span> |
|
65 |
+ </p> |
|
66 |
+ |
|
67 |
+ <table> |
|
68 |
+ <tbody> |
|
69 |
+ |
|
70 |
+ @for(issue <- currentPage.getList.reverse){ |
|
71 |
+ <tr> |
|
72 |
+ <td class="no">@issue.id</td> |
|
73 |
+ <td class="attachmend attached">@if(Attachment.findByContainer(Resource.ISSUE_POST, issue.id).size > 0){<span class="icon-file"/>}</td> |
|
74 |
+ <td class="info"> |
|
75 |
+ <p><a href="@routes.IssueApp.issue(project.owner, project.name, issue.id)">@issue.title</a></p> |
|
76 |
+ <p class="author">by <a href="/users/@issue.authorId">@issue.authorName</a> |
|
77 |
+ </a> @agoString(issue.ago())</p> |
|
78 |
+ </td> |
|
79 |
+ <td class="state @issue.state.toString.toLowerCase">@Messages(issue.state.state)</td> |
|
80 |
+ <td class="comments">@issue.numOfComments</td> |
|
81 |
+ <td class="assignee"><a href="#"><img class="user-picture" src="/assets/images/default-avatar-34.png"></a> |
|
82 |
+ <span>@{ if (issue.assigneeId == null) Messages("issue.noAssignee") |
|
83 |
+ else issue.assigneeName }</span> |
|
84 |
+ </td> |
|
85 |
+ |
|
86 |
+ </tr> |
|
87 |
+ } |
|
88 |
+ |
|
89 |
+ <tbody> |
|
90 |
+ </table> |
|
91 |
+ <div class="pull-right"> |
|
92 |
+ <a class="btn btn-primary" href="@routes.IssueApp.newIssue(project.owner, project.name)" >@Messages("issue.menu.new")</a> |
|
93 |
+ </div> |
|
94 |
+ <div class="pagination pagination-centered"> |
|
95 |
+ @pagination(currentPage, 5, "pagination") |
|
96 |
+ </div> |
|
113 | 97 |
</div> |
114 |
-} |
|
98 |
+ |
|
115 | 99 |
<script type="text/javascript"> |
100 |
+ nforge.require('issue.label', '@routes.IssueLabelApp.getAll(project.owner, project.name)', '@routes.IssueLabelApp.post(project.owner, project.name)', {editable: true}); |
|
116 | 101 |
nforge.require('issue.list'); |
117 | 102 |
</script> |
118 | 103 |
} |
--- app/views/issue/newIssue.scala.html
+++ app/views/issue/newIssue.scala.html
... | ... | @@ -2,7 +2,7 @@ |
2 | 2 |
|
3 | 3 |
@import helper._ |
4 | 4 |
@import scala.collection.mutable.Map |
5 |
-@implicitFieldConstructor = @{ FieldConstructor(twitterBootstrapInput.render) } |
|
5 |
+@implicitFieldConstructor = @{ FieldConstructor(twitterBootstrapInput.render) } |
|
6 | 6 |
|
7 | 7 |
@isVisible(resource: models.enumeration.Resource)(content: => Html) = @{ |
8 | 8 |
roleCheck(session.get("userId"), project.id, resource, models.enumeration.Operation.EDIT){ |
... | ... | @@ -11,46 +11,57 @@ |
11 | 11 |
} |
12 | 12 |
|
13 | 13 |
@main(Messages(title), project) { |
14 |
- <div class="page-header"> |
|
15 |
- <h1>@Messages(title)</h1> |
|
16 |
- </div> |
|
17 |
- @form(action = routes.IssueApp.saveIssue(project.owner, project.name), |
|
18 |
- 'enctype -> "multipart/form-data", |
|
19 |
- 'class -> "form-horizontal") { |
|
20 |
- <fieldset> |
|
21 |
- @inputText( |
|
22 |
- issueForm("title"), |
|
23 |
- '_showConstraints -> false, |
|
14 |
+ <style> |
|
15 |
+ @@IMPORT url("/assets/stylesheets/issue.css"); |
|
16 |
+ </style> |
|
17 |
+ |
|
18 |
+ <div class="page-header"> |
|
19 |
+ <h1>@Messages(title)</h1> |
|
20 |
+ </div> |
|
21 |
+ |
|
22 |
+ @form(action = routes.IssueApp.saveIssue(project.owner, project.name), |
|
23 |
+ 'id -> "issue-form", |
|
24 |
+ 'enctype -> "multipart/form-data", |
|
25 |
+ 'class -> "form-horizontal", |
|
26 |
+ 'onkeypress -> "return event.keyCode !== 13" |
|
27 |
+ ) { |
|
28 |
+ <fieldset> |
|
29 |
+ @inputText( |
|
30 |
+ issueForm("title"), |
|
31 |
+ '_showConstraints -> false, |
|
24 | 32 |
'_label-> Messages("post.new.title"), |
25 | 33 |
'class -> "input-xxlarge") |
26 |
- @textarea( |
|
27 |
- issueForm("body"), |
|
28 |
- '_showConstraints -> false, |
|
34 |
+ @textarea( |
|
35 |
+ issueForm("body"), |
|
36 |
+ '_showConstraints -> false, |
|
29 | 37 |
'_label-> Messages("post.new.contents"), |
30 | 38 |
'rows -> 16, |
31 | 39 |
'class -> "input-xxlarge textbody", |
32 | 40 |
'markdown -> true) |
33 |
- </fieldset> |
|
41 |
+ </fieldset> |
|
34 | 42 |
|
35 |
- <fieldset> |
|
43 |
+ <!-- issue.label module make this as label selector --> |
|
44 |
+ <fieldset class="labels"></fieldset> |
|
45 |
+ |
|
46 |
+ <fieldset> |
|
36 | 47 |
<div class="well"> |
37 | 48 |
<legend><b>@Messages("issue.new.environment")</b></legend> |
38 | 49 |
<div class="well form-inline"> |
39 | 50 |
@selectEx( |
40 |
- issueForm("osType"), |
|
51 |
+ issueForm("osType"), |
|
41 | 52 |
options(Issue.osTypes), |
42 | 53 |
'_label-> Messages("issue.new.environment.osType"), |
43 | 54 |
'_default -> Messages("issue.new.selectDefault.osType"), |
44 | 55 |
'_showConstraints -> false) |
45 |
- |
|
56 |
+ |
|
46 | 57 |
@selectEx( |
47 |
- issueForm("browserType"), |
|
58 |
+ issueForm("browserType"), |
|
48 | 59 |
options(Issue.browserTypes), |
49 | 60 |
'_label-> Messages("issue.new.environment.browserType"), |
50 | 61 |
'_default -> Messages("issue.new.selectDefault.browserType"), |
51 | 62 |
'_showConstraints -> false) |
52 | 63 |
@selectEx( |
53 |
- issueForm("dbmsType"), |
|
64 |
+ issueForm("dbmsType"), |
|
54 | 65 |
options(Issue.dbmsTypes), |
55 | 66 |
'_label-> Messages("issue.new.environment.dbmsType"), |
56 | 67 |
'_default -> Messages("issue.new.selectDefault.dbmsType"), |
... | ... | @@ -59,81 +70,41 @@ |
59 | 70 |
</div> |
60 | 71 |
</fieldset> |
61 | 72 |
|
62 |
- <fieldset> |
|
63 |
- <div class="well"> |
|
64 |
- <legend><b>@Messages("issue.new.detailInfo")</b></legend> |
|
65 |
- <div class="well form-inline"> |
|
66 |
- @isVisible(models.enumeration.Resource.ISSUE_CATEGORY) { |
|
67 |
- @selectEx( |
|
68 |
- issueForm("issueType"), |
|
69 |
- options(Issue.issueTypes), |
|
70 |
- '_label-> Messages("issue.new.detailInfo.issueType"), |
|
71 |
- '_default -> Messages("issue.new.selectDefault.issueType"), |
|
72 |
- '_showConstraints -> false) |
|
73 |
- } |
|
74 |
- @isVisible(models.enumeration.Resource.ISSUE_ASSIGNEE) { |
|
75 |
- @select( |
|
76 |
- issueForm("assigneeId"), |
|
77 |
- options(ProjectUser.options(project.id)), |
|
78 |
- '_label-> Messages("issue.new.detailInfo.assignee"), |
|
79 |
- '_default -> Messages("issue.new.selectDefault.assignee"), |
|
80 |
- '_showConstraints -> false) |
|
73 |
+ <fieldset> |
|
74 |
+ <div class="well"> |
|
75 |
+ <legend><b>@Messages("issue.new.detailInfo")</b></legend> |
|
76 |
+ <div class="well form-inline"> |
|
77 |
+ @isVisible(models.enumeration.Resource.ISSUE_ASSIGNEE) { |
|
78 |
+ @select( |
|
79 |
+ issueForm("assigneeId"), |
|
80 |
+ options(ProjectUser.options(project.id)), |
|
81 |
+ '_label-> Messages("issue.new.detailInfo.assignee"), |
|
82 |
+ '_default -> Messages("issue.new.selectDefault.assignee"), |
|
83 |
+ '_showConstraints -> false) |
|
81 | 84 |
} |
82 |
- @isVisible(models.enumeration.Resource.ISSUE_COMPONENT) { |
|
83 |
- @select( |
|
84 |
- issueForm("componentName"), |
|
85 |
- options = options( |
|
86 |
- "component_1"->"Component id 1"), |
|
87 |
- '_label-> Messages("issue.new.detailInfo.component"), |
|
88 |
- '_default -> Messages("issue.new.selectDefault.component"), |
|
89 |
- '_showConstraints -> false) |
|
90 |
- } |
|
91 |
- @isVisible(models.enumeration.Resource.ISSUE_MILESTONE) { |
|
92 |
- @select( |
|
93 |
- issueForm("milestoneId"), |
|
94 |
- options(Milestone.options(project.id)), |
|
95 |
- '_label-> Messages("issue.new.detailInfo.milestone"), |
|
96 |
- '_default -> Messages("issue.new.selectDefault.milestone"), |
|
97 |
- '_showConstraints -> false) |
|
98 |
- } |
|
99 |
- </div> |
|
100 |
- </div> |
|
101 |
- |
|
102 |
- </fieldset> |
|
103 |
- <fieldset> |
|
104 |
- <div class="well"> |
|
105 |
- <legend><b>@Messages("issue.new.result")</b></legend> |
|
106 |
- <div class="well form-inline"> |
|
107 |
- @isVisible(models.enumeration.Resource.ISSUE_IMPORTANCE) { |
|
108 |
- @selectEx( |
|
109 |
- issueForm("importance"), |
|
110 |
- options(Issue.importances), |
|
111 |
- '_label-> Messages("issue.new.result.importance"), |
|
112 |
- '_default -> Messages("issue.new.selectDefault.importance"), |
|
113 |
- '_showConstraints -> false) |
|
114 |
- } |
|
115 |
- @isVisible(models.enumeration.Resource.ISSUE_DIAGNOSISRESULT) { |
|
116 |
- @selectEx( |
|
117 |
- issueForm("diagnosisResult"), |
|
118 |
- options(Issue.diagnosisResults), |
|
119 |
- '_label-> Messages("issue.new.result.diagnosisResult"), |
|
120 |
- '_default -> Messages("issue.new.selectDefault.diagnosisResult"), |
|
121 |
- '_showConstraints -> false) |
|
122 |
- } |
|
123 |
- </div> |
|
124 |
- </div> |
|
125 |
- </fieldset> |
|
85 |
+ @isVisible(models.enumeration.Resource.ISSUE_MILESTONE) { |
|
86 |
+ @select( |
|
87 |
+ issueForm("milestoneId"), |
|
88 |
+ options(Milestone.options(project.id)), |
|
89 |
+ '_label-> Messages("issue.new.detailInfo.milestone"), |
|
90 |
+ '_default -> Messages("issue.new.selectDefault.milestone"), |
|
91 |
+ '_showConstraints -> false) |
|
92 |
+ } |
|
93 |
+ </div> |
|
94 |
+ </div> |
|
95 |
+ </fieldset> |
|
126 | 96 |
|
127 |
- <div class="row pull-right"> |
|
128 |
- <div class="actions"> |
|
129 |
- <input type="submit" class="btn btn-primary" value="@Messages("button.save")"> |
|
130 |
- <a href="@routes.IssueApp.issues(project.owner, project.name, "all")" class="btn">@Messages("button.cancel")</a> |
|
131 |
- </div> |
|
97 |
+ <div class="row pull-right"> |
|
98 |
+ <div class="actions"> |
|
99 |
+ <input type="submit" class="btn btn-primary" value="@Messages("button.save")"> |
|
100 |
+ <a href="@routes.IssueApp.issues(project.owner, project.name, "all")" class="btn">@Messages("button.cancel")</a> |
|
101 |
+ </div> |
|
132 | 102 |
</div> |
133 | 103 |
} |
134 | 104 |
|
135 | 105 |
@views.html.markdown() |
136 | 106 |
<script type="text/javascript"> |
137 | 107 |
nforge.require('shortcut.submit'); |
108 |
+ nforge.require('issue.label', '@routes.IssueLabelApp.getAll(project.owner, project.name)', '@routes.IssueLabelApp.post(project.owner, project.name)'); |
|
138 | 109 |
</script> |
139 | 110 |
} |
--- app/views/layout.scala.html
+++ app/views/layout.scala.html
... | ... | @@ -33,6 +33,8 @@ |
33 | 33 |
<script src="@getJSLink("modules/code")" type="text/javascript"></script> |
34 | 34 |
<script src="@getJSLink("modules/markdown")" type="text/javascript"></script> |
35 | 35 |
<script src="@getJSLink("modules/shortcut")" type="text/javascript"></script> |
36 |
+ <script src="@getJSLink("rgbcolor")" type="text/javascript"></script> |
|
37 |
+ <script src="@getJSLink("jquery.form")" type="text/javascript"></script> |
|
36 | 38 |
</head> |
37 | 39 |
|
38 | 40 |
<body class="@thema"> |
--- app/views/milestone/list.scala.html
+++ app/views/milestone/list.scala.html
... | ... | @@ -65,8 +65,8 @@ |
65 | 65 |
@milestone.contents |
66 | 66 |
</td> |
67 | 67 |
<td> |
68 |
- <a href="@makeIssuesLink(milestone.id,"closed")">@milestone.numClosedIssues</a> <span>@Messages("issue.stateType.closed")</span> |
|
69 |
- <a href="@makeIssuesLink(milestone.id,"open")">@milestone.numOpenIssues</a> <span>@Messages("issue.stateType.open")</span> |
|
68 |
+ <a href="@makeIssuesLink(milestone.id,"closed")">@milestone.numClosedIssues</a> <span>@Messages("issue.state.closed")</span> |
|
69 |
+ <a href="@makeIssuesLink(milestone.id,"open")">@milestone.numOpenIssues</a> <span>@Messages("issue.state.open")</span> |
|
70 | 70 |
<div class="progress"> |
71 | 71 |
<div class="bar" style="width: @milestone.completionRate%;"></div> |
72 | 72 |
</div> |
... | ... | @@ -81,4 +81,4 @@ |
81 | 81 |
<div style="text-align: right"> |
82 | 82 |
<a class="btn btn-primary" href="@routes.MilestoneApp.manageMilestones(projectInst.owner, projectInst.name)" >@Messages("milestone.menu.manage")</a> |
83 | 83 |
</div> |
84 |
-}(No newline at end of file) |
|
84 |
+} |
--- conf/initial-data.yml
+++ conf/initial-data.yml
... | ... | @@ -98,8 +98,7 @@ |
98 | 98 |
authorId: 2 |
99 | 99 |
title: 불필요한 로그 출력 코드 제거 |
100 | 100 |
body: 내용 불필요한~ |
101 |
- state: ENROLLED |
|
102 |
- stateType: OPEN |
|
101 |
+ state: OPEN |
|
103 | 102 |
milestoneId: 1 |
104 | 103 |
project: !!models.Project |
105 | 104 |
id: 1 |
... | ... | @@ -108,8 +107,7 @@ |
108 | 107 |
assigneeId: 2 |
109 | 108 |
title: 다운로드는 익명 댓글에도 사용자명에 링크가 걸림 |
110 | 109 |
body: 내용 다운로드는 익명 댓글에도 사용자명에 링크가 걸림 |
111 |
- state: ASSIGNED |
|
112 |
- stateType: OPEN |
|
110 |
+ state: OPEN |
|
113 | 111 |
milestoneId: 2 |
114 | 112 |
project: !!models.Project |
115 | 113 |
id: 1 |
... | ... | @@ -118,10 +116,8 @@ |
118 | 116 |
assigneeId: 2 |
119 | 117 |
title: gittracker.php의 메모리 제한 에러 |
120 | 118 |
body: 내용 gittracker.php의 메모리 제한 에러 |
121 |
- state: SOLVED |
|
122 |
- stateType: CLOSED |
|
119 |
+ state: CLOSED |
|
123 | 120 |
milestoneId: 2 |
124 |
- diagnosisResult: 2 |
|
125 | 121 |
project: !!models.Project |
126 | 122 |
id: 1 |
127 | 123 |
numOfComments: 1 |
... | ... | @@ -130,10 +126,8 @@ |
130 | 126 |
assigneeId: 3 |
131 | 127 |
title: git/hg 코드 브라우저에 i18n이 적용되지 않음 |
132 | 128 |
body: 내용 git/hg 코드 브라우저에 i18n이 적용되지 않음 |
133 |
- state: FINISHED |
|
134 |
- stateType: CLOSED |
|
129 |
+ state: CLOSED |
|
135 | 130 |
milestoneId: 2 |
136 |
- diagnosisResult: 5 |
|
137 | 131 |
project: !!models.Project |
138 | 132 |
id: 1 |
139 | 133 |
- !!models.Issue |
... | ... | @@ -141,8 +135,7 @@ |
141 | 135 |
assigneeId: 3 |
142 | 136 |
title: CUBRID 설치 문제 |
143 | 137 |
body: IOS는 설치 못하나요? |
144 |
- state: ASSIGNED |
|
145 |
- stateType: OPEN |
|
138 |
+ state: OPEN |
|
146 | 139 |
milestoneId: 0 |
147 | 140 |
project: !!models.Project |
148 | 141 |
id: 3 |
... | ... | @@ -151,10 +144,8 @@ |
151 | 144 |
assigneeId: 3 |
152 | 145 |
title: 메모리 누수 현상 |
153 | 146 |
body: 메모리가 너무 누수가 되는듯. |
154 |
- state: FINISHED |
|
155 |
- stateType: CLOSED |
|
147 |
+ state: CLOSED |
|
156 | 148 |
milestoneId: 6 |
157 |
- diagnosisResult: 1 |
|
158 | 149 |
project: !!models.Project |
159 | 150 |
id: 3 |
160 | 151 |
- !!models.Issue |
... | ... | @@ -162,8 +153,7 @@ |
162 | 153 |
assigneeId: 3 |
163 | 154 |
title: Client application for Mac. |
164 | 155 |
body: Please make it. |
165 |
- state: ASSIGNED |
|
166 |
- stateType: OPEN |
|
156 |
+ state: OPEN |
|
167 | 157 |
milestoneId: 5 |
168 | 158 |
project: !!models.Project |
169 | 159 |
id: 3 |
... | ... | @@ -172,10 +162,8 @@ |
172 | 162 |
assigneeId: 3 |
173 | 163 |
title: CPU 무한 점유울 문제. |
174 | 164 |
body: CPU를 무한사용 중이에요. |
175 |
- state: FINISHED |
|
176 |
- stateType: CLOSED |
|
165 |
+ state: CLOSED |
|
177 | 166 |
milestoneId: 5 |
178 |
- diagnosisResult: 4 |
|
179 | 167 |
project: !!models.Project |
180 | 168 |
id: 3 |
181 | 169 |
- !!models.Issue |
... | ... | @@ -183,10 +171,8 @@ |
183 | 171 |
assigneeId: 3 |
184 | 172 |
title: Less chained imports causes compile error |
185 | 173 |
body: When using Play, when I chain less files such as a.less imports b.less which in turn imports c.less |
186 |
- state: FINISHED |
|
187 |
- stateType: CLOSED |
|
174 |
+ state: CLOSED |
|
188 | 175 |
milestoneId: 1 |
189 |
- diagnosisResult: 4 |
|
190 | 176 |
project: !!models.Project |
191 | 177 |
id: 1 |
192 | 178 |
- !!models.Issue |
... | ... | @@ -194,18 +180,15 @@ |
194 | 180 |
assigneeId: 2 |
195 | 181 |
title: Weird TypeDoesNotMatch exception in RC-3 and final |
196 | 182 |
body: The following code works as expected in RC1-Snapshot, but breaks with an TypeDoesNotMatch exception in RC-3 and 2.0 final. |
197 |
- state: FINISHED |
|
198 |
- stateType: CLOSED |
|
183 |
+ state: CLOSED |
|
199 | 184 |
milestoneId: 1 |
200 |
- diagnosisResult: 3 |
|
201 | 185 |
project: !!models.Project |
202 | 186 |
id: 1 |
203 | 187 |
- !!models.Issue |
204 | 188 |
authorId: 1 |
205 | 189 |
title: Anorm and PostgreSQL - return value not accessible |
206 | 190 |
body: PostgreSQL allows you to insert a row and obtain the id of the new row in one query |
207 |
- state: ENROLLED |
|
208 |
- stateType: OPEN |
|
191 |
+ state: OPEN |
|
209 | 192 |
milestoneId: 1 |
210 | 193 |
project: !!models.Project |
211 | 194 |
id: 1 |
... | ... | @@ -214,10 +197,8 @@ |
214 | 197 |
assigneeId: 3 |
215 | 198 |
title: Update sbt-idea to 1.1.0 |
216 | 199 |
body: Create sbt project definition module, if exists, for each subproject (pull 128) |
217 |
- state: FINISHED |
|
218 |
- stateType: CLOSED |
|
200 |
+ state: CLOSED |
|
219 | 201 |
milestoneId: 3 |
220 |
- diagnosisResult: 3 |
|
221 | 202 |
project: !!models.Project |
222 | 203 |
id: 2 |
223 | 204 |
- !!models.Issue |
... | ... | @@ -225,10 +206,8 @@ |
225 | 206 |
assigneeId: 3 |
226 | 207 |
title: Support Tuple 22, not just Tuple 18 in api/data/Forms.scala |
227 | 208 |
body: While creating some complex forms, Eclipse started hanging for some unknown reason. |
228 |
- state: FINISHED |
|
229 |
- stateType: CLOSED |
|
209 |
+ state: CLOSED |
|
230 | 210 |
milestoneId: 3 |
231 |
- diagnosisResult: 3 |
|
232 | 211 |
project: !!models.Project |
233 | 212 |
id: 2 |
234 | 213 |
- !!models.Issue |
... | ... | @@ -236,8 +215,7 @@ |
236 | 215 |
assigneeId: 3 |
237 | 216 |
title: Support Tuple 22, not just Tuple 18 in api/data/Forms.scala |
238 | 217 |
body: While creating some complex forms, Eclipse started hanging for some unknown reason. |
239 |
- state: ASSIGNED |
|
240 |
- stateType: OPEN |
|
218 |
+ state: OPEN |
|
241 | 219 |
milestoneId: 4 |
242 | 220 |
project: !!models.Project |
243 | 221 |
id: 2 |
... | ... | @@ -246,8 +224,7 @@ |
246 | 224 |
assigneeId: 3 |
247 | 225 |
title: Cookie Expires date |
248 | 226 |
body: With Play 2.0.2, when creating a cookie with response().setCookie, HTTP output is like |
249 |
- state: ASSIGNED |
|
250 |
- stateType: OPEN |
|
227 |
+ state: OPEN |
|
251 | 228 |
milestoneId: 4 |
252 | 229 |
project: !!models.Project |
253 | 230 |
id: 2 |
... | ... | @@ -256,8 +233,7 @@ |
256 | 233 |
assigneeId: 3 |
257 | 234 |
title: require is not work in windows (Closure Compiler) |
258 | 235 |
body: Test on Play2.1-Snapshot |
259 |
- state: ASSIGNED |
|
260 |
- stateType: OPEN |
|
236 |
+ state: OPEN |
|
261 | 237 |
milestoneId: 4 |
262 | 238 |
project: !!models.Project |
263 | 239 |
id: 2 |
... | ... | @@ -266,8 +242,7 @@ |
266 | 242 |
assigneeId: 3 |
267 | 243 |
title: Remember me is not working |
268 | 244 |
body: Test on Play2.1-Snapshot |
269 |
- state: ASSIGNED |
|
270 |
- stateType: OPEN |
|
245 |
+ state: OPEN |
|
271 | 246 |
milestoneId: 4 |
272 | 247 |
project: !!models.Project |
273 | 248 |
id: 3 |
... | ... | @@ -363,12 +338,6 @@ |
363 | 338 |
- !!models.Permission |
364 | 339 |
id: 8 |
365 | 340 |
- !!models.Permission |
366 |
- id: 9 |
|
367 |
- - !!models.Permission |
|
368 |
- id: 10 |
|
369 |
- - !!models.Permission |
|
370 |
- id: 11 |
|
371 |
- - !!models.Permission |
|
372 | 341 |
id: 12 |
373 | 342 |
- !!models.Permission |
374 | 343 |
id: 13 |
... | ... | @@ -381,12 +350,6 @@ |
381 | 350 |
- !!models.Permission |
382 | 351 |
id: 17 |
383 | 352 |
- !!models.Permission |
384 |
- id: 18 |
|
385 |
- - !!models.Permission |
|
386 |
- id: 19 |
|
387 |
- - !!models.Permission |
|
388 |
- id: 20 |
|
389 |
- - !!models.Permission |
|
390 | 353 |
id: 21 |
391 | 354 |
- !!models.Permission |
392 | 355 |
id: 22 |
... | ... | @@ -398,18 +361,6 @@ |
398 | 361 |
id: 25 |
399 | 362 |
- !!models.Permission |
400 | 363 |
id: 26 |
401 |
- - !!models.Permission |
|
402 |
- id: 27 |
|
403 |
- - !!models.Permission |
|
404 |
- id: 28 |
|
405 |
- - !!models.Permission |
|
406 |
- id: 29 |
|
407 |
- - !!models.Permission |
|
408 |
- id: 30 |
|
409 |
- - !!models.Permission |
|
410 |
- id: 31 |
|
411 |
- - !!models.Permission |
|
412 |
- id: 32 |
|
413 | 364 |
- !!models.Permission |
414 | 365 |
id: 33 |
415 | 366 |
- !!models.Permission |
... | ... | @@ -485,10 +436,6 @@ |
485 | 436 |
- !!models.Permission |
486 | 437 |
id: 6 |
487 | 438 |
- !!models.Permission |
488 |
- id: 9 |
|
489 |
- - !!models.Permission |
|
490 |
- id: 10 |
|
491 |
- - !!models.Permission |
|
492 | 439 |
id: 12 |
493 | 440 |
- !!models.Permission |
494 | 441 |
id: 13 |
... | ... | @@ -501,12 +448,6 @@ |
501 | 448 |
- !!models.Permission |
502 | 449 |
id: 17 |
503 | 450 |
- !!models.Permission |
504 |
- id: 18 |
|
505 |
- - !!models.Permission |
|
506 |
- id: 19 |
|
507 |
- - !!models.Permission |
|
508 |
- id: 20 |
|
509 |
- - !!models.Permission |
|
510 | 451 |
id: 21 |
511 | 452 |
- !!models.Permission |
512 | 453 |
id: 22 |
... | ... | @@ -518,18 +459,6 @@ |
518 | 459 |
id: 25 |
519 | 460 |
- !!models.Permission |
520 | 461 |
id: 26 |
521 |
- - !!models.Permission |
|
522 |
- id: 27 |
|
523 |
- - !!models.Permission |
|
524 |
- id: 28 |
|
525 |
- - !!models.Permission |
|
526 |
- id: 29 |
|
527 |
- - !!models.Permission |
|
528 |
- id: 30 |
|
529 |
- - !!models.Permission |
|
530 |
- id: 31 |
|
531 |
- - !!models.Permission |
|
532 |
- id: 32 |
|
533 | 462 |
- !!models.Permission |
534 | 463 |
id: 33 |
535 | 464 |
- !!models.Permission |
... | ... | @@ -583,12 +512,6 @@ |
583 | 512 |
- !!models.Permission |
584 | 513 |
id: 8 |
585 | 514 |
- !!models.Permission |
586 |
- id: 9 |
|
587 |
- - !!models.Permission |
|
588 |
- id: 10 |
|
589 |
- - !!models.Permission |
|
590 |
- id: 11 |
|
591 |
- - !!models.Permission |
|
592 | 515 |
id: 12 |
593 | 516 |
- !!models.Permission |
594 | 517 |
id: 13 |
... | ... | @@ -601,12 +524,6 @@ |
601 | 524 |
- !!models.Permission |
602 | 525 |
id: 17 |
603 | 526 |
- !!models.Permission |
604 |
- id: 18 |
|
605 |
- - !!models.Permission |
|
606 |
- id: 19 |
|
607 |
- - !!models.Permission |
|
608 |
- id: 20 |
|
609 |
- - !!models.Permission |
|
610 | 527 |
id: 21 |
611 | 528 |
- !!models.Permission |
612 | 529 |
id: 22 |
... | ... | @@ -618,18 +535,6 @@ |
618 | 535 |
id: 25 |
619 | 536 |
- !!models.Permission |
620 | 537 |
id: 26 |
621 |
- - !!models.Permission |
|
622 |
- id: 27 |
|
623 |
- - !!models.Permission |
|
624 |
- id: 28 |
|
625 |
- - !!models.Permission |
|
626 |
- id: 29 |
|
627 |
- - !!models.Permission |
|
628 |
- id: 30 |
|
629 |
- - !!models.Permission |
|
630 |
- id: 31 |
|
631 |
- - !!models.Permission |
|
632 |
- id: 32 |
|
633 | 538 |
- !!models.Permission |
634 | 539 |
id: 33 |
635 | 540 |
- !!models.Permission |
... | ... | @@ -707,23 +612,13 @@ |
707 | 612 |
- !!models.Permission |
708 | 613 |
id: 6 |
709 | 614 |
- !!models.Permission |
710 |
- id: 9 |
|
711 |
- - !!models.Permission |
|
712 |
- id: 10 |
|
713 |
- - !!models.Permission |
|
714 | 615 |
id: 12 |
715 | 616 |
- !!models.Permission |
716 | 617 |
id: 15 |
717 | 618 |
- !!models.Permission |
718 |
- id: 18 |
|
719 |
- - !!models.Permission |
|
720 | 619 |
id: 21 |
721 | 620 |
- !!models.Permission |
722 | 621 |
id: 24 |
723 |
- - !!models.Permission |
|
724 |
- id: 27 |
|
725 |
- - !!models.Permission |
|
726 |
- id: 30 |
|
727 | 622 |
- !!models.Permission |
728 | 623 |
id: 33 |
729 | 624 |
- !!models.Permission |
... | ... | @@ -782,19 +677,6 @@ |
782 | 677 |
operation: delete |
783 | 678 |
|
784 | 679 |
- !!models.Permission |
785 |
- id: 9 |
|
786 |
- resource: issue_environment |
|
787 |
- operation: read |
|
788 |
- - !!models.Permission |
|
789 |
- id: 10 |
|
790 |
- resource: issue_environment |
|
791 |
- operation: write |
|
792 |
- - !!models.Permission |
|
793 |
- id: 11 |
|
794 |
- resource: issue_environment |
|
795 |
- operation: edit |
|
796 |
- |
|
797 |
- - !!models.Permission |
|
798 | 680 |
id: 12 |
799 | 681 |
resource: issue_assignee |
800 | 682 |
operation: read |
... | ... | @@ -821,19 +703,6 @@ |
821 | 703 |
operation: edit |
822 | 704 |
|
823 | 705 |
- !!models.Permission |
824 |
- id: 18 |
|
825 |
- resource: issue_importance |
|
826 |
- operation: read |
|
827 |
- - !!models.Permission |
|
828 |
- id: 19 |
|
829 |
- resource: issue_importance |
|
830 |
- operation: write |
|
831 |
- - !!models.Permission |
|
832 |
- id: 20 |
|
833 |
- resource: issue_importance |
|
834 |
- operation: edit |
|
835 |
- |
|
836 |
- - !!models.Permission |
|
837 | 706 |
id: 21 |
838 | 707 |
resource: issue_category |
839 | 708 |
operation: read |
... | ... | @@ -857,32 +726,6 @@ |
857 | 726 |
- !!models.Permission |
858 | 727 |
id: 26 |
859 | 728 |
resource: issue_milestone |
860 |
- operation: edit |
|
861 |
- |
|
862 |
- - !!models.Permission |
|
863 |
- id: 27 |
|
864 |
- resource: issue_component |
|
865 |
- operation: read |
|
866 |
- - !!models.Permission |
|
867 |
- id: 28 |
|
868 |
- resource: issue_component |
|
869 |
- operation: write |
|
870 |
- - !!models.Permission |
|
871 |
- id: 29 |
|
872 |
- resource: issue_component |
|
873 |
- operation: edit |
|
874 |
- |
|
875 |
- - !!models.Permission |
|
876 |
- id: 30 |
|
877 |
- resource: issue_diagnosisResult |
|
878 |
- operation: read |
|
879 |
- - !!models.Permission |
|
880 |
- id: 31 |
|
881 |
- resource: issue_diagnosisResult |
|
882 |
- operation: write |
|
883 |
- - !!models.Permission |
|
884 |
- id: 32 |
|
885 |
- resource: issue_diagnosisResult |
|
886 | 729 |
operation: edit |
887 | 730 |
|
888 | 731 |
- !!models.Permission |
... | ... | @@ -1022,19 +865,6 @@ |
1022 | 865 |
resource: site_setting |
1023 | 866 |
operation: write |
1024 | 867 |
|
1025 |
- - !!models.Permission |
|
1026 |
- id: 65 |
|
1027 |
- resource: issue_importance |
|
1028 |
- operation: read |
|
1029 |
- - !!models.Permission |
|
1030 |
- id: 66 |
|
1031 |
- resource: issue_importance |
|
1032 |
- operation: write |
|
1033 |
- - !!models.Permission |
|
1034 |
- id: 67 |
|
1035 |
- resource: issue_importance |
|
1036 |
- operation: edit |
|
1037 |
- |
|
1038 | 868 |
# ProjectUser |
1039 | 869 |
projectUsers: |
1040 | 870 |
- !!models.ProjectUser |
... | ... | @@ -1197,6 +1027,3 @@ |
1197 | 1027 |
body: test item2 |
1198 | 1028 |
checklist : !!models.task.Checklist |
1199 | 1029 |
id: 1 |
1200 |
- |
|
1201 |
- |
|
1202 |
-(No newline at end of file) |
--- conf/messages.ko
+++ conf/messages.ko
... | ... | @@ -77,13 +77,9 @@ |
77 | 77 |
sort.by.completionRate = 완료울순 |
78 | 78 |
|
79 | 79 |
#Issue |
80 |
-issue.stateType.all = 전체 |
|
81 |
-issue.stateType.open = 미해결 |
|
82 |
-issue.stateType.closed = 해결 |
|
83 |
-issue.state.assigned = 진행중 |
|
84 |
-issue.state.enrolled = 등록 |
|
85 |
-issue.state.solved = 해결 |
|
86 |
-issue.state.finished = 닫힘 |
|
80 |
+issue.state.all = 전체 |
|
81 |
+issue.state.open = 미해결 |
|
82 |
+issue.state.closed = 해결 |
|
87 | 83 |
issue.is.empty = 등록된 이슈가 없습니다. |
88 | 84 |
issue.menu.searchDefault = 이슈 검색 |
89 | 85 |
issue.menu.milestoneSelectDefault = 마일스톤 선택 |
--- conf/routes
+++ conf/routes
... | ... | @@ -70,9 +70,9 @@ |
70 | 70 |
GET /:userName/:projectName/milestone/:id/edit controllers.MilestoneApp.editMilestone(userName: String, projectName:String, id: Long) |
71 | 71 |
|
72 | 72 |
# Issues |
73 |
-GET /:userName/:projectName/issueList controllers.IssueApp.issues(userName:String, projectName:String, stateType:String) |
|
73 |
+GET /:userName/:projectName/issueList controllers.IssueApp.issues(userName:String, projectName:String, state:String) |
|
74 | 74 |
GET /:userName/:projectName/issueList/autonotify controllers.IssueApp.enrollAutoNotification(userName:String, projectName:String) |
75 |
-GET /:userName/:projectName/issueList/excel controllers.IssueApp.extractExcelFile(userName:String, projectName:String, stateType:String) |
|
75 |
+GET /:userName/:projectName/issueList/excel controllers.IssueApp.extractExcelFile(userName:String, projectName:String, state:String) |
|
76 | 76 |
GET /:userName/:projectName/issues/new controllers.IssueApp.newIssue(userName:String, projectName:String) |
77 | 77 |
POST /:userName/:projectName/issues/new controllers.IssueApp.saveIssue(userName:String, projectName:String) |
78 | 78 |
GET /:userName/:projectName/issues/:id controllers.IssueApp.issue(userName:String, projectName:String, id:Long) |
... | ... | @@ -83,6 +83,10 @@ |
83 | 83 |
POST /:userName/:projectName/issues/:id/edit controllers.IssueApp.updateIssue(userName:String, projectName:String, id:Long) |
84 | 84 |
GET /issuedetail controllers.IssueApp.getIssueDatil() |
85 | 85 |
|
86 |
+# Label |
|
87 |
+POST /:userName/:projectName/labels controllers.IssueLabelApp.post(userName:String, projectName:String) |
|
88 |
+GET /:userName/:projectName/labels controllers.IssueLabelApp.getAll(userName:String, projectName:String) |
|
89 |
+POST /:userName/:projectName/labels/:id controllers.IssueLabelApp.delete(userName:String, projectName:String, id:Long) |
|
86 | 90 |
|
87 | 91 |
# Git |
88 | 92 |
GET /:ownerName/:projectName/info/refs controllers.GitApp.advertise(ownerName:String, projectName:String) |
--- public/javascripts/modules/issue.js
+++ public/javascripts/modules/issue.js
... | ... | @@ -1,53 +1,299 @@ |
1 | 1 |
nforge.namespace('issue'); |
2 |
-nforge.issue.list = function () { |
|
3 |
- var that, |
|
4 |
- $checkboxForm, |
|
5 |
- $searchForm, |
|
6 |
- $pagination; |
|
2 |
+ |
|
3 |
+// Compute a color contrasted with the given color. |
|
4 |
+// e.g.) dimgray if yellow is given. |
|
5 |
+var contrasted_color = function(color) { |
|
6 |
+ var rgb = new RGBColor(color); |
|
7 |
+ // Compute lightness. See http://en.wikipedia.org/wiki/Luma_(video) |
|
8 |
+ var y709 = rgb.r * 0.21 + 0.72 * rgb.g + rgb.b * 0.07; |
|
9 |
+ return y709 > 192 ? 'dimgray' : 'white'; |
|
10 |
+}; |
|
11 |
+ |
|
12 |
+nforge.issue.label = function () { |
|
13 |
+ var that; |
|
14 |
+ |
|
7 | 15 |
that = { |
8 |
- init : function () { |
|
9 |
- $checkboxForm = $("#checkboxForm"); |
|
10 |
- $searchForm = $("#searchForm"); |
|
11 |
- $pagination = $("#pagination"); |
|
12 |
- that.setEvent(); |
|
13 |
- }, |
|
14 |
- |
|
15 |
- setEvent : function () { |
|
16 |
- $checkboxForm.find('.filters').click(that.filter); |
|
17 |
- $('.th-sort').click(that.search); |
|
18 |
- |
|
19 |
- $("#milestone").change(function () { |
|
20 |
- $searchForm.submit(); |
|
21 |
- }); |
|
22 |
- |
|
23 |
- /* TODO */ |
|
24 |
- $pagination.click(function () { |
|
25 |
- console.log('TBD..'); |
|
26 |
- }); |
|
27 |
- }, |
|
28 |
- |
|
29 |
- filter : function (e) { |
|
30 |
- setTimeout(function () { |
|
31 |
- $checkboxForm.submit(); |
|
32 |
- }, 1); |
|
33 |
- }, |
|
34 |
- |
|
35 |
- search : function (e) { |
|
36 |
- nforge.stopEvent(e); |
|
37 |
- var _self = $(this), |
|
38 |
- sortBy = _self.data('sortBy'), |
|
39 |
- $hiddenInputs = $searchForm.find('h-value'), |
|
40 |
- $sortInput = $hiddenInputs.filter('.sort'), |
|
41 |
- $orderInput = $hiddenInputs.filter('.order'), |
|
42 |
- orderVal = $orderInput.val(); |
|
43 |
- |
|
44 |
- if ($sortInput.val() !== sortBy) { |
|
45 |
- $sortInput.val(sortBy); |
|
46 |
- } else { |
|
47 |
- $orderInput.val(orderVal === 'asc' ? 'desc' : orderVal); |
|
16 |
+ init: function(urlToLabels, urlToPost, options) { |
|
17 |
+ that.urlToLabels = urlToLabels; |
|
18 |
+ that.urlToPost = urlToPost; |
|
19 |
+ that.options = { |
|
20 |
+ editable: false |
|
21 |
+ }; |
|
22 |
+ for(var key in options) { |
|
23 |
+ that.options[key] = options[key]; |
|
48 | 24 |
} |
49 |
- $searchForm.submit(); |
|
25 |
+ |
|
26 |
+ if (that.options.editable) { |
|
27 |
+ that.addLabelEditor(); |
|
28 |
+ } |
|
29 |
+ that.setEvent(); |
|
30 |
+ that.updateLabels(); |
|
31 |
+ }, |
|
32 |
+ |
|
33 |
+ addLabelEditor: function() { |
|
34 |
+ var div = $('<div>') |
|
35 |
+ .addClass('control-group'); |
|
36 |
+ |
|
37 |
+ var label = $('<label id="custom-label-label">') |
|
38 |
+ .addClass('control-label') |
|
39 |
+ .text('New Label'); |
|
40 |
+ |
|
41 |
+ var controls = $('<div id="custom-label">') |
|
42 |
+ .addClass('controls'); |
|
43 |
+ |
|
44 |
+ /* |
|
45 |
+ colors = ['gray', 'red', 'orange', 'yellow', 'green', |
|
46 |
+ 'CornflowerBlue', 'blue', 'purple', 'white']; |
|
47 |
+ */ |
|
48 |
+ |
|
49 |
+ colors = ['#999999', '#da5454', '#ff9933', '#ffcc33', '#99ca3c', |
|
50 |
+ '#22b4b9', '#4d68b1', '#9966cc', '#ffffff']; |
|
51 |
+ |
|
52 |
+ for (var i = 0; i < colors.length; i++) { |
|
53 |
+ var button = $('<button type="button">') |
|
54 |
+ .addClass('issue-label') |
|
55 |
+ .css('background-color', colors[i]); |
|
56 |
+ controls.append(button); |
|
57 |
+ } |
|
58 |
+ |
|
59 |
+ var input_color = $('<input id="custom-label-color" type="text">') |
|
60 |
+ .addClass('input-medium') |
|
61 |
+ .attr('placeholder', 'Custom Color'); |
|
62 |
+ |
|
63 |
+ var input_category = $('<input id="custom-label-category" type="text">') |
|
64 |
+ .addClass('input-small') |
|
65 |
+ .attr('data-provider', 'typeahead') |
|
66 |
+ .attr('placeholder', 'Category'); |
|
67 |
+ |
|
68 |
+ var input_name = $('<input id="custom-label-name" type="text">') |
|
69 |
+ .addClass('input-small') |
|
70 |
+ .attr('placeholder', 'Name'); |
|
71 |
+ |
|
72 |
+ var input_submit = $('<button id="custom-label-submit" type="button">') |
|
73 |
+ .addClass('btn') |
|
74 |
+ .text('Add'); |
|
75 |
+ |
|
76 |
+ controls.append(input_color); |
|
77 |
+ controls.append(input_category); |
|
78 |
+ controls.append(input_name); |
|
79 |
+ controls.append(input_submit); |
|
80 |
+ |
|
81 |
+ div.append(label); |
|
82 |
+ div.append(controls); |
|
83 |
+ $('fieldset.labels').append(div); |
|
84 |
+ }, |
|
85 |
+ |
|
86 |
+ updateLabels: function() { |
|
87 |
+ var form = $('<form>') |
|
88 |
+ .attr('method', 'get') |
|
89 |
+ .attr('action', that.urlToLabels); |
|
90 |
+ |
|
91 |
+ form.ajaxForm({ |
|
92 |
+ success: function(responseBody, statusText, xhr) { |
|
93 |
+ var labels = responseBody; |
|
94 |
+ |
|
95 |
+ if (!(labels instanceof Object)) { |
|
96 |
+ alert('Failed to update - Server error.'); |
|
97 |
+ return; |
|
98 |
+ } |
|
99 |
+ |
|
100 |
+ for (var i = 0; i < labels.length; i++) { |
|
101 |
+ that.add_label_into_category(labels[i].id, labels[i].category, labels[i].name, labels[i].color); |
|
102 |
+ } |
|
103 |
+ } |
|
104 |
+ }); |
|
105 |
+ |
|
106 |
+ form.submit(); |
|
107 |
+ }, |
|
108 |
+ |
|
109 |
+ setEvent: function() { |
|
110 |
+ $('#custom-label button.issue-label').click(function(e) { |
|
111 |
+ $('#custom-label button.issue-label').removeClass('active'); |
|
112 |
+ $(e.srcElement).addClass('active'); |
|
113 |
+ $('#custom-label-color').val($(e.srcElement).css('background-color')); |
|
114 |
+ $('#custom-label-category').focus(); |
|
115 |
+ $('#custom-label-name').css('background-color', $(e.srcElement).css('background-color')); |
|
116 |
+ var contrasted = contrasted_color($(e.srcElement).css('background-color')); |
|
117 |
+ $('#custom-label-name').css('color', contrasted); |
|
118 |
+ try { |
|
119 |
+ document.styleSheets[0].addRule('#custom-label-name:-moz-placeholder', 'color: ' + contrasted); |
|
120 |
+ document.styleSheets[0].addRule('#custom-label-name:-ms-input-placeholder', 'color: ' + contrasted); |
|
121 |
+ } catch (e) { |
|
122 |
+ document.styleSheets[0].addRule('#custom-label-name::-webkit-input-placeholder', 'color: ' + contrasted); |
|
123 |
+ } |
|
124 |
+ }); |
|
125 |
+ |
|
126 |
+ var issueForm = |