[Notice] Announcing the End of Demo Server [Read me]
--- app/assets/stylesheets/less/_page.less
+++ app/assets/stylesheets/less/_page.less
... | ... | @@ -7273,3 +7273,22 @@ |
7273 | 7273 |
white-space: nowrap; |
7274 | 7274 |
} |
7275 | 7275 |
} |
7276 |
+ |
|
7277 |
+.favorite-issue { |
|
7278 |
+ .my-issue { |
|
7279 |
+ font-size: 16px !important; |
|
7280 |
+ } |
|
7281 |
+ |
|
7282 |
+ .star { |
|
7283 |
+ color: rgba(0, 0, 0, 0.2); |
|
7284 |
+ font-size: 20px; |
|
7285 |
+ |
|
7286 |
+ &:hover { |
|
7287 |
+ color: #e91e63; |
|
7288 |
+ cursor: pointer; |
|
7289 |
+ } |
|
7290 |
+ } |
|
7291 |
+ .starred { |
|
7292 |
+ color: #e91e63 !important; |
|
7293 |
+ } |
|
7294 |
+} |
--- app/controllers/IssueApp.java
+++ app/controllers/IssueApp.java
... | ... | @@ -73,7 +73,7 @@ |
73 | 73 |
// SearchCondition from param |
74 | 74 |
Form<models.support.SearchCondition> issueParamForm = new Form<>(models.support.SearchCondition.class); |
75 | 75 |
models.support.SearchCondition searchCondition = issueParamForm.bindFromRequest().get(); |
76 |
- if (hasNotConditions(searchCondition)) { |
|
76 |
+ if (!searchCondition.hasCondition()) { |
|
77 | 77 |
searchCondition.assigneeId = UserApp.currentUser().id; |
78 | 78 |
} |
79 | 79 |
searchCondition.pageNum = pageNum - 1; |
... | ... | @@ -104,11 +104,6 @@ |
104 | 104 |
default: |
105 | 105 |
return issuesAsHTML(project, issues, searchCondition); |
106 | 106 |
} |
107 |
- } |
|
108 |
- |
|
109 |
- private static boolean hasNotConditions(models.support.SearchCondition searchCondition) { |
|
110 |
- return searchCondition.assigneeId == null && searchCondition.authorId == null && searchCondition.mentionId == null |
|
111 |
- && searchCondition.commenterId == null && searchCondition.sharerId == null; |
|
112 | 107 |
} |
113 | 108 |
|
114 | 109 |
@Transactional |
--- app/controllers/api/UserApi.java
+++ app/controllers/api/UserApi.java
... | ... | @@ -9,6 +9,7 @@ |
9 | 9 |
import com.fasterxml.jackson.databind.JsonNode; |
10 | 10 |
import com.fasterxml.jackson.databind.node.ObjectNode; |
11 | 11 |
import controllers.UserApp; |
12 |
+import models.FavoriteIssue; |
|
12 | 13 |
import models.FavoriteOrganization; |
13 | 14 |
import models.FavoriteProject; |
14 | 15 |
import models.User; |
... | ... | @@ -58,6 +59,42 @@ |
58 | 59 |
return ok(json); |
59 | 60 |
} |
60 | 61 |
|
62 |
+ @Transactional |
|
63 |
+ public static Result toggleFoveriteIssue(String issueId) { |
|
64 |
+ if (issueId == null) { |
|
65 |
+ return badRequest("Wrong issue id"); |
|
66 |
+ } |
|
67 |
+ boolean isFavored = UserApp.currentUser().toggleFavoriteIssue(Long.valueOf(issueId)); |
|
68 |
+ ObjectNode json = Json.newObject(); |
|
69 |
+ json.put("issueId", issueId); |
|
70 |
+ json.put("favored", isFavored); |
|
71 |
+ |
|
72 |
+ if(isFavored) { |
|
73 |
+ json.put("message", Messages.get("issue.favorite.added")); |
|
74 |
+ } else { |
|
75 |
+ json.put("message", Messages.get("issue.favorite.deleted")); |
|
76 |
+ } |
|
77 |
+ |
|
78 |
+ return ok(json); |
|
79 |
+ } |
|
80 |
+ |
|
81 |
+ @Transactional |
|
82 |
+ public static Result getFoveriteIssues() { |
|
83 |
+ ObjectNode json = Json.newObject(); |
|
84 |
+ List<ObjectNode> issues = new ArrayList<>(); |
|
85 |
+ List<Long> issueIds = new ArrayList<>(); |
|
86 |
+ for (FavoriteIssue favoriteIssue : UserApp.currentUser().favoriteIssues) { |
|
87 |
+ ObjectNode project = Json.newObject(); |
|
88 |
+ project.put("issueId", favoriteIssue.issue.id); |
|
89 |
+ project.put("issueTitle", favoriteIssue.issue.title); |
|
90 |
+ project.put("issueAuthorName", favoriteIssue.issue.author.getPureNameOnly()); |
|
91 |
+ issues.add(project); |
|
92 |
+ issueIds.add(favoriteIssue.issue.id); |
|
93 |
+ } |
|
94 |
+ json.put("projectIds", toJson(issueIds)); |
|
95 |
+ json.put("projects", toJson(issues)); |
|
96 |
+ return ok(json); |
|
97 |
+ } |
|
61 | 98 |
|
62 | 99 |
@Transactional |
63 | 100 |
public static Result toggleFoveriteOrganization(String organizationId) { |
+++ app/models/FavoriteIssue.java
... | ... | @@ -0,0 +1,59 @@ |
1 | +/** | |
2 | + * Yona, 21st Century Project Hosting SW | |
3 | + * <p> | |
4 | + * Copyright Yona & Yobi Authors & NAVER Corp. | |
5 | + * https://yona.io | |
6 | + **/ | |
7 | +package models; | |
8 | + | |
9 | +import models.enumeration.State; | |
10 | +import play.db.ebean.Model; | |
11 | + | |
12 | +import javax.annotation.Nonnull; | |
13 | +import javax.persistence.Entity; | |
14 | +import javax.persistence.Id; | |
15 | +import javax.persistence.ManyToOne; | |
16 | +import javax.persistence.OneToOne; | |
17 | +import java.util.List; | |
18 | + | |
19 | +@Entity | |
20 | +public class FavoriteIssue extends Model { | |
21 | + public static Finder<Long, FavoriteIssue> find = new Finder<>(Long.class, FavoriteIssue.class); | |
22 | + | |
23 | + @Id | |
24 | + public Long id; | |
25 | + | |
26 | + @ManyToOne | |
27 | + public User user; | |
28 | + | |
29 | + @OneToOne | |
30 | + public Issue issue; | |
31 | + | |
32 | + public FavoriteIssue(User user, Issue issue) { | |
33 | + this.user = user; | |
34 | + this.issue = issue; | |
35 | + } | |
36 | + | |
37 | + public static void updateFavoriteIssue(@Nonnull Issue issue){ | |
38 | + List<FavoriteIssue> favoriteIssues = find.where().eq("issue.id", issue.id).findList(); | |
39 | + | |
40 | + for (FavoriteIssue favoriteProject : favoriteIssues) { | |
41 | + favoriteProject.issue.refresh(); | |
42 | + favoriteProject.update(); | |
43 | + } | |
44 | + } | |
45 | + | |
46 | + public static FavoriteIssue findByIssueId(Long userId, Long issueId){ | |
47 | + return find.where() | |
48 | + .eq("user.id", userId) | |
49 | + .eq("issue.id", issueId) | |
50 | + .findUnique(); | |
51 | + } | |
52 | + | |
53 | + public static int getNumberOpenFavoriteIssues(Long userId){ | |
54 | + return find.where() | |
55 | + .eq("user.id", userId) | |
56 | + .eq("issue.state", State.OPEN) | |
57 | + .findRowCount(); | |
58 | + } | |
59 | +} |
--- app/models/User.java
+++ app/models/User.java
... | ... | @@ -135,6 +135,9 @@ |
135 | 135 |
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL) |
136 | 136 |
public List<OrganizationUser> groupUser; |
137 | 137 |
|
138 |
+ @OneToMany(mappedBy = "user") |
|
139 |
+ public List<FavoriteIssue> favoriteIssues; |
|
140 |
+ |
|
138 | 141 |
/** |
139 | 142 |
* project which is requested member join |
140 | 143 |
*/ |
... | ... | @@ -984,6 +987,50 @@ |
984 | 987 |
} |
985 | 988 |
} |
986 | 989 |
|
990 |
+ public List<Issue> getFavoriteIssues() { |
|
991 |
+ List<Issue> issues = new ArrayList<>(); |
|
992 |
+ for (FavoriteIssue favoriteIssue : this.favoriteIssues) { |
|
993 |
+ favoriteIssue.issue.refresh(); |
|
994 |
+ issues.add(0, favoriteIssue.issue); |
|
995 |
+ } |
|
996 |
+ |
|
997 |
+ return issues; |
|
998 |
+ } |
|
999 |
+ |
|
1000 |
+ public void updateFavoriteIssue(@Nonnull Issue issue){ |
|
1001 |
+ for (FavoriteIssue favoriteIssue : this.favoriteIssues) { |
|
1002 |
+ if (favoriteIssue.issue.id.equals(issue.id)) { |
|
1003 |
+ favoriteIssue.issue.refresh(); |
|
1004 |
+ } |
|
1005 |
+ } |
|
1006 |
+ } |
|
1007 |
+ |
|
1008 |
+ public boolean toggleFavoriteIssue(Long issueId) { |
|
1009 |
+ for (FavoriteIssue favoriteIssue : this.favoriteIssues) { |
|
1010 |
+ if( favoriteIssue.issue.id.equals(issueId) ){ |
|
1011 |
+ removeFavoriteIssue(issueId); |
|
1012 |
+ this.favoriteIssues.remove(favoriteIssue); |
|
1013 |
+ return false; |
|
1014 |
+ } |
|
1015 |
+ } |
|
1016 |
+ |
|
1017 |
+ FavoriteIssue favoriteIssue = new FavoriteIssue(this, Issue.finder.byId(issueId)); |
|
1018 |
+ this.favoriteIssues.add(favoriteIssue); |
|
1019 |
+ favoriteIssue.save(); |
|
1020 |
+ return true; |
|
1021 |
+ } |
|
1022 |
+ |
|
1023 |
+ public void removeFavoriteIssue(Long issueId) { |
|
1024 |
+ List<FavoriteIssue> list = FavoriteIssue.find.where() |
|
1025 |
+ .eq("user.id", this.id) |
|
1026 |
+ .eq("issue.id", issueId).findList(); |
|
1027 |
+ |
|
1028 |
+ if(list != null && list.size() > 0){ |
|
1029 |
+ favoriteIssues.remove(list.get(0)); |
|
1030 |
+ list.get(0).delete(); |
|
1031 |
+ } |
|
1032 |
+ } |
|
1033 |
+ |
|
987 | 1034 |
public List<Project> getIssueMovableProject(){ |
988 | 1035 |
Set<Project> projects = new LinkedHashSet<>(); |
989 | 1036 |
projects.addAll(getFavoriteProjects()); |
--- app/models/support/SearchCondition.java
+++ app/models/support/SearchCondition.java
... | ... | @@ -34,6 +34,8 @@ |
34 | 34 |
|
35 | 35 |
public Long mentionId; |
36 | 36 |
public Long sharerId; |
37 |
+ public Long favoriteId; |
|
38 |
+ |
|
37 | 39 |
public Organization organization; |
38 | 40 |
public List<String> projectNames; |
39 | 41 |
|
... | ... | @@ -63,6 +65,7 @@ |
63 | 65 |
one.commenterId = this.commenterId; |
64 | 66 |
one.mentionId = this.mentionId; |
65 | 67 |
one.sharerId = this.sharerId; |
68 |
+ one.favoriteId = this.favoriteId; |
|
66 | 69 |
one.dueDate = this.dueDate; |
67 | 70 |
one.projectNames = this.projectNames; |
68 | 71 |
return one; |
... | ... | @@ -116,6 +119,7 @@ |
116 | 119 |
setAuthorIfExist(el); |
117 | 120 |
setMentionedIssuesIfExist(el); |
118 | 121 |
setSharedIssuesIfExist(el); |
122 |
+ setFavoriteIssuesIfExist(el); |
|
119 | 123 |
setFilteredStringIfExist(el); |
120 | 124 |
|
121 | 125 |
if (commentedCheck) { |
... | ... | @@ -188,12 +192,7 @@ |
188 | 192 |
if(!commenter.isAnonymous()) { |
189 | 193 |
List<Long> ids = getCommentedIssueIds(byUser, project); |
190 | 194 |
|
191 |
- if (ids.isEmpty()) { |
|
192 |
- // No need to progress because the query matches nothing. |
|
193 |
- el.idEq(-1); |
|
194 |
- } else { |
|
195 |
- el.idIn(ids); |
|
196 |
- } |
|
195 |
+ updateElWhenIdsEmpty(el, ids); |
|
197 | 196 |
} |
198 | 197 |
} |
199 | 198 |
} |
... | ... | @@ -233,6 +232,7 @@ |
233 | 232 |
setCommenterIfExist(el, null); |
234 | 233 |
setMentionedIssuesIfExist(el); |
235 | 234 |
setSharedIssuesIfExist(el); |
235 |
+ setFavoriteIssuesIfExist(el); |
|
236 | 236 |
setFilteredStringIfExist(el); |
237 | 237 |
setIssueState(el); |
238 | 238 |
setOrderByIfExist(el); |
... | ... | @@ -261,12 +261,7 @@ |
261 | 261 |
if(!byUser.isAnonymous()) { |
262 | 262 |
List<Long> ids = Mention.getMentioningIssueIds(byUser.id); |
263 | 263 |
|
264 |
- if (ids.isEmpty()) { |
|
265 |
- // No need to progress because the query matches nothing. |
|
266 |
- el.idEq(-1); |
|
267 |
- } else { |
|
268 |
- el.idIn(ids); |
|
269 |
- } |
|
264 |
+ updateElWhenIdsEmpty(el, ids); |
|
270 | 265 |
} |
271 | 266 |
} |
272 | 267 |
} |
... | ... | @@ -277,12 +272,26 @@ |
277 | 272 |
if(!byUser.isAnonymous()) { |
278 | 273 |
List<Long> ids = getSharedIssueIds(byUser); |
279 | 274 |
|
280 |
- if (ids.isEmpty()) { |
|
281 |
- // No need to progress because the query matches nothing. |
|
282 |
- el.idEq(-1); |
|
283 |
- } else { |
|
284 |
- el.idIn(ids); |
|
285 |
- } |
|
275 |
+ updateElWhenIdsEmpty(el, ids); |
|
276 |
+ } |
|
277 |
+ } |
|
278 |
+ } |
|
279 |
+ |
|
280 |
+ private void updateElWhenIdsEmpty(ExpressionList<Issue> el, List<Long> ids) { |
|
281 |
+ if (ids.isEmpty()) { |
|
282 |
+ // No need to progress because the query matches nothing. |
|
283 |
+ el.idEq(-1); |
|
284 |
+ } else { |
|
285 |
+ el.idIn(ids); |
|
286 |
+ } |
|
287 |
+ } |
|
288 |
+ |
|
289 |
+ private void setFavoriteIssuesIfExist(ExpressionList<Issue> el) { |
|
290 |
+ if (favoriteId != null) { |
|
291 |
+ if(!byUser.isAnonymous()) { |
|
292 |
+ List<Long> ids = getFavoriteIssueIds(byUser); |
|
293 |
+ |
|
294 |
+ updateElWhenIdsEmpty(el, ids); |
|
286 | 295 |
} |
287 | 296 |
} |
288 | 297 |
} |
... | ... | @@ -314,6 +323,18 @@ |
314 | 323 |
.findList(); |
315 | 324 |
for (IssueSharer issueSharer : issueSharers) { |
316 | 325 |
ids.add(issueSharer.issue.id); |
326 |
+ } |
|
327 |
+ |
|
328 |
+ return new ArrayList<>(ids); |
|
329 |
+ } |
|
330 |
+ |
|
331 |
+ private List<Long> getFavoriteIssueIds(User user) { |
|
332 |
+ Set<Long> ids = new HashSet<>(); |
|
333 |
+ List<FavoriteIssue> favoriteIssues = FavoriteIssue.find.where() |
|
334 |
+ .eq("user.id", user.id) |
|
335 |
+ .findList(); |
|
336 |
+ for (FavoriteIssue favoriteIssue : favoriteIssues) { |
|
337 |
+ ids.add(favoriteIssue.issue.id); |
|
317 | 338 |
} |
318 | 339 |
|
319 | 340 |
return new ArrayList<>(ids); |
... | ... | @@ -364,6 +385,7 @@ |
364 | 385 |
|
365 | 386 |
setCommenterIfExist(el, project); |
366 | 387 |
setSharedIssuesIfExist(el); |
388 |
+ setFavoriteIssuesIfExist(el); |
|
367 | 389 |
setIssueState(el); |
368 | 390 |
setLabelsIfExist(project, el); |
369 | 391 |
setOrderByIfExist(el); |
... | ... | @@ -433,6 +455,15 @@ |
433 | 455 |
return sdf.format(this.dueDate); |
434 | 456 |
} |
435 | 457 |
|
458 |
+ public boolean hasCondition(){ |
|
459 |
+ return !(assigneeId == null |
|
460 |
+ && authorId == null |
|
461 |
+ && mentionId == null |
|
462 |
+ && commenterId == null |
|
463 |
+ && sharerId == null |
|
464 |
+ && favoriteId == null); |
|
465 |
+ } |
|
466 |
+ |
|
436 | 467 |
@Override |
437 | 468 |
public String toString() { |
438 | 469 |
return "SearchCondition{" + |
... | ... | @@ -445,6 +476,7 @@ |
445 | 476 |
", project=" + project + |
446 | 477 |
", mentionId=" + mentionId + |
447 | 478 |
", sharerId=" + sharerId + |
479 |
+ ", favoriteId=" + favoriteId + |
|
448 | 480 |
", organization=" + organization + |
449 | 481 |
", projectNames=" + projectNames + |
450 | 482 |
", commenterId=" + commenterId + |
--- app/views/issue/my_partial_list_quicksearch.scala.html
+++ app/views/issue/my_partial_list_quicksearch.scala.html
... | ... | @@ -21,7 +21,8 @@ |
21 | 21 |
data-commenter-id="" |
22 | 22 |
data-milestone-id="@param.milestoneId" |
23 | 23 |
data-mention-id="" |
24 |
- data-sharer-id=""> |
|
24 |
+ data-sharer-id="" |
|
25 |
+ data-favorite-id=""> |
|
25 | 26 |
@Messages("issue.list.assignedToMe") |
26 | 27 |
</a> |
27 | 28 |
</li> |
... | ... | @@ -32,7 +33,8 @@ |
32 | 33 |
data-commenter-id="" |
33 | 34 |
data-milestone-id="@param.milestoneId" |
34 | 35 |
data-mention-id="" |
35 |
- data-sharer-id=""> |
|
36 |
+ data-sharer-id="" |
|
37 |
+ data-favorite-id=""> |
|
36 | 38 |
@Messages("issue.list.authoredByMe") |
37 | 39 |
</a> |
38 | 40 |
</li> |
... | ... | @@ -43,7 +45,8 @@ |
43 | 45 |
data-commenter-id="@currentUserId" |
44 | 46 |
data-milestone-id="@param.milestoneId" |
45 | 47 |
data-mention-id="" |
46 |
- data-sharer-id=""> |
|
48 |
+ data-sharer-id="" |
|
49 |
+ data-favorite-id=""> |
|
47 | 50 |
@Messages("issue.list.commentedByMe") |
48 | 51 |
</a> |
49 | 52 |
</li> |
... | ... | @@ -54,7 +57,8 @@ |
54 | 57 |
data-commenter-id="" |
55 | 58 |
data-milestone-id="@param.milestoneId" |
56 | 59 |
data-mention-id="@currentUserId" |
57 |
- data-sharer-id=""> |
|
60 |
+ data-sharer-id="" |
|
61 |
+ data-favorite-id=""> |
|
58 | 62 |
@Messages("issue.list.mentionedOfMe") @if(StringUtils.isBlank(param.filter)) { |
59 | 63 |
(@Issue.getCountOfMentionedOpenIssues(currentUserId)) |
60 | 64 |
} |
... | ... | @@ -67,11 +71,30 @@ |
67 | 71 |
data-commenter-id="" |
68 | 72 |
data-milestone-id="@param.milestoneId" |
69 | 73 |
data-mention-id="" |
70 |
- data-sharer-id="@currentUserId"> |
|
74 |
+ data-sharer-id="@currentUserId" |
|
75 |
+ data-favorite-id=""> |
|
71 | 76 |
@Messages("issue.list.sharedWithMe") @if(StringUtils.isBlank(param.filter)) { |
72 | 77 |
(@IssueSharer.getNumberOfIssuesSharedWithUser(currentUserId)) |
73 | 78 |
} |
74 | 79 |
</a> |
75 | 80 |
</li> |
81 |
+ <li @if(param.favoriteId == currentUserId) { |
|
82 |
+ class="active"}> |
|
83 |
+ <a pjax-filter href="#" |
|
84 |
+ data-author-id="" |
|
85 |
+ data-assignee-id="" |
|
86 |
+ data-commenter-id="" |
|
87 |
+ data-milestone-id="@param.milestoneId" |
|
88 |
+ data-mention-id="" |
|
89 |
+ data-sharer-id="" |
|
90 |
+ data-favorite-id="@currentUserId"> |
|
91 |
+ <span class="favorite-issue"> |
|
92 |
+ <i class="starred star material-icons va-text-top my-issue">star</i>@Messages("issue.list.favorite") |
|
93 |
+ </span> |
|
94 |
+ @if(StringUtils.isBlank(param.filter)) { |
|
95 |
+ (@FavoriteIssue.getNumberOpenFavoriteIssues(currentUserId)) |
|
96 |
+ } |
|
97 |
+ </a> |
|
98 |
+ </li> |
|
76 | 99 |
} |
77 | 100 |
</ul> |
--- app/views/issue/my_partial_search.scala.html
+++ app/views/issue/my_partial_search.scala.html
... | ... | @@ -36,6 +36,7 @@ |
36 | 36 |
<input type="hidden" name="assigneeId" value="@param.assigneeId" data-search="assigneeId"> |
37 | 37 |
<input type="hidden" name="mentionId" value="@param.mentionId" data-search="mentionId"> |
38 | 38 |
<input type="hidden" name="sharerId" value="@param.sharerId" data-search="sharerId"> |
39 |
+ <input type="hidden" name="favoriteId" value="@param.favoriteId" data-search="favoriteId"> |
|
39 | 40 |
<div class="search myissues-search-input"> |
40 | 41 |
<div class="search-bar"> |
41 | 42 |
<input name="filter" class="textbox full" type="text" value="@param.filter" placeholder="@Messages("issue.search")"> |
--- app/views/issue/view.scala.html
+++ app/views/issue/view.scala.html
... | ... | @@ -93,6 +93,10 @@ |
93 | 93 |
found |
94 | 94 |
} |
95 | 95 |
|
96 |
+@isFavoriteIssue = @{ |
|
97 |
+ FavoriteIssue.findByIssueId(UserApp.currentUser().id, issue.id) != null |
|
98 |
+} |
|
99 |
+ |
|
96 | 100 |
@projectLayout(titleForOGTag, project, utils.MenuType.ISSUE){ |
97 | 101 |
@projectMenu(project, utils.MenuType.ISSUE, "main-menu-only") |
98 | 102 |
<div class="page-wrap-outer"> |
... | ... | @@ -110,6 +114,9 @@ |
110 | 114 |
<span class="subtask-mark">subtask</span> |
111 | 115 |
} |
112 | 116 |
<strong class="board-id">#@issue.getNumber</strong> @issue.title |
117 |
+ <span class="favorite-issue" data-issue-id="@issue.id"> |
|
118 |
+ <i class="@if(isFavoriteIssue){starred} star material-icons va-text-top">star</i> |
|
119 |
+ </span> |
|
113 | 120 |
<div class="pull-right hide show-in-mobile" style="font-size: 0.7em"> |
114 | 121 |
<span class="date" title="@JodaDateUtil.getDateString(issue.createdDate)"> |
115 | 122 |
@agoOrDateString(issue.createdDate) |
--- app/views/layout.scala.html
+++ app/views/layout.scala.html
... | ... | @@ -55,6 +55,8 @@ |
55 | 55 |
var UsermenuToggleFavoriteProjectUrl = "@api.routes.UserApi.toggleFoveriteProject("")"; |
56 | 56 |
var UsermenuToggleFoveriteOrganizationUrl = "@api.routes.UserApi.toggleFoveriteOrganization("")"; |
57 | 57 |
var UsermenuGetFoveriteProjectsUrl = "@api.routes.UserApi.getFoveriteProjects()"; |
58 |
+ var UsermenuToggleFavoriteIssueUrl = "@api.routes.UserApi.toggleFoveriteIssue("")"; |
|
59 |
+ var UsermenuGetFoveriteIssuesUrl = "@api.routes.UserApi.getFoveriteIssues()"; |
|
58 | 60 |
var UsermenuUrl = "@routes.UserApp.usermenuTabContentList()"; |
59 | 61 |
</script> |
60 | 62 |
<script type="text/javascript" src="@routes.Assets.at("javascripts/common/yona.Usermenu.js")"></script> |
+++ conf/evolutions/default/23.sql
... | ... | @@ -0,0 +1,18 @@ |
1 | +# --- !Ups | |
2 | +CREATE TABLE favorite_issue ( | |
3 | + id BIGINT AUTO_INCREMENT NOT NULL, | |
4 | + user_id BIGINT, | |
5 | + issue_id BIGINT, | |
6 | + CONSTRAINT pk_favorite_issue PRIMARY KEY (id), | |
7 | + CONSTRAINT uq_favorite_issue_user_id_issue_id_1 UNIQUE (user_id, issue_id), | |
8 | + CONSTRAINT fk_favorite_issue_user FOREIGN KEY (user_id) REFERENCES n4user (id) on DELETE CASCADE, | |
9 | + CONSTRAINT fk_favorite_issue_issue FOREIGN KEY (issue_id) REFERENCES issue (id) on DELETE CASCADE | |
10 | + ) | |
11 | + row_format=compressed, key_block_size=8 | |
12 | +; | |
13 | + | |
14 | +CREATE index ix_favorite_issue_user_1 ON favorite_issue (user_id); | |
15 | +CREATE index ix_favorite_issue_project_2 ON favorite_issue (issue_id); | |
16 | + | |
17 | +# --- !Downs | |
18 | +DROP TABLE favorite_issue; |
--- conf/messages
+++ conf/messages
... | ... | @@ -296,6 +296,8 @@ |
296 | 296 |
issue.event.sharer.deleted = {0} cancelled issue sharing with {1} |
297 | 297 |
issue.event.sharer.deleted.title = Cancelled |
298 | 298 |
issue.event.unassigned = {0} set assignee to unassigned |
299 |
+issue.favorite.added = Added as a favorite issue. You can see it at my issues page |
|
300 |
+issue.favorite.deleted = Removed from a favorite issue |
|
299 | 301 |
issue.is.empty = No issue found |
300 | 302 |
issue.label = Issue Label |
301 | 303 |
issue.list.all = All issues |
... | ... | @@ -304,6 +306,7 @@ |
304 | 306 |
issue.list.assignedToMe = Assigned |
305 | 307 |
issue.list.authoredByMe = Created |
306 | 308 |
issue.list.commentedByMe = Commented |
309 |
+issue.list.favorite = Favorite |
|
307 | 310 |
issue.list.mentionedOfMe = Mentioned |
308 | 311 |
issue.list.sharedWithMe = Shared |
309 | 312 |
issue.menu.new = New issue |
--- conf/messages.ko-KR
+++ conf/messages.ko-KR
... | ... | @@ -296,6 +296,8 @@ |
296 | 296 |
issue.event.sharer.deleted = {0} 님이 {1}님을 공유대상에서 제외했습니다. |
297 | 297 |
issue.event.sharer.deleted.title = 공유 취소 |
298 | 298 |
issue.event.unassigned = {0}님이 이 이슈의 담당자를 "없음"으로 설정하였습니다. |
299 |
+issue.favorite.added = 즐겨 찾는 이슈로 등록되었습니다. 내 이슈 페이지에서 확인 가능합니다. |
|
300 |
+issue.favorite.deleted = 즐겨 찾는 이슈에서 제외되었습니다. |
|
299 | 301 |
issue.is.empty = 등록된 이슈가 없습니다. |
300 | 302 |
issue.label = 이슈 라벨 |
301 | 303 |
issue.list.all = 전체 이슈 |
... | ... | @@ -304,6 +306,7 @@ |
304 | 306 |
issue.list.assignedToMe = 할당된 이슈 |
305 | 307 |
issue.list.authoredByMe = 작성한 이슈 |
306 | 308 |
issue.list.commentedByMe = 댓글 남긴 이슈 |
309 |
+issue.list.favorite = 즐겨 찾는 이슈 |
|
307 | 310 |
issue.list.mentionedOfMe = 나를 언급한 이슈 |
308 | 311 |
issue.list.sharedWithMe = 공유된 이슈 |
309 | 312 |
issue.menu.new = 새 이슈 |
--- conf/routes
+++ conf/routes
... | ... | @@ -53,6 +53,8 @@ |
53 | 53 |
POST /-_-api/v1/favoriteProjects/:projectId controllers.api.UserApi.toggleFoveriteProject(projectId:String) |
54 | 54 |
GET /-_-api/v1/favoriteOrganizations controllers.api.UserApi.getFoveriteOrganizations |
55 | 55 |
POST /-_-api/v1/favoriteOrganizations/:organizationId controllers.api.UserApi.toggleFoveriteOrganization(organizationId:String) |
56 |
+GET /-_-api/v1/favoriteIssues controllers.api.UserApi.getFoveriteIssues |
|
57 |
+POST /-_-api/v1/favoriteIssues/:issueId controllers.api.UserApi.toggleFoveriteIssue(issueId:String) |
|
56 | 58 |
GET /-_-api/v1/owners/:owner/projects/:projectName/issues/:number/assignableUsers controllers.api.IssueApi.findAssignableUsers(owner:String, projectName:String, number:Long, query: String ?= "") |
57 | 59 |
GET /-_-api/v1/owners/:owner/projects/:projectName/assignableUsers controllers.api.IssueApi.findAssignableUsersOfProject(owner:String, projectName:String, query: String ?= "") |
58 | 60 |
POST /-_-api/v1/owners/:owner/projects/:projectName/issues/:number/assignees controllers.api.IssueApi.updateAssginees(owner:String, projectName:String, number:Long) |
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?