
apply markup to user info
@9fcae2ef519e6eafe67c6b4f4f12b27698e550f6
--- app/models/Project.java
+++ app/models/Project.java
... | ... | @@ -1,5 +1,7 @@ |
1 | 1 |
package models; |
2 | 2 |
|
3 |
+import java.io.IOException; |
|
4 |
+import java.util.Date; |
|
3 | 5 |
import java.util.Iterator; |
4 | 6 |
import java.util.List; |
5 | 7 |
|
... | ... | @@ -8,160 +10,203 @@ |
8 | 10 |
import javax.persistence.Id; |
9 | 11 |
import javax.persistence.OneToMany; |
10 | 12 |
import javax.persistence.OneToOne; |
13 |
+import javax.servlet.ServletException; |
|
11 | 14 |
|
12 | 15 |
import models.enumeration.RoleType; |
13 | 16 |
import models.task.TaskBoard; |
17 |
+ |
|
18 |
+import org.eclipse.jgit.api.errors.GitAPIException; |
|
19 |
+import org.eclipse.jgit.api.errors.NoHeadException; |
|
20 |
+import org.joda.time.Duration; |
|
21 |
+ |
|
22 |
+import play.Logger; |
|
14 | 23 |
import play.data.validation.Constraints; |
15 | 24 |
import play.db.ebean.Model; |
25 |
+import playRepository.Commit; |
|
26 |
+import playRepository.GitRepository; |
|
16 | 27 |
import playRepository.RepositoryService; |
28 |
+import utils.JodaDateUtil; |
|
17 | 29 |
|
18 | 30 |
import com.avaje.ebean.Page; |
19 | 31 |
|
20 | 32 |
/** |
21 |
- * |
|
33 |
+ * |
|
22 | 34 |
* @author "Hwi Ahn" |
23 | 35 |
*/ |
24 | 36 |
|
25 | 37 |
@Entity |
26 | 38 |
public class Project extends Model { |
27 |
- private static final long serialVersionUID = 1L; |
|
28 |
- public static Finder<Long, Project> find = new Finder<Long, Project>( |
|
29 |
- Long.class, Project.class); |
|
39 |
+ private static final long serialVersionUID = 1L; |
|
40 |
+ public static Finder<Long, Project> find = new Finder<Long, Project>( |
|
41 |
+ Long.class, Project.class); |
|
30 | 42 |
|
31 |
- @Id |
|
32 |
- public Long id; |
|
43 |
+ @Id |
|
44 |
+ public Long id; |
|
33 | 45 |
|
34 |
- @Constraints.Required |
|
35 |
- @Constraints.Pattern("^[a-zA-Z0-9_]*$") |
|
36 |
- public String name; |
|
46 |
+ @Constraints.Required |
|
47 |
+ @Constraints.Pattern("^[a-zA-Z0-9_]*$") |
|
48 |
+ public String name; |
|
37 | 49 |
|
38 |
- public String overview; |
|
39 |
- public String vcs; |
|
40 |
- public String siteurl; |
|
41 |
- public String logoPath; |
|
42 |
- public String owner; |
|
50 |
+ public String overview; |
|
51 |
+ public String vcs; |
|
52 |
+ public String siteurl; |
|
53 |
+ public String logoPath; |
|
54 |
+ public String owner; |
|
43 | 55 |
|
44 |
- public boolean share_option; |
|
45 |
- public boolean isAuthorEditable; |
|
46 |
- |
|
47 |
- @OneToMany(mappedBy = "project", cascade = CascadeType.ALL) |
|
48 |
- public List<Issue> issues; |
|
49 |
- |
|
50 |
- @OneToMany(mappedBy = "project", cascade = CascadeType.ALL) |
|
51 |
- public List<ProjectUser> projectUser; |
|
52 |
- |
|
53 |
- @OneToMany(mappedBy = "project", cascade = CascadeType.ALL) |
|
54 |
- public List<Post> posts; |
|
55 |
- |
|
56 |
- @OneToMany(mappedBy = "project", cascade = CascadeType.ALL) |
|
57 |
- public List<Milestone> milestones; |
|
58 |
- |
|
59 |
- @OneToOne(mappedBy = "project", cascade = CascadeType.ALL) |
|
60 |
- public TaskBoard taskBoard; |
|
56 |
+ public boolean share_option; |
|
57 |
+ public boolean isAuthorEditable; |
|
61 | 58 |
|
62 |
- public static Long create(Project newProject) { |
|
63 |
- newProject.siteurl = "http://localhost:9000/" + newProject.name; |
|
64 |
- newProject.save(); |
|
65 |
- ProjectUser.assignRole(User.SITE_MANAGER_ID, newProject.id, RoleType.SITEMANAGER); |
|
66 |
- return newProject.id; |
|
67 |
- } |
|
59 |
+ public Date date; |
|
68 | 60 |
|
69 |
- public static void delete(Long id) { |
|
70 |
- Project.find.byId(id).delete(); |
|
71 |
- } |
|
61 |
+ @OneToMany(mappedBy = "project", cascade = CascadeType.ALL) |
|
62 |
+ public List<Issue> issues; |
|
72 | 63 |
|
73 |
- public static Page<Project> findByName(String name, int pageSize, int pageNum) { |
|
74 |
- return find.where().ilike("name", "%" + name + "%").findPagingList(pageSize).getPage(pageNum); |
|
75 |
- } |
|
76 |
- |
|
77 |
- public static Project findByNameAndOwner(String userName, String projectName) { |
|
78 |
- return find.where().eq("name", projectName).eq("owner", userName).findUnique(); |
|
79 |
- } |
|
80 |
- |
|
81 |
- /** |
|
82 |
- * 해당 프로젝트가 존재하는지 여부를 검사합니다. 해당 파라미터에 대하여 프로젝트가 존재하면 true, 존재하지 않으면 false를 반환합니다. |
|
83 |
- * |
|
84 |
- * @param userName |
|
85 |
- * @param projectName |
|
86 |
- * @return |
|
87 |
- */ |
|
88 |
- public static boolean isProject(String userName, String projectName) { |
|
89 |
- int findRowCount = find.where().eq("name", projectName).eq("owner", userName).findRowCount(); |
|
90 |
- return (findRowCount != 0) ? true : false; |
|
91 |
- } |
|
92 |
- |
|
64 |
+ @OneToMany(mappedBy = "project", cascade = CascadeType.ALL) |
|
65 |
+ public List<ProjectUser> projectUser; |
|
93 | 66 |
|
94 |
- /** |
|
95 |
- * 프로젝트 이름을 해당 이름(projectName)으로 변경이 가능한지 검사합니다. |
|
96 |
- * |
|
97 |
- * @param id |
|
98 |
- * @param userName |
|
99 |
- * @param projectName |
|
100 |
- * @return |
|
101 |
- */ |
|
102 |
- public static boolean projectNameChangeable(Long id, String userName, String projectName) { |
|
103 |
- int findRowCount = find.where().eq("name", projectName).eq("owner", userName).ne("id", id).findRowCount(); |
|
104 |
- return (findRowCount == 0) ? true : false; |
|
105 |
- } |
|
106 |
- |
|
107 |
- /** |
|
108 |
- * 해당 유저가 속해있는 프로젝트들 중에서 해당 유저가 유일한 Manager인 프로젝트가 있는지 검사하고, |
|
109 |
- * 있다면 그 프로젝트들의 리스트를 반환합니다. |
|
110 |
- * |
|
111 |
- * @param userId |
|
112 |
- * @return |
|
113 |
- */ |
|
114 |
- public static List<Project> isOnlyManager(Long userId) { |
|
115 |
- List<Project> projects = find |
|
116 |
- .select("id") |
|
117 |
- .select("name") |
|
118 |
- .where() |
|
119 |
- .eq("projectUser.user.id", userId) |
|
120 |
- .eq("projectUser.role.id", RoleType.MANAGER.roleType()) |
|
121 |
- .findList(); |
|
122 |
- Iterator<Project> iterator = projects.iterator(); |
|
123 |
- while(iterator.hasNext()){ |
|
124 |
- Project project = iterator.next(); |
|
125 |
- if(ProjectUser.checkOneMangerPerOneProject(project.id)) { |
|
126 |
- iterator.remove(); |
|
127 |
- } |
|
128 |
- } |
|
129 |
- |
|
130 |
- return projects; |
|
131 |
- } |
|
132 |
- |
|
133 |
- /** |
|
134 |
- * 해당 유저가 속해있는 프로젝트들의 리스트를 제공합니다. |
|
135 |
- * |
|
136 |
- * @param ownerId |
|
137 |
- * @return |
|
138 |
- */ |
|
139 |
- public static List<Project> findProjectsByMember(Long userId) { |
|
140 |
- return find.where() |
|
141 |
- .eq("projectUser.user.id", userId).findList(); |
|
142 |
- } |
|
67 |
+ @OneToMany(mappedBy = "project", cascade = CascadeType.ALL) |
|
68 |
+ public List<Post> posts; |
|
143 | 69 |
|
144 |
- public static Page<Project> projects(int pageNum) { |
|
145 |
- return find.findPagingList(25).getPage(pageNum); |
|
146 |
- } |
|
70 |
+ @OneToMany(mappedBy = "project", cascade = CascadeType.ALL) |
|
71 |
+ public List<Milestone> milestones; |
|
147 | 72 |
|
148 |
- public static int countByState(String state) { |
|
149 |
- if (state == "all") { |
|
150 |
- return find.findRowCount(); |
|
151 |
- } else if (state == "public") { |
|
152 |
- return find.where().eq("share_option", true).findRowCount(); |
|
153 |
- } else if (state == "private") { |
|
154 |
- return find.where().eq("share_option", false).findRowCount(); |
|
155 |
- } else { |
|
156 |
- return 0; |
|
157 |
- } |
|
158 |
- } |
|
73 |
+ @OneToOne(mappedBy = "project", cascade = CascadeType.ALL) |
|
74 |
+ public TaskBoard taskBoard; |
|
159 | 75 |
|
160 |
- public String readme() { |
|
161 |
- try { |
|
162 |
- return new String(RepositoryService.getRepository(this).getRawFile("README.md")); |
|
163 |
- } catch (Exception e) { |
|
164 |
- return null; |
|
165 |
- } |
|
166 |
- } |
|
76 |
+ public static Long create(Project newProject) { |
|
77 |
+ newProject.siteurl = "http://localhost:9000/" + newProject.name; |
|
78 |
+ newProject.save(); |
|
79 |
+ ProjectUser.assignRole(User.SITE_MANAGER_ID, newProject.id, |
|
80 |
+ RoleType.SITEMANAGER); |
|
81 |
+ return newProject.id; |
|
82 |
+ } |
|
83 |
+ |
|
84 |
+ public static void delete(Long id) { |
|
85 |
+ Project.find.byId(id).delete(); |
|
86 |
+ } |
|
87 |
+ |
|
88 |
+ public static Page<Project> findByName(String name, int pageSize, |
|
89 |
+ int pageNum) { |
|
90 |
+ return find.where().ilike("name", "%" + name + "%") |
|
91 |
+ .findPagingList(pageSize).getPage(pageNum); |
|
92 |
+ } |
|
93 |
+ |
|
94 |
+ public static Project findByNameAndOwner(String userName, String projectName) { |
|
95 |
+ return find.where().eq("name", projectName).eq("owner", userName) |
|
96 |
+ .findUnique(); |
|
97 |
+ } |
|
98 |
+ |
|
99 |
+ /** |
|
100 |
+ * 해당 프로젝트가 존재하는지 여부를 검사합니다. 해당 파라미터에 대하여 프로젝트가 존재하면 true, 존재하지 않으면 false를 |
|
101 |
+ * 반환합니다. |
|
102 |
+ * |
|
103 |
+ * @param userName |
|
104 |
+ * @param projectName |
|
105 |
+ * @return |
|
106 |
+ */ |
|
107 |
+ public static boolean isProject(String userName, String projectName) { |
|
108 |
+ int findRowCount = find.where().eq("name", projectName) |
|
109 |
+ .eq("owner", userName).findRowCount(); |
|
110 |
+ return (findRowCount != 0) ? true : false; |
|
111 |
+ } |
|
112 |
+ |
|
113 |
+ /** |
|
114 |
+ * 프로젝트 이름을 해당 이름(projectName)으로 변경이 가능한지 검사합니다. |
|
115 |
+ * |
|
116 |
+ * @param id |
|
117 |
+ * @param userName |
|
118 |
+ * @param projectName |
|
119 |
+ * @return |
|
120 |
+ */ |
|
121 |
+ public static boolean projectNameChangeable(Long id, String userName, |
|
122 |
+ String projectName) { |
|
123 |
+ int findRowCount = find.where().eq("name", projectName) |
|
124 |
+ .eq("owner", userName).ne("id", id).findRowCount(); |
|
125 |
+ return (findRowCount == 0) ? true : false; |
|
126 |
+ } |
|
127 |
+ |
|
128 |
+ /** |
|
129 |
+ * 해당 유저가 속해있는 프로젝트들 중에서 해당 유저가 유일한 Manager인 프로젝트가 있는지 검사하고, 있다면 그 프로젝트들의 |
|
130 |
+ * 리스트를 반환합니다. |
|
131 |
+ * |
|
132 |
+ * @param userId |
|
133 |
+ * @return |
|
134 |
+ */ |
|
135 |
+ public static List<Project> isOnlyManager(Long userId) { |
|
136 |
+ List<Project> projects = find.select("id").select("name").where() |
|
137 |
+ .eq("projectUser.user.id", userId) |
|
138 |
+ .eq("projectUser.role.id", RoleType.MANAGER.roleType()) |
|
139 |
+ .findList(); |
|
140 |
+ Iterator<Project> iterator = projects.iterator(); |
|
141 |
+ while (iterator.hasNext()) { |
|
142 |
+ Project project = iterator.next(); |
|
143 |
+ if (ProjectUser.checkOneMangerPerOneProject(project.id)) { |
|
144 |
+ iterator.remove(); |
|
145 |
+ } |
|
146 |
+ } |
|
147 |
+ |
|
148 |
+ return projects; |
|
149 |
+ } |
|
150 |
+ |
|
151 |
+ /** |
|
152 |
+ * 해당 유저가 속해있는 프로젝트들의 리스트를 제공합니다. |
|
153 |
+ * |
|
154 |
+ * @param ownerId |
|
155 |
+ * @return |
|
156 |
+ */ |
|
157 |
+ public static List<Project> findProjectsByMember(Long userId) { |
|
158 |
+ return find.where().eq("projectUser.user.id", userId).findList(); |
|
159 |
+ } |
|
160 |
+ |
|
161 |
+ public static Page<Project> projects(int pageNum) { |
|
162 |
+ return find.findPagingList(25).getPage(pageNum); |
|
163 |
+ } |
|
164 |
+ |
|
165 |
+ public static int countByState(String state) { |
|
166 |
+ if (state == "all") { |
|
167 |
+ return find.findRowCount(); |
|
168 |
+ } else if (state == "public") { |
|
169 |
+ return find.where().eq("share_option", true).findRowCount(); |
|
170 |
+ } else if (state == "private") { |
|
171 |
+ return find.where().eq("share_option", false).findRowCount(); |
|
172 |
+ } else { |
|
173 |
+ return 0; |
|
174 |
+ } |
|
175 |
+ } |
|
176 |
+ |
|
177 |
+ public Date lastUpdateDate() { |
|
178 |
+ try { |
|
179 |
+ GitRepository gitRepo = new GitRepository(owner, name); |
|
180 |
+ List<String> branches = RepositoryService.getRepository(this) |
|
181 |
+ .getBranches(); |
|
182 |
+ if (!branches.isEmpty()) { |
|
183 |
+ List<Commit> history = gitRepo.getHistory(0, 2, "HEAD"); |
|
184 |
+ return history.get(0).getAuthorDate(); |
|
185 |
+ } |
|
186 |
+ } catch (IOException e) { |
|
187 |
+ e.printStackTrace(); |
|
188 |
+ } catch (NoHeadException e) { |
|
189 |
+ e.printStackTrace(); |
|
190 |
+ } catch (GitAPIException e) { |
|
191 |
+ e.printStackTrace(); |
|
192 |
+ } catch (UnsupportedOperationException e) { |
|
193 |
+ e.printStackTrace(); |
|
194 |
+ } catch (ServletException e) { |
|
195 |
+ e.printStackTrace(); |
|
196 |
+ } |
|
197 |
+ return this.date; |
|
198 |
+ } |
|
199 |
+ |
|
200 |
+ public Duration ago() { |
|
201 |
+ return JodaDateUtil.ago(lastUpdateDate()); |
|
202 |
+ } |
|
203 |
+ |
|
204 |
+ public String readme() { |
|
205 |
+ try { |
|
206 |
+ return new String(RepositoryService.getRepository(this).getRawFile( |
|
207 |
+ "README.md")); |
|
208 |
+ } catch (Exception e) { |
|
209 |
+ return null; |
|
210 |
+ } |
|
211 |
+ } |
|
167 | 212 |
} |
--- app/models/User.java
+++ app/models/User.java
... | ... | @@ -1,5 +1,7 @@ |
1 | 1 |
package models; |
2 | 2 |
|
3 |
+import java.text.SimpleDateFormat; |
|
4 |
+import java.util.Date; |
|
3 | 5 |
import java.util.LinkedHashMap; |
4 | 6 |
import java.util.List; |
5 | 7 |
import java.util.Map; |
... | ... | @@ -18,10 +20,12 @@ |
18 | 20 |
import models.support.OrderParams; |
19 | 21 |
import models.support.SearchParams; |
20 | 22 |
import play.cache.Cached; |
23 |
+import play.data.format.Formats; |
|
21 | 24 |
import play.data.validation.Constraints; |
22 | 25 |
import play.data.validation.Constraints.Email; |
23 | 26 |
import play.db.ebean.Model; |
24 | 27 |
import play.db.ebean.Model.Finder; |
28 |
+import utils.JodaDateUtil; |
|
25 | 29 |
|
26 | 30 |
import com.avaje.ebean.Page; |
27 | 31 |
|
... | ... | @@ -52,15 +56,29 @@ |
52 | 56 |
public String avatarUrl; |
53 | 57 |
|
54 | 58 |
public boolean rememberMe; |
59 |
+ |
|
60 |
+ @Formats.DateTime(pattern = "yyyy-MM-dd") |
|
61 |
+ public Date date; |
|
55 | 62 |
|
56 | 63 |
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL) |
57 | 64 |
public List<ProjectUser> projectUser; |
58 | 65 |
|
66 |
+ /** |
|
67 |
+ * 완료일을 yyyy-MM-dd 형식의 문자열로 변환시킵니다. |
|
68 |
+ * |
|
69 |
+ * @return |
|
70 |
+ */ |
|
71 |
+ public String getDateString() { |
|
72 |
+ SimpleDateFormat sdf = new SimpleDateFormat("MMM dd yyyy"); |
|
73 |
+ return sdf.format(this.date); |
|
74 |
+ } |
|
75 |
+ |
|
59 | 76 |
public List<Project> myProjects(){ |
60 | 77 |
return Project.findProjectsByMember(id); |
61 | 78 |
} |
62 | 79 |
|
63 | 80 |
public static Long create(User user) { |
81 |
+ user.date = JodaDateUtil.now(); |
|
64 | 82 |
user.save(); |
65 | 83 |
return user.id; |
66 | 84 |
} |
--- app/views/user/info.scala.html
+++ app/views/user/info.scala.html
... | ... | @@ -1,33 +1,36 @@ |
1 | 1 |
@(user:User) |
2 | 2 |
|
3 |
+@import utils.TemplateHelper._ |
|
4 |
+ |
|
3 | 5 |
@home("Users Info", utils.MenuType.USER) { |
6 |
+ @if(user.loginId == session.get("loginId")){ |
|
7 |
+ <div class="side-menu-wrap"> |
|
8 |
+ <ul class="side-menus ico bg-side-menu unstyled"> |
|
9 |
+ <li class="side-menu"><a href="@routes.UserApp.editUserInfoForm()"><i class="ico ico-setting on"></i></a></li> |
|
10 |
+ </ul> |
|
11 |
+ </div> |
|
12 |
+ } |
|
4 | 13 |
|
5 | 14 |
<div class="page-wrap container"> |
6 | 15 |
<div class="page"> |
7 |
- <h1 class="page-title"><span class="gray">nforge4/</span>SW.CHAE</h1> |
|
16 |
+ <div> |
|
17 |
+ <div><h1 class="page-title">@user.loginId</h1></div> |
|
18 |
+ </div> |
|
19 |
+ |
|
8 | 20 |
<section class="user-box"> |
9 | 21 |
<div class="user-info-box"> |
10 | 22 |
<div class="whoami-wrap"> |
11 |
- <p class="uname">SW.CHAE</p> |
|
12 |
- <p class="name">(doortts)</p> |
|
13 |
- <img src="/images/default-avatar-128.png" width="127" height="127" class="img-rounded"> |
|
23 |
+ <p class="uname">@user.name</p> |
|
24 |
+ <p class="name">(@user.loginId)</p> |
|
25 |
+ <img src="@user.avatarUrl" width="127" height="127" class="img-rounded"> |
|
14 | 26 |
</div> |
15 | 27 |
<div class="user-location info-box"> |
16 |
- <p class="u-location"><i class="ico ico-location"></i>Seoul, Korea, South</p> |
|
17 |
- </div> |
|
18 |
- <hr/> |
|
19 |
- <div class="user-status info-box"> |
|
20 |
- <p><span class="labels">My projects</span><span class="num">6350</span></p> |
|
21 |
- <p><span class="labels">Followers</span><span class="num">6350</span></p> |
|
22 |
- <p><span class="labels">Following</span><span class="num">521385</span></p> |
|
23 |
- <p><span class="labels">Starred</span><span class="num">323</span></p> |
|
28 |
+ <p class="u-location"><i class="ico ico-location"></i>@user.email</p> |
|
24 | 29 |
</div> |
25 | 30 |
<hr/> |
26 | 31 |
<div class="user-other-info info-box"> |
27 |
- <p><strong>FOCUS</strong></p> |
|
28 |
- <p class="focuses">C#, JAVA, SQL, JAVASCRIPT</p> |
|
29 | 32 |
<p class="since"><strong>MEMBER SINCE</strong></p> |
30 |
- <p><span class="since">November 22, 2012</span></p> |
|
33 |
+ <p><span class="since">@user.getDateString</span></p> |
|
31 | 34 |
<p class="social-btns"> |
32 | 35 |
<a href="http://twitter.com/nforge"><i class="ico btn-tw"></i></a> |
33 | 36 |
<a href="http://facebook.com/nforge"><i class="ico btn-fb"></i></a> |
... | ... | @@ -36,228 +39,41 @@ |
36 | 39 |
</div> |
37 | 40 |
<hr/> |
38 | 41 |
<div class="btn-wrap"> |
39 |
- <a href="/new-project"><i class="ico btn-new-project"></i></a> |
|
42 |
+ <a href="/projectform"><i class="ico btn-new-project"></i></a> |
|
40 | 43 |
</div> |
41 | 44 |
</div> |
42 | 45 |
<div class="user-stream-box"> |
43 | 46 |
<ul class="nav nav-tabs user-stream-tab"> |
44 | 47 |
<li class="active"><a href="user-setting.html">Repositories</a></li> |
45 |
- <li><a href="user-activities.html">Activities</a></li> |
|
48 |
+ <!-- li><a href="user-activities.html">Activities</a></li--> |
|
46 | 49 |
</ul> |
47 |
- <div class="user-stream-wrap"> |
|
48 |
- <div class="header-wrap"> |
|
49 |
- <div class="search-wrap user-setting"> |
|
50 |
- <div class="inner"> |
|
51 |
- <form action="/activity-search" method="get"> |
|
52 |
- <input name="query" class="text" type="text" placeholder="검색"><button type="submit" class="btn-transparent search-btn">SEARCH</button> |
|
53 |
- </form> |
|
54 |
- </div> |
|
55 |
- </div> |
|
56 |
- </div> |
|
50 |
+ <!-- div class="user-stream-wrap"> |
|
57 | 51 |
<div class="filter-wrap user-setting"> |
58 | 52 |
<div class="filters"> |
59 | 53 |
<a href="/html/user-setting.html" class="filter"><i class="ico btn-gray-arrow"></i>전체</a> |
60 | 54 |
<a href="/html/user-activities.html?order=staus" class="filter active"><i class="ico btn-gray-arrow down"></i>상태순</a> |
61 | 55 |
</div> |
62 | 56 |
</div> |
63 |
- </div> |
|
57 |
+ </div--> |
|
64 | 58 |
<ul class="user-streams"> |
59 |
+ @for(project <- user.myProjects()){ |
|
65 | 60 |
<li class="user-stream"> |
66 |
- <h3 class="project-name">nforge4</h3> |
|
61 |
+ <h3 class="project-name"><a href="@routes.ProjectApp.project(project.owner, project.name)">@project.owner/@project.name</a></h3> |
|
67 | 62 |
<div class="stream-desc-wrap"> |
68 | 63 |
<div class="stream-desc"> |
69 |
- <p class="nm">최근 활동 관련 영역입니다.</p> |
|
70 |
- <p class="date">Last updated 2 months ago</p> |
|
64 |
+ <!-- p class="nm">최근 활동 관련 영역입니다.</p--> |
|
65 |
+ <p class="date">Last updated @agoString(project.ago)</p> |
|
71 | 66 |
</div> |
72 |
- <div class="project-status"> |
|
67 |
+ <!-- div class="project-status"> |
|
73 | 68 |
<i class="ico ico-like"></i><span class="num">1</span> |
74 | 69 |
<i class="ico ico-activity high"></i> |
75 |
- </div> |
|
70 |
+ </div--> |
|
76 | 71 |
</div> |
77 | 72 |
</li> |
78 |
- <li class="user-stream"> |
|
79 |
- <h3 class="project-name">nforge4</h3> |
|
80 |
- <div class="stream-desc-wrap"> |
|
81 |
- <div class="stream-desc"> |
|
82 |
- <p class="nm">최근 활동 관련 영역입니다. 활동 관련 영역입니다.최근 활동 관련 영역입니다.최근 활동 관련 영역입니다.최근 활동 관련 영역입니다.최근 활동 관련 영역입니다.최근 활동 관련 영역입니다.최근 활동 관련 영역입니다.최근 활동 관련 영역입니다.최근 활동 관련 영역입니다.최근 활동 관련 영역입니다.</p> |
|
83 |
- <p class="date">Last updated 2 months ago</p> |
|
84 |
- </div> |
|
85 |
- <div class="project-status"> |
|
86 |
- <i class="ico ico-like"></i><span class="num">1</span> |
|
87 |
- <i class="ico ico-activity high"></i> |
|
88 |
- </div> |
|
89 |
- </div> |
|
90 |
- </li> |
|
91 |
- <li class="user-stream"> |
|
92 |
- <h3 class="project-name">nforge4</h3> |
|
93 |
- <div class="stream-desc-wrap"> |
|
94 |
- <div class="stream-desc"> |
|
95 |
- <p class="nm">최근 활동 관련 영역입니다.</p> |
|
96 |
- <p class="date">Last updated 2 months ago</p> |
|
97 |
- </div> |
|
98 |
- <div class="project-status"> |
|
99 |
- <i class="ico ico-like"></i><span class="num">1</span> |
|
100 |
- <i class="ico ico-activity high"></i> |
|
101 |
- </div> |
|
102 |
- </div> |
|
103 |
- </li> |
|
104 |
- <li class="user-stream"> |
|
105 |
- <h3 class="project-name">nforge4</h3> |
|
106 |
- <div class="stream-desc-wrap"> |
|
107 |
- <div class="stream-desc"> |
|
108 |
- <p class="nm">최근 활동 관련 영역입니다.</p> |
|
109 |
- <p class="date">Last updated 2 months ago</p> |
|
110 |
- </div> |
|
111 |
- <div class="project-status"> |
|
112 |
- <i class="ico ico-like"></i><span class="num">1</span> |
|
113 |
- <i class="ico ico-activity high"></i> |
|
114 |
- </div> |
|
115 |
- </div> |
|
116 |
- </li> |
|
117 |
- <li class="user-stream"> |
|
118 |
- <h3 class="project-name">nforge4</h3> |
|
119 |
- <div class="stream-desc-wrap"> |
|
120 |
- <div class="stream-desc"> |
|
121 |
- <p class="nm">최근 활동 관련 영역입니다.</p> |
|
122 |
- <p class="date">Last updated 2 months ago</p> |
|
123 |
- </div> |
|
124 |
- <div class="project-status"> |
|
125 |
- <i class="ico ico-like"></i><span class="num">1</span> |
|
126 |
- <i class="ico ico-activity high"></i> |
|
127 |
- </div> |
|
128 |
- </div> |
|
129 |
- </li> |
|
130 |
- <li class="user-stream"> |
|
131 |
- <h3 class="project-name">nforge4</h3> |
|
132 |
- <div class="stream-desc-wrap"> |
|
133 |
- <div class="stream-desc"> |
|
134 |
- <p class="nm">최근 활동 관련 영역입니다.</p> |
|
135 |
- <p class="date">Last updated 2 months ago</p> |
|
136 |
- </div> |
|
137 |
- <div class="project-status"> |
|
138 |
- <i class="ico ico-like"></i><span class="num">1</span> |
|
139 |
- <i class="ico ico-activity high"></i> |
|
140 |
- </div> |
|
141 |
- </div> |
|
142 |
- </li> |
|
143 |
- <li class="user-stream"> |
|
144 |
- <h3 class="project-name">nforge4</h3> |
|
145 |
- <div class="stream-desc-wrap"> |
|
146 |
- <div class="stream-desc"> |
|
147 |
- <p class="nm">최근 활동 관련 영역입니다.</p> |
|
148 |
- <p class="date">Last updated 2 months ago</p> |
|
149 |
- </div> |
|
150 |
- <div class="project-status"> |
|
151 |
- <i class="ico ico-like"></i><span class="num">1</span> |
|
152 |
- <i class="ico ico-activity high"></i> |
|
153 |
- </div> |
|
154 |
- </div> |
|
155 |
- </li> |
|
156 |
- <li class="user-stream"> |
|
157 |
- <h3 class="project-name">nforge4</h3> |
|
158 |
- <div class="stream-desc-wrap"> |
|
159 |
- <div class="stream-desc"> |
|
160 |
- <p class="nm">최근 활동 관련 영역입니다.</p> |
|
161 |
- <p class="date">Last updated 2 months ago</p> |
|
162 |
- </div> |
|
163 |
- <div class="project-status"> |
|
164 |
- <i class="ico ico-like"></i><span class="num">1</span> |
|
165 |
- <i class="ico ico-activity high"></i> |
|
166 |
- </div> |
|
167 |
- </div> |
|
168 |
- </li> |
|
169 |
- <li class="user-stream"> |
|
170 |
- <h3 class="project-name">nforge4</h3> |
|
171 |
- <div class="stream-desc-wrap"> |
|
172 |
- <div class="stream-desc"> |
|
173 |
- <p class="nm">최근 활동 관련 영역입니다.</p> |
|
174 |
- <p class="date">Last updated 2 months ago</p> |
|
175 |
- </div> |
|
176 |
- <div class="project-status"> |
|
177 |
- <i class="ico ico-like"></i><span class="num">1</span> |
|
178 |
- <i class="ico ico-activity high"></i> |
|
179 |
- </div> |
|
180 |
- </div> |
|
181 |
- </li> |
|
182 |
- <li class="user-stream"> |
|
183 |
- <h3 class="project-name">nforge4</h3> |
|
184 |
- <div class="stream-desc-wrap"> |
|
185 |
- <div class="stream-desc"> |
|
186 |
- <p class="nm">최근 활동 관련 영역입니다.</p> |
|
187 |
- <p class="date">Last updated 2 months ago</p> |
|
188 |
- </div> |
|
189 |
- <div class="project-status"> |
|
190 |
- <i class="ico ico-like"></i><span class="num">1</span> |
|
191 |
- <i class="ico ico-activity high"></i> |
|
192 |
- </div> |
|
193 |
- </div> |
|
194 |
- </li> |
|
195 |
- <li class="user-stream"> |
|
196 |
- <h3 class="project-name">nforge4</h3> |
|
197 |
- <div class="stream-desc-wrap"> |
|
198 |
- <div class="stream-desc"> |
|
199 |
- <p class="nm">최근 활동 관련 영역입니다.</p> |
|
200 |
- <p class="date">Last updated 2 months ago</p> |
|
201 |
- </div> |
|
202 |
- <div class="project-status"> |
|
203 |
- <i class="ico ico-like"></i><span class="num">1</span> |
|
204 |
- <i class="ico ico-activity high"></i> |
|
205 |
- </div> |
|
206 |
- </div> |
|
207 |
- </li> |
|
73 |
+ } |
|
208 | 74 |
</ul> |
209 | 75 |
</div> |
210 | 76 |
</section> |
211 |
- </div> |
|
212 |
-</div> |
|
213 |
- |
|
214 |
- |
|
215 |
- |
|
216 |
-<div class="page"> |
|
217 |
- |
|
218 |
- <h1 class="page-title"> |
|
219 |
- @user.loginId |
|
220 |
- </h1> |
|
221 |
- @if(user.loginId == session.get("loginId")){ |
|
222 |
- <a href="@routes.UserApp.editUserInfoForm()" class="btn">수정</a> |
|
223 |
- } |
|
224 |
- |
|
225 |
- <div class="bubble-wrap dark-gray project-home"> |
|
226 |
- <div class="inner logo"> |
|
227 |
- <div class="logo-wrap"> |
|
228 |
- <img src="@user.avatarUrl" /> |
|
229 |
- </div> |
|
230 |
- </div> |
|
231 |
- <div class="inner project-info"> |
|
232 |
- <header> |
|
233 |
- <h3>프로파일 정보</h3> |
|
234 |
- </header> |
|
235 |
- <ul class="infos"> |
|
236 |
- <li class="info"> |
|
237 |
- <strong>로그인 아이디 :</strong> @user.loginId |
|
238 |
- </li> |
|
239 |
- <li class="info"> |
|
240 |
- <strong>이름 :</strong> @user.name |
|
241 |
- </li> |
|
242 |
- <li class="info"> |
|
243 |
- <strong>이메일 :</strong> @user.email |
|
244 |
- </li> |
|
245 |
- </ul> |
|
246 |
- </div> |
|
247 |
- <div class="inner member-info"> |
|
248 |
- <header> |
|
249 |
- <h3>프로젝트 정보</h3> |
|
250 |
- </header> |
|
251 |
- <div class="member-wrap"> |
|
252 |
- <ul class="project-members"> |
|
253 |
- @for(project <- user.myProjects()){ |
|
254 |
- <li class="member"> |
|
255 |
- <a href="@routes.ProjectApp.project(project.owner, project.name)"><strong>@project.name</strong></a> |
|
256 |
- </li> |
|
257 |
- } |
|
258 |
- </ul> |
|
259 |
- </div> |
|
260 |
- </div> |
|
261 | 77 |
</div> |
262 | 78 |
</div> |
263 | 79 |
} |
--- conf/initial-data.yml
+++ conf/initial-data.yml
... | ... | @@ -7,6 +7,7 @@ |
7 | 7 |
passwordSalt: '[B@1032a4' |
8 | 8 |
email: admin@nhn.com |
9 | 9 |
avatarUrl: /assets/images/default-avatar-128.png |
10 |
+ date: 2012-11-01 08:00:00 |
|
10 | 11 |
- !!models.User |
11 | 12 |
name: Hobi |
12 | 13 |
loginId: hobi |
... | ... | @@ -14,6 +15,7 @@ |
14 | 15 |
passwordSalt: '[B@1032a4' |
15 | 16 |
email: hobi@nhn.com |
16 | 17 |
avatarUrl: /assets/images/default-avatar-128.png |
18 |
+ date: 2012-12-01 08:00:00 |
|
17 | 19 |
- !!models.User |
18 | 20 |
name: scott |
19 | 21 |
loginId: k16wire |
... | ... | @@ -21,6 +23,7 @@ |
21 | 23 |
passwordSalt: '[B@1032a4' |
22 | 24 |
email: k16wire@naver.com |
23 | 25 |
avatarUrl: /assets/images/default-avatar-128.png |
26 |
+ date: 2012-01-01 08:00:00 |
|
24 | 27 |
- !!models.User |
25 | 28 |
name: suwon |
26 | 29 |
loginId: doortts |
... | ... | @@ -28,6 +31,7 @@ |
28 | 31 |
passwordSalt: '[B@1032a4' |
29 | 32 |
email: doortts@gmail.com |
30 | 33 |
avatarUrl: /assets/images/default-avatar-128.png |
34 |
+ date: 2012-07-01 08:00:00 |
|
31 | 35 |
- !!models.User |
32 | 36 |
name: eungjun |
33 | 37 |
loginId: nori |
... | ... | @@ -35,12 +39,14 @@ |
35 | 39 |
passwordSalt: '[B@1032a4' |
36 | 40 |
email: ejlee@nhn.com |
37 | 41 |
avatarUrl: /assets/images/default-avatar-128.png |
42 |
+ date: 2012-05-01 08:00:00 |
|
38 | 43 |
- !!models.User |
39 | 44 |
name: anonymous |
40 | 45 |
loginId: anonymous |
41 | 46 |
password: |
42 | 47 |
email: anonymous@nhn.com |
43 | 48 |
avatarUrl: /assets/images/default-avatar-128.png |
49 |
+ date: 2012-02-01 08:00:00 |
|
44 | 50 |
|
45 | 51 |
# Projects |
46 | 52 |
projects: |
... | ... | @@ -52,6 +58,7 @@ |
52 | 58 |
siteurl: http://localhost:9000/nForge4java |
53 | 59 |
owner: hobi |
54 | 60 |
isAuthorEditable: true |
61 |
+ date: 2012-02-01 08:00:00 |
|
55 | 62 |
- !!models.Project |
56 | 63 |
name: Jindo |
57 | 64 |
overview: Jindo는 NHN에서 제작한 Javascript Library 이다. |
... | ... | @@ -60,6 +67,7 @@ |
60 | 67 |
siteurl: http://localhost:9000/Jindo |
61 | 68 |
owner: k16wire |
62 | 69 |
isAuthorEditable: false |
70 |
+ date: 2012-01-01 08:00:00 |
|
63 | 71 |
- !!models.Project |
64 | 72 |
name: CUBRID |
65 | 73 |
overview: CUBRID는 엔터프라이즈급 오픈 소스 DBMS로서, 인터넷 서비스에 최적화된 DBMS를 지향하고 있습니다. |
... | ... | @@ -68,6 +76,7 @@ |
68 | 76 |
siteurl: http://localhost:9000/CUBRID |
69 | 77 |
owner: doortts |
70 | 78 |
isAuthorEditable: true |
79 |
+ date: 2012-08-01 08:00:00 |
|
71 | 80 |
- !!models.Project |
72 | 81 |
name: HelloSocialApp |
73 | 82 |
overview: 네이버 앱팩토리 플랫폼에서 제공하는 오픈소셜 API를 직접 실행해 볼 수 있는 애플리케이션입니다. |
... | ... | @@ -76,6 +85,7 @@ |
76 | 85 |
siteurl: http://localhost:9000/HelloSocialApp |
77 | 86 |
owner: hobi |
78 | 87 |
isAuthorEditable: true |
88 |
+ date: 2012-12-01 08:00:00 |
|
79 | 89 |
|
80 | 90 |
# Board |
81 | 91 |
posts: |
... | ... | @@ -107,7 +117,7 @@ |
107 | 117 |
body: 내용 불필요한~ |
108 | 118 |
state: OPEN |
109 | 119 |
milestoneId: 1 |
110 |
- date: 2012-11-01 08:00:00 |
|
120 |
+ date: 2012-11-01 08:00:00 |
|
111 | 121 |
project: !!models.Project |
112 | 122 |
id: 1 |
113 | 123 |
- !!models.Issue |
--- test/models/ProjectTest.java
+++ test/models/ProjectTest.java
... | ... | @@ -1,13 +1,22 @@ |
1 | 1 |
package models; |
2 | 2 |
|
3 |
+import static org.fest.assertions.Assertions.assertThat; |
|
4 |
+ |
|
5 |
+import java.io.BufferedWriter; |
|
6 |
+import java.io.File; |
|
7 |
+import java.io.FileWriter; |
|
8 |
+import java.util.List; |
|
9 |
+ |
|
10 |
+import org.eclipse.jgit.api.Git; |
|
11 |
+import org.eclipse.jgit.api.errors.GitAPIException; |
|
12 |
+import org.eclipse.jgit.api.errors.NoFilepatternException; |
|
13 |
+import org.eclipse.jgit.lib.Repository; |
|
14 |
+import org.eclipse.jgit.lib.RepositoryBuilder; |
|
3 | 15 |
import org.junit.Ignore; |
4 | 16 |
import org.junit.Test; |
5 | 17 |
|
6 |
-import controllers.UserApp; |
|
7 |
- |
|
8 |
-import java.util.List; |
|
9 |
- |
|
10 |
-import static org.fest.assertions.Assertions.assertThat; |
|
18 |
+import playRepository.Commit; |
|
19 |
+import playRepository.GitRepository; |
|
11 | 20 |
|
12 | 21 |
/** |
13 | 22 |
* @author "Hwi Ahn" |
... | ... | @@ -136,4 +145,35 @@ |
136 | 145 |
assertThat(result1).isEqualTo(false); |
137 | 146 |
assertThat(result2).isEqualTo(true); |
138 | 147 |
} |
148 |
+ |
|
149 |
+ @Test |
|
150 |
+ public void lastUpdate() throws Exception { |
|
151 |
+ // given |
|
152 |
+ String userName = "hobi"; |
|
153 |
+ String projectName = "testProject"; |
|
154 |
+ String wcPath = GitRepository.getRepoPrefix() + userName + "/" + projectName; |
|
155 |
+ |
|
156 |
+ String repoPath = wcPath + "/.git"; |
|
157 |
+ File repoDir = new File(repoPath); |
|
158 |
+ Repository repo = new RepositoryBuilder().setGitDir(repoDir).build(); |
|
159 |
+ repo.create(false); |
|
160 |
+ |
|
161 |
+ Git git = new Git(repo); |
|
162 |
+ String testFilePath = wcPath + "/readme.txt"; |
|
163 |
+ BufferedWriter out = new BufferedWriter(new FileWriter(testFilePath)); |
|
164 |
+ |
|
165 |
+ out.write("hello 1"); |
|
166 |
+ out.flush(); |
|
167 |
+ git.add().addFilepattern("readme.txt").call(); |
|
168 |
+ git.commit().setMessage("commit 1").call(); |
|
169 |
+ |
|
170 |
+ GitRepository gitRepo = new GitRepository(userName, projectName + "/"); |
|
171 |
+ |
|
172 |
+// Project project = Project.findByNameAndOwner(userName, projectName); |
|
173 |
+ // When |
|
174 |
+ List<Commit> history = gitRepo.getHistory(0, 2, "HEAD"); |
|
175 |
+ Commit commit = history.get(0); |
|
176 |
+ // Then |
|
177 |
+ assertThat(commit.getAuthorDate()).isNotNull(); |
|
178 |
+ } |
|
139 | 179 |
} |
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?