usermenu: Recategorize user project menu
@7a5bfd580519eeb30cde587a3fba37e9e353b656
--- app/models/Organization.java
+++ app/models/Organization.java
... | ... | @@ -92,6 +92,16 @@ |
92 | 92 |
return (findRowCount != 0); |
93 | 93 |
} |
94 | 94 |
|
95 |
+ public List<Project> getSortedByProjectName() { |
|
96 |
+ this.projects.sort(new Comparator<Project>() { |
|
97 |
+ @Override |
|
98 |
+ public int compare(Project o1, Project o2) { |
|
99 |
+ return o1.name.compareToIgnoreCase(o2.name); |
|
100 |
+ } |
|
101 |
+ }); |
|
102 |
+ return this.projects; |
|
103 |
+ } |
|
104 |
+ |
|
95 | 105 |
public boolean isLastAdmin(User currentUser) { |
96 | 106 |
return OrganizationUser.isAdmin(this, currentUser) && getAdmins().size() == 1; |
97 | 107 |
} |
... | ... | @@ -155,6 +165,40 @@ |
155 | 165 |
.findList(); |
156 | 166 |
} |
157 | 167 |
|
168 |
+ public static List<Organization> findAllOrganizations() { |
|
169 |
+ List<Organization> projects = Organization.find.fetch("projects").where().orderBy("name asc, projects.name asc").findList(); |
|
170 |
+ projects.sort(new Comparator<Organization>() { |
|
171 |
+ @Override |
|
172 |
+ public int compare(Organization o1, Organization o2) { |
|
173 |
+ return o1.name.compareToIgnoreCase(o2.name); |
|
174 |
+ } |
|
175 |
+ }); |
|
176 |
+ return projects; |
|
177 |
+ } |
|
178 |
+ |
|
179 |
+ public static List<Organization> findAllOrganizations(String loginId) { |
|
180 |
+ User user = User.findByLoginId(loginId); |
|
181 |
+ |
|
182 |
+ Set<String> owners = new TreeSet<>(new Comparator<String>() { |
|
183 |
+ @Override |
|
184 |
+ public int compare(String o1, String o2) { |
|
185 |
+ return o1.compareToIgnoreCase(o2); |
|
186 |
+ } |
|
187 |
+ }); |
|
188 |
+ for (FavoriteProject fp : user.favoriteProjects) { |
|
189 |
+ owners.add(fp.owner); |
|
190 |
+ } |
|
191 |
+ |
|
192 |
+ List<Organization> orgs = new ArrayList<>(); |
|
193 |
+ for (String owner: owners) { |
|
194 |
+ Organization org = Organization.findByName(owner); |
|
195 |
+ if (org != null) { |
|
196 |
+ orgs.add(org); |
|
197 |
+ } |
|
198 |
+ } |
|
199 |
+ return orgs; |
|
200 |
+ } |
|
201 |
+ |
|
158 | 202 |
/** |
159 | 203 |
* As resource. |
160 | 204 |
* |
--- app/models/Project.java
+++ app/models/Project.java
... | ... | @@ -176,6 +176,10 @@ |
176 | 176 |
} |
177 | 177 |
} |
178 | 178 |
|
179 |
+ public static List<Project> findByOwner(String loginId) { |
|
180 |
+ return find.where().ieq("owner", decodeUrlString(loginId)).orderBy("name asc").findList(); |
|
181 |
+ } |
|
182 |
+ |
|
179 | 183 |
public Set<User> findAuthors() { |
180 | 184 |
Set<User> allAuthors = new LinkedHashSet<>(); |
181 | 185 |
allAuthors.addAll(getIssueUsers()); |
--- app/models/RecentProject.java
+++ app/models/RecentProject.java
... | ... | @@ -15,7 +15,7 @@ |
15 | 15 |
@Table(uniqueConstraints = @UniqueConstraint(columnNames = {"user_id", "project_id"})) |
16 | 16 |
public class RecentProject extends Model { |
17 | 17 |
private static final long serialVersionUID = 7306890271871188281L; |
18 |
- public static int MAX_RECENT_LIST_PER_USER = 20; |
|
18 |
+ public static int MAX_RECENT_LIST_PER_USER = 30; |
|
19 | 19 |
|
20 | 20 |
public static Finder<Long, RecentProject> find = new Finder<>(Long.class, RecentProject.class); |
21 | 21 |
|
--- app/models/User.java
+++ app/models/User.java
... | ... | @@ -249,6 +249,10 @@ |
249 | 249 |
return Project.findProjectsByMemberWithFilter(id, orderString); |
250 | 250 |
} |
251 | 251 |
|
252 |
+ public List<Project> ownProjects() { |
|
253 |
+ return Project.findByOwner(loginId); |
|
254 |
+ } |
|
255 |
+ |
|
252 | 256 |
/** |
253 | 257 |
* Create a user and set creation date |
254 | 258 |
* |
... | ... | @@ -899,10 +903,24 @@ |
899 | 903 |
} |
900 | 904 |
|
901 | 905 |
public List<Project> getFavoriteProjects() { |
906 |
+ final User user = this; |
|
907 |
+ favoriteProjects.sort(new Comparator<FavoriteProject>() { |
|
908 |
+ @Override |
|
909 |
+ public int compare(FavoriteProject o1, FavoriteProject o2) { |
|
910 |
+ if (o1.owner.equals(user.loginId) || o2.owner.equals(user.loginId)) { |
|
911 |
+ return Integer.MIN_VALUE; |
|
912 |
+ } |
|
913 |
+ if (o1.owner.equals(o2.owner)) { |
|
914 |
+ return o1.projectName.compareToIgnoreCase(o2.projectName); |
|
915 |
+ } |
|
916 |
+ return o1.owner.compareToIgnoreCase(o2.owner); |
|
917 |
+ } |
|
918 |
+ }); |
|
919 |
+ |
|
902 | 920 |
List<Project> projects = new ArrayList<>(); |
903 | 921 |
for (FavoriteProject favoriteProject : this.favoriteProjects) { |
904 | 922 |
favoriteProject.project.refresh(); |
905 |
- projects.add(0, favoriteProject.project); |
|
923 |
+ projects.add(favoriteProject.project); |
|
906 | 924 |
} |
907 | 925 |
|
908 | 926 |
return projects; |
+++ app/views/index/allOrganizationList.scala.html
... | ... | @@ -0,0 +1,32 @@ |
1 | +@** | |
2 | +* Yona, 21st Century Project Hosting SW | |
3 | +* | |
4 | +* Copyright Yona & Yobi Authors & NAVER Corp. | |
5 | +* https://yona.io | |
6 | +**@ | |
7 | +@(currentUser:User) | |
8 | +@import utils.TemplateHelper._ | |
9 | + | |
10 | +@displayOrganizations(title:String, organizations:List[Organization], favoredOrganizations:List[Organization], isActive:Boolean = false) = { | |
11 | + @if(organizations.isEmpty && favoredOrganizations.isEmpty) { | |
12 | + <div id="@title" class="no-result tab-pane user-ul @if(isActive) {active}">@Messages("title.no.results")</div> | |
13 | + } else { | |
14 | + <ul class="tab-pane user-ul @if(isActive) {active}" id="@title"> | |
15 | + @for(organization <- organizations) { | |
16 | + @if(favoredOrganizations.last.equals(organization)){ | |
17 | + @myOrganizationList_partial(organization, true, true) | |
18 | + } else { | |
19 | + @myOrganizationList_partial(organization, true) | |
20 | + } | |
21 | + } | |
22 | + </ul> | |
23 | + } | |
24 | +} | |
25 | + | |
26 | + <div class="search-result"> | |
27 | + <div class="group"> | |
28 | + <input class="search-input org-search" type="text" autocomplete="off" placeholder="@Messages("title.type.name")"> | |
29 | + <span class="bar"></span> | |
30 | + </div> | |
31 | + @displayOrganizations("organizations", Organization.findOrganizationsByUserLoginId(UserApp.currentUser.loginId), currentUser.getFavoriteOrganizations) | |
32 | + </div> |
+++ app/views/index/allOrganizationList_partial.scala.html
... | ... | @@ -0,0 +1,36 @@ |
1 | +@** | |
2 | +* Yona, 21st Century Project Hosting SW | |
3 | +* | |
4 | +* Copyright Yona & Yobi Authors & NAVER Corp. | |
5 | +* https://yona.io | |
6 | +**@ | |
7 | +@(organization: Organization, favored:Boolean, isLast:Boolean = false) | |
8 | +@import utils.TemplateHelper._ | |
9 | + | |
10 | +@isAllowShowCount() = @{ | |
11 | + UserApp.currentUser().isSiteManager || UserApp.currentUser().isAdminOf(organization) | |
12 | +} | |
13 | + | |
14 | +@defining(UserApp.currentUser().getFavoriteProjects){ favoriteProjects => | |
15 | +<li class="@if(isLast){favored} org-li"> | |
16 | + <div class="org-list project-flex-container all-orgs"> | |
17 | + <div class="project-item project-item-container"> | |
18 | + <div class="flex-item site-logo"> | |
19 | + <i class="project-avatar">@if(hasOrganizationLogo(organization)){<img class="logo" src="@urlToOrganizationLogo(organization)">}else{<span class="dummy-25px"> </span>}</i> | |
20 | + </div> | |
21 | + <div class="projectName-owner all-org-names flex-item"> | |
22 | + <div class="project-name org-name flex-item"><a href="@routes.OrganizationApp.organization(organization.name)" target="_blank">@organization.name</a></div> | |
23 | + <div class="project-owner flex-item">@if(isAllowShowCount){@organization.projects.size()}</div> | |
24 | + </div> | |
25 | + </div> | |
26 | + <div class="star-org flex-item" data-organization-id="@organization.id"> | |
27 | + <i class="star @if(favored){starred} material-icons">star</i> | |
28 | + </div> | |
29 | + </div> | |
30 | + <ul class="project-ul"> | |
31 | + @for(project <- organization.projects){ | |
32 | + @allProjectList_partial(project, favoriteProjects.contains(project)) | |
33 | + } | |
34 | + </ul> | |
35 | +</li> | |
36 | +} |
+++ app/views/index/allProjectList.scala.html
... | ... | @@ -0,0 +1,28 @@ |
1 | +@** | |
2 | +* Yona, 21st Century Project Hosting SW | |
3 | +* | |
4 | +* Copyright Yona & Yobi Authors & NAVER Corp. | |
5 | +* https://yona.io | |
6 | +**@ | |
7 | +@(currentUser:User) | |
8 | +@import utils.TemplateHelper._ | |
9 | + | |
10 | +@displayOrganizations(title:String, organizations:List[Organization], isActive:Boolean = false) = { | |
11 | +@if(organizations.isEmpty) { | |
12 | + <div id="@title" class="no-result tab-pane user-ul @if(isActive) {active}">@Messages("title.no.results")</div> | |
13 | +} else { | |
14 | + <ul class="tab-pane user-ul @if(isActive) {active}" id="@title"> | |
15 | + @for(organization <- organizations) { | |
16 | + @allOrganizationList_partial(organization, UserApp.currentUser().getFavoriteOrganizations.contains(organization)) | |
17 | + } | |
18 | + </ul> | |
19 | +} | |
20 | +} | |
21 | + | |
22 | +<div class="search-result"> | |
23 | + <div class="group"> | |
24 | + <input class="search-input org-search" type="text" autocomplete="off" placeholder="@Messages("title.type.name")"> | |
25 | + <span class="bar"></span> | |
26 | + </div> | |
27 | + @displayOrganizations("organizations", Organization.findAllOrganizations()) | |
28 | +</div> |
+++ app/views/index/allProjectList_partial.scala.html
... | ... | @@ -0,0 +1,29 @@ |
1 | +@** | |
2 | +* Yona, 21st Century Project Hosting SW | |
3 | +* | |
4 | +* Copyright Yona & Yobi Authors & NAVER Corp. | |
5 | +* https://yona.io | |
6 | +**@ | |
7 | +@import utils.AccessControl | |
8 | +@import models.enumeration.Operation | |
9 | +@(project:Project, favored:Boolean, isLast:Boolean = false) | |
10 | +@import utils.TemplateHelper._ | |
11 | + | |
12 | + | |
13 | +@if(AccessControl.isAllowed(UserApp.currentUser(), project.asResource(), Operation.READ)){ | |
14 | +<li class="user-li @if(isLast){favored} @if(favored){ show-always } else { hide }" data-location="@routes.ProjectApp.goConventionMenu(project.owner, project.name)"> | |
15 | + <div class="project-list project-flex-container"> | |
16 | + <div class="project-item project-item-container"> | |
17 | + <div class="flex-item site-logo all-project-names"> | |
18 | + <i class="project-avatar">@if(hasProjectLogo(project)){<img class="logo" src="@urlToProjectLogo(project)">}else{<span class="dummy-25px"> </span>}</i> | |
19 | + </div> | |
20 | + <div class="projectName-owner flex-item"> | |
21 | + <div class="project-name flex-item">@project.name @if(project.isPrivate){<i class="yobicon-lock yobicon-small"></i>}</div> | |
22 | + </div> | |
23 | + </div> | |
24 | + <div class="star-project flex-item" data-project-id="@project.id"> | |
25 | + <i class="star @if(favored){starred} material-icons">star</i> | |
26 | + </div> | |
27 | + </div> | |
28 | +</li> | |
29 | +} |
+++ app/views/index/displayProjects.scala.html
... | ... | @@ -0,0 +1,17 @@ |
1 | +@** | |
2 | +* Yona, 21st Century Project Hosting SW | |
3 | +* | |
4 | +* Copyright Yona & Yobi Authors & NAVER Corp. | |
5 | +* https://yona.io | |
6 | +**@ | |
7 | +@(title:String, projects:List[Project], isActive:Boolean = false) | |
8 | + | |
9 | +@if(projects.isEmpty) { | |
10 | + <div id="@title" class="no-result tab-pane user-ul @if(isActive){active}">@Messages("title.no.results")</div> | |
11 | +} else { | |
12 | + <ul class="tab-pane user-ul @if(isActive){active}" id="@title"> | |
13 | + @for(project <- projects){ | |
14 | + @views.html.index.myProjectList_partial(project, false) | |
15 | + } | |
16 | + </ul> | |
17 | +} |
--- app/views/index/displayProjectsWithFavored.scala.html
... | ... | @@ -1,26 +0,0 @@ |
1 | -@** | |
2 | -* Yona, 21st Century Project Hosting SW | |
3 | -* | |
4 | -* Copyright Yona & Yobi Authors & NAVER Corp. | |
5 | -* https://yona.io | |
6 | -**@ | |
7 | -@(title:String, projects:List[Project], favoredProjects:List[Project], isActive:Boolean = false) | |
8 | - | |
9 | -@if(projects.isEmpty && favoredProjects.isEmpty) { | |
10 | - <div id="@title" class="no-result tab-pane user-ul @if(isActive){active}">@Messages("title.no.results")</div> | |
11 | -} else { | |
12 | - <ul class="tab-pane user-ul @if(isActive){active}" id="@title"> | |
13 | - @for(project <- favoredProjects){ | |
14 | - @if(favoredProjects.last.equals(project)){ | |
15 | - @views.html.index.myProjectList_partial(project, true, true) | |
16 | - } else { | |
17 | - @views.html.index.myProjectList_partial(project, true) | |
18 | - } | |
19 | - } | |
20 | - @for(project <- projects){ | |
21 | - @if(!favoredProjects.contains(project)){ | |
22 | - @views.html.index.myProjectList_partial(project, false) | |
23 | - } | |
24 | - } | |
25 | - </ul> | |
26 | -} |
--- app/views/index/myOrganizationList.scala.html
+++ app/views/index/myOrganizationList.scala.html
... | ... | @@ -1,7 +1,7 @@ |
1 | 1 |
@** |
2 | 2 |
* Yona, 21st Century Project Hosting SW |
3 | 3 |
* |
4 |
-* Copyright Yona & Yobi Authors & NAVER Corp. |
|
4 |
+* Copyright Yona & Yobi Authors & NAVER Corp. & NAVER LABS Corp. |
|
5 | 5 |
* https://yona.io |
6 | 6 |
**@ |
7 | 7 |
@(currentUser:User) |
... | ... | @@ -12,6 +12,27 @@ |
12 | 12 |
<div id="@title" class="no-result tab-pane user-ul @if(isActive) {active}">@Messages("title.no.results")</div> |
13 | 13 |
} else { |
14 | 14 |
<ul class="tab-pane user-ul @if(isActive) {active}" id="@title"> |
15 |
+ @defining(UserApp.currentUser().ownProjects){ ownProjects => |
|
16 |
+ <li class="org-li"> |
|
17 |
+ <div class="org-list project-flex-container all-orgs"> |
|
18 |
+ <div class="project-item project-item-container"> |
|
19 |
+ <div class="flex-item site-logo"> |
|
20 |
+ <i class="project-avatar"></i> |
|
21 |
+ </div> |
|
22 |
+ <div class="projectName-owner all-org-names flex-item"> |
|
23 |
+ <div class="project-name org-name flex-item">@UserApp.currentUser().loginId</div> |
|
24 |
+ <div class="project-owner flex-item">@ownProjects.size()</div> |
|
25 |
+ </div> |
|
26 |
+ </div> |
|
27 |
+ <div class="star-org flex-item"></div> |
|
28 |
+ </div> |
|
29 |
+ <ul class="project-ul"> |
|
30 |
+ @for(project <- ownProjects){ |
|
31 |
+ @allProjectList_partial(project, FavoriteProject.findByProjectId(UserApp.currentUser().id, project.id) != null) |
|
32 |
+ } |
|
33 |
+ </ul> |
|
34 |
+ </li> |
|
35 |
+ } |
|
15 | 36 |
@for(organization <- favoredOrganizations) { |
16 | 37 |
@if(favoredOrganizations.last.equals(organization)){ |
17 | 38 |
@myOrganizationList_partial(organization, true, true) |
... | ... | @@ -28,10 +49,11 @@ |
28 | 49 |
} |
29 | 50 |
} |
30 | 51 |
|
52 |
+ |
|
31 | 53 |
<div class="search-result"> |
32 | 54 |
<div class="group"> |
33 | 55 |
<input class="search-input org-search" type="text" autocomplete="off" placeholder="@Messages("title.type.name")"> |
34 | 56 |
<span class="bar"></span> |
35 | 57 |
</div> |
36 |
- @displayOrganizations("organizations", Organization.findOrganizationsByUserLoginId(UserApp.currentUser.loginId), currentUser.getFavoriteOrganizations) |
|
58 |
+ @displayOrganizations("organizations", Organization.findAllOrganizations(UserApp.currentUser.loginId), currentUser.getFavoriteOrganizations) |
|
37 | 59 |
</div> |
--- app/views/index/myOrganizationList_partial.scala.html
+++ app/views/index/myOrganizationList_partial.scala.html
... | ... | @@ -7,19 +7,30 @@ |
7 | 7 |
@(organization: Organization, favored:Boolean, isLast:Boolean = false) |
8 | 8 |
@import utils.TemplateHelper._ |
9 | 9 |
|
10 |
-<li class="user-li @if(isLast){favored}" data-location="@routes.OrganizationApp.organization(organization.name)"> |
|
11 |
- <div class="org-list project-flex-container"> |
|
10 |
+@isAllowShowCount() = @{ |
|
11 |
+ UserApp.currentUser().isSiteManager || UserApp.currentUser().isAdminOf(organization) |
|
12 |
+} |
|
13 |
+ |
|
14 |
+@defining(UserApp.currentUser().getFavoriteProjects){ favoriteProjects => |
|
15 |
+<li class="org-li @if(isLast){favored}"> |
|
16 |
+ <div class="org-list project-flex-container all-orgs"> |
|
12 | 17 |
<div class="project-item project-item-container"> |
13 | 18 |
<div class="flex-item site-logo"> |
14 | 19 |
<i class="project-avatar">@if(hasOrganizationLogo(organization)){<img class="logo" src="@urlToOrganizationLogo(organization)">}else{<span class="dummy-25px"> </span>}</i> |
15 | 20 |
</div> |
16 |
- <div class="projectName-owner flex-item"> |
|
17 |
- <div class="project-name org-name flex-item">@organization.name</div> |
|
18 |
- <div class="project-owner flex-item"></div> |
|
21 |
+ <div class="projectName-owner all-org-names flex-item"> |
|
22 |
+ <div class="project-name org-name flex-item"><a href="@routes.OrganizationApp.organization(organization.name)" target="_blank">@organization.name</a></div> |
|
23 |
+ <div class="project-owner flex-item">@if(isAllowShowCount){@organization.projects.size()}</div> |
|
19 | 24 |
</div> |
20 | 25 |
</div> |
21 | 26 |
<div class="star-org flex-item" data-organization-id="@organization.id"> |
22 | 27 |
<i class="star @if(favored){starred} material-icons">star</i> |
23 | 28 |
</div> |
24 | 29 |
</div> |
30 |
+ <ul class="project-ul"> |
|
31 |
+ @for(project <- organization.getSortedByProjectName){ |
|
32 |
+ @allProjectList_partial(project, favoriteProjects.contains(project)) |
|
33 |
+ } |
|
34 |
+ </ul> |
|
25 | 35 |
</li> |
36 |
+} |
+++ app/views/index/myOwnProjectList_partial.scala.html
... | ... | @@ -0,0 +1,31 @@ |
1 | +@** | |
2 | +* Yona, 21st Century Project Hosting SW | |
3 | +* | |
4 | +* Copyright Yona & Yobi Authors & NAVER Corp. | |
5 | +* https://yona.io | |
6 | +**@ | |
7 | +@(organization: Organization, favored:Boolean, isLast:Boolean = false) | |
8 | +@import utils.TemplateHelper._ | |
9 | + | |
10 | +@defining(UserApp.currentUser().ownProjects){ ownProjects => | |
11 | +<li class="org-li @if(isLast){favored}"> | |
12 | + <div class="org-list project-flex-container all-orgs"> | |
13 | + <div class="project-item project-item-container"> | |
14 | + <div class="flex-item site-logo"> | |
15 | + <i class="project-avatar">@if(hasOrganizationLogo(organization)){<img class="logo" src="@urlToOrganizationLogo(organization)">}else{<span class="dummy-25px"> </span>}</i> | |
16 | + </div> | |
17 | + <div class="projectName-owner all-org-names flex-item"> | |
18 | + <div class="project-name org-name flex-item">@UserApp.currentUser().loginId</div> | |
19 | + <div class="project-owner flex-item">@ownProjects.size()</div> | |
20 | + </div> | |
21 | + </div> | |
22 | + <div class="star-org flex-item"> | |
23 | + </div> | |
24 | + </div> | |
25 | + <ul class="project-ul"> | |
26 | + @for(project <- ownProjects){ | |
27 | + @allProjectList_partial(project, UserApp.currentUser().favoriteProjects.contains(project)) | |
28 | + } | |
29 | + </ul> | |
30 | +</li> | |
31 | +} |
--- app/views/index/myProjectList.scala.html
+++ app/views/index/myProjectList.scala.html
... | ... | @@ -20,7 +20,7 @@ |
20 | 20 |
} |
21 | 21 |
<div> |
22 | 22 |
<div class="search-result"> |
23 |
- <div class="tab-pane myproject-list-wrap active" > |
|
23 |
+ <div class="tab-pane myproject-list-wrap" > |
|
24 | 24 |
<div class="group"> |
25 | 25 |
<input class="search-input project-search" type="text" id="query" autocomplete="off" placeholder="@Messages("title.type.name")"> |
26 | 26 |
<span class="bar"></span> |
... | ... | @@ -50,7 +50,7 @@ |
50 | 50 |
</ul> |
51 | 51 |
</div> |
52 | 52 |
<div class="tab-content"> |
53 |
- @views.html.index.displayProjectsWithFavored("recentlyVisited", currentUser.getVisitedProjects, currentUser.getFavoriteProjects, true) |
|
53 |
+ @views.html.index.displayProjects("recentlyVisited", currentUser.getVisitedProjects, true) |
|
54 | 54 |
@displayProjects("watching", currentUser.getWatchingProjects("name ASC")) |
55 | 55 |
@displayProjects("createdByMe", Project.findProjectsCreatedByUser(currentUser.loginId, "createdDate desc")) |
56 | 56 |
@displayProjects("joinmember", Project.findProjectsJustMemberAndNotOwner(currentUser, "name ASC")) |
--- conf/messages
+++ conf/messages
... | ... | @@ -927,6 +927,7 @@ |
927 | 927 |
title.editIssue = Edit issue |
928 | 928 |
title.editMilestone = Edit milestone |
929 | 929 |
title.editPullRequest = Edit pull request |
930 |
+title.favorite = Favorite |
|
930 | 931 |
title.features = Key features |
931 | 932 |
title.forgotpassword = Password forgotten? |
932 | 933 |
title.gettingStarted = Getting your new project started with <strong>{0}</strong> |
--- conf/messages.ko-KR
+++ conf/messages.ko-KR
... | ... | @@ -928,6 +928,7 @@ |
928 | 928 |
title.editIssue = 이슈 수정 |
929 | 929 |
title.editMilestone = 마일스톤 수정 |
930 | 930 |
title.editPullRequest = 코드 보내기 수정 |
931 |
+title.favorite = 즐겨찾기 |
|
931 | 932 |
title.features = 주요 기능 소개 |
932 | 933 |
title.forgotpassword = 비밀번호를 잊어버리셨나요? |
933 | 934 |
title.gettingStarted = <strong>{0}</strong> 와 함께 새로운 프로젝트를 시작해 보세요. |
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?