[Notice] Announcing the End of Demo Server [Read me]

referring issues from a pullrequest and a commit
@05e4a408bb86c1b81dd00f2056da1a0edbfcc89f
+++ app/actors/CommitCheckActor.java
... | ... | @@ -0,0 +1,114 @@ |
1 | +package actors; | |
2 | + | |
3 | +import akka.actor.UntypedActor; | |
4 | +import controllers.routes; | |
5 | +import models.*; | |
6 | +import models.enumeration.EventType; | |
7 | +import org.eclipse.jgit.lib.ObjectId; | |
8 | +import org.eclipse.jgit.lib.Repository; | |
9 | +import org.eclipse.jgit.revwalk.RevCommit; | |
10 | +import org.eclipse.jgit.revwalk.RevWalk; | |
11 | +import org.eclipse.jgit.transport.ReceiveCommand; | |
12 | +import play.i18n.Messages; | |
13 | +import playRepository.GitCommit; | |
14 | +import playRepository.GitRepository; | |
15 | + | |
16 | +import java.io.IOException; | |
17 | +import java.util.ArrayList; | |
18 | +import java.util.Collection; | |
19 | +import java.util.Date; | |
20 | +import java.util.List; | |
21 | +import java.util.regex.Matcher; | |
22 | + | |
23 | +/** | |
24 | + * @author Keesun Baik | |
25 | + */ | |
26 | +public class CommitCheckActor extends UntypedActor { | |
27 | + | |
28 | + @Override | |
29 | + public void onReceive(Object object) throws Exception { | |
30 | + if(!(object instanceof CommitCheckMessage)) { | |
31 | + return; | |
32 | + } | |
33 | + | |
34 | + CommitCheckMessage message = (CommitCheckMessage)object; | |
35 | + List<RevCommit> commits = getCommits(message); | |
36 | + for(RevCommit commit : commits) { | |
37 | + addIssueEvent(commit, message.getProject()); | |
38 | + } | |
39 | + | |
40 | + } | |
41 | + | |
42 | + private void addIssueEvent(RevCommit commit, Project project) { | |
43 | + GitCommit gitCommit = new GitCommit(commit); | |
44 | + String fullMessage = gitCommit.getMessage(); | |
45 | + List<Issue> referredIssues = IssueEvent.findReferredIssue(fullMessage, project); | |
46 | + String newValue = getNewEventValue(gitCommit, project); | |
47 | + for(Issue issue : referredIssues) { | |
48 | + IssueEvent issueEvent = new IssueEvent(); | |
49 | + issueEvent.issue = issue; | |
50 | + issueEvent.senderLoginId = gitCommit.getCommitterName(); | |
51 | + issueEvent.newValue = newValue; | |
52 | + issueEvent.created = new Date(); | |
53 | + issueEvent.eventType = EventType.ISSUE_REFERRED; | |
54 | + issueEvent.save(); | |
55 | + } | |
56 | + } | |
57 | + | |
58 | + private String getNewEventValue(GitCommit gitCommit, Project project) { | |
59 | + return Messages.get("issue.event.referred.from.commit", | |
60 | + gitCommit.getCommitterName(), gitCommit.getShortId(), | |
61 | + routes.CodeHistoryApp.show(project.owner, project.name, gitCommit.getId())); | |
62 | + } | |
63 | + | |
64 | + private List<RevCommit> getCommits(CommitCheckMessage message) { | |
65 | + List<RevCommit> commits = new ArrayList<>(); | |
66 | + for(ReceiveCommand command : message.getCommands()) { | |
67 | + if(isNewOrUpdateCommand(command)) { | |
68 | + commits.addAll(parseCommitsFrom(command, message.getProject())); | |
69 | + } | |
70 | + } | |
71 | + return commits; | |
72 | + } | |
73 | + | |
74 | + private Collection<? extends RevCommit> parseCommitsFrom(ReceiveCommand command, Project project) { | |
75 | + Repository repository = GitRepository.buildGitRepository(project); | |
76 | + List<RevCommit> list = new ArrayList<>(); | |
77 | + | |
78 | + try { | |
79 | + ObjectId endRange = command.getNewId(); | |
80 | + ObjectId startRange = command.getOldId(); | |
81 | + | |
82 | + RevWalk rw = new RevWalk(repository); | |
83 | + rw.markStart(rw.parseCommit(endRange)); | |
84 | + if (startRange.equals(ObjectId.zeroId())) { | |
85 | + // maybe this is a tag or an orphan branch | |
86 | + list.add(rw.parseCommit(endRange)); | |
87 | + rw.dispose(); | |
88 | + return list; | |
89 | + } else { | |
90 | + rw.markUninteresting(rw.parseCommit(startRange)); | |
91 | + } | |
92 | + | |
93 | + Iterable<RevCommit> revlog = rw; | |
94 | + for (RevCommit rev : revlog) { | |
95 | + list.add(rev); | |
96 | + } | |
97 | + rw.dispose(); | |
98 | + } catch (IOException e) { | |
99 | + e.printStackTrace(); | |
100 | + } | |
101 | + | |
102 | + return list; | |
103 | + } | |
104 | + | |
105 | + private boolean isNewOrUpdateCommand(ReceiveCommand command) { | |
106 | + List<ReceiveCommand.Type> allowdTypes = new ArrayList<>(); | |
107 | + allowdTypes.add(ReceiveCommand.Type.CREATE); | |
108 | + allowdTypes.add(ReceiveCommand.Type.UPDATE); | |
109 | + allowdTypes.add(ReceiveCommand.Type.UPDATE_NONFASTFORWARD); | |
110 | + return allowdTypes.contains(command.getType()); | |
111 | + } | |
112 | + | |
113 | + | |
114 | +} |
--- app/controllers/PullRequestApp.java
+++ app/controllers/PullRequestApp.java
... | ... | @@ -235,7 +235,7 @@ |
235 | 235 |
return redirect(routes.PullRequestApp.pullRequest(originalProject.owner, originalProject.name, sentRequest.number)); |
236 | 236 |
} |
237 | 237 |
|
238 |
- pullRequest.saveWithNumber(); |
|
238 |
+ pullRequest.save(); |
|
239 | 239 |
|
240 | 240 |
Attachment.moveAll(UserApp.currentUser().asResource(), pullRequest.asResource()); |
241 | 241 |
|
+++ app/models/CommitCheckMessage.java
... | ... | @@ -0,0 +1,28 @@ |
1 | +package models; | |
2 | + | |
3 | +import org.eclipse.jgit.transport.ReceiveCommand; | |
4 | + | |
5 | +import java.util.Collection; | |
6 | + | |
7 | +/** | |
8 | + * @author Keesun Baik | |
9 | + */ | |
10 | +public class CommitCheckMessage { | |
11 | + | |
12 | + private Collection<ReceiveCommand> commands; | |
13 | + | |
14 | + private Project project; | |
15 | + | |
16 | + public CommitCheckMessage(Collection<ReceiveCommand> commands, Project project) { | |
17 | + this.commands = commands; | |
18 | + this.project = project; | |
19 | + } | |
20 | + | |
21 | + public Collection<ReceiveCommand> getCommands() { | |
22 | + return commands; | |
23 | + } | |
24 | + | |
25 | + public Project getProject() { | |
26 | + return project; | |
27 | + } | |
28 | +} |
--- app/models/Issue.java
+++ app/models/Issue.java
... | ... | @@ -21,6 +21,7 @@ |
21 | 21 |
import java.io.ByteArrayOutputStream; |
22 | 22 |
import java.io.IOException; |
23 | 23 |
import java.util.*; |
24 |
+import java.util.regex.Pattern; |
|
24 | 25 |
|
25 | 26 |
/** |
26 | 27 |
* 이슈 |
... | ... | @@ -47,6 +48,7 @@ |
47 | 48 |
|
48 | 49 |
public static final String DEFAULT_SORTER = "createdDate"; |
49 | 50 |
public static final String TO_BE_ASSIGNED = "TBA"; |
51 |
+ public static final Pattern ISSUE_PATTERN = Pattern.compile("#\\d+"); |
|
50 | 52 |
|
51 | 53 |
public State state; |
52 | 54 |
|
--- app/models/IssueEvent.java
+++ app/models/IssueEvent.java
... | ... | @@ -10,7 +10,10 @@ |
10 | 10 |
import javax.persistence.Entity; |
11 | 11 |
import javax.persistence.Id; |
12 | 12 |
import javax.persistence.ManyToOne; |
13 |
+import java.util.ArrayList; |
|
13 | 14 |
import java.util.Date; |
15 |
+import java.util.List; |
|
16 |
+import java.util.regex.Matcher; |
|
14 | 17 |
|
15 | 18 |
@Entity |
16 | 19 |
public class IssueEvent extends Model implements TimelineItem { |
... | ... | @@ -106,4 +109,20 @@ |
106 | 109 |
public Date getDate() { |
107 | 110 |
return created; |
108 | 111 |
} |
112 |
+ |
|
113 |
+ public static List<Issue> findReferredIssue(String message, Project project) { |
|
114 |
+ Matcher m = Issue.ISSUE_PATTERN.matcher(message); |
|
115 |
+ List<Issue> referredIssues = new ArrayList<>(); |
|
116 |
+ |
|
117 |
+ while(m.find()) { |
|
118 |
+ String issueText = m.group(); |
|
119 |
+ String issueNumber = issueText.substring(1); // removing the leading char # |
|
120 |
+ Issue issue = Issue.findByNumber(project, Long.parseLong(issueNumber)); |
|
121 |
+ if(issue != null) { |
|
122 |
+ referredIssues.add(issue); |
|
123 |
+ } |
|
124 |
+ } |
|
125 |
+ |
|
126 |
+ return referredIssues; |
|
127 |
+ } |
|
109 | 128 |
} |
--- app/models/PullRequest.java
+++ app/models/PullRequest.java
... | ... | @@ -2,6 +2,8 @@ |
2 | 2 |
|
3 | 3 |
import com.avaje.ebean.Page; |
4 | 4 |
import controllers.UserApp; |
5 |
+import controllers.routes; |
|
6 |
+import models.enumeration.EventType; |
|
5 | 7 |
import models.enumeration.Operation; |
6 | 8 |
import models.enumeration.ResourceType; |
7 | 9 |
import models.enumeration.State; |
... | ... | @@ -15,6 +17,7 @@ |
15 | 17 |
import play.data.validation.Constraints; |
16 | 18 |
import play.db.ebean.Model; |
17 | 19 |
import play.db.ebean.Transactional; |
20 |
+import play.i18n.Messages; |
|
18 | 21 |
import playRepository.GitRepository; |
19 | 22 |
import utils.AccessControl; |
20 | 23 |
import utils.Constants; |
... | ... | @@ -23,10 +26,8 @@ |
23 | 26 |
|
24 | 27 |
import javax.persistence.*; |
25 | 28 |
import javax.validation.constraints.Size; |
26 |
-import java.util.Date; |
|
27 |
-import java.util.HashSet; |
|
28 |
-import java.util.List; |
|
29 |
-import java.util.Set; |
|
29 |
+import java.util.*; |
|
30 |
+import java.util.regex.Matcher; |
|
30 | 31 |
|
31 | 32 |
@Entity |
32 | 33 |
public class PullRequest extends Model implements ResourceConvertible { |
... | ... | @@ -289,6 +290,7 @@ |
289 | 290 |
this.fromBranch = newPullRequest.fromBranch; |
290 | 291 |
this.title = newPullRequest.title; |
291 | 292 |
this.body = newPullRequest.body; |
293 |
+ updateIssueEvents(); |
|
292 | 294 |
update(); |
293 | 295 |
} |
294 | 296 |
|
... | ... | @@ -382,9 +384,11 @@ |
382 | 384 |
} |
383 | 385 |
|
384 | 386 |
@Transactional |
385 |
- public void saveWithNumber() { |
|
387 |
+ @Override |
|
388 |
+ public void save() { |
|
386 | 389 |
this.number = nextPullRequestNumber(toProject); |
387 |
- this.save(); |
|
390 |
+ super.save(); |
|
391 |
+ addNewIssueEvents(); |
|
388 | 392 |
} |
389 | 393 |
|
390 | 394 |
public static long nextPullRequestNumber(Project project) { |
... | ... | @@ -465,4 +469,59 @@ |
465 | 469 |
.findPagingList(ITEMS_PER_PAGE) |
466 | 470 |
.getPage(pageNum); |
467 | 471 |
} |
472 |
+ |
|
473 |
+ /** |
|
474 |
+ * 새로운 풀리퀘가 저장될때 풀리퀘의 제목과 본문에서 참조한 이슈에 이슈 이벤트를 생성한다. |
|
475 |
+ */ |
|
476 |
+ private void addNewIssueEvents() { |
|
477 |
+ List<Issue> referredIsseus = IssueEvent.findReferredIssue(this.title + this.body, this.toProject); |
|
478 |
+ String newValue = getNewEventValue(); |
|
479 |
+ for(Issue issue : referredIsseus) { |
|
480 |
+ IssueEvent issueEvent = new IssueEvent(); |
|
481 |
+ issueEvent.issue = issue; |
|
482 |
+ issueEvent.senderLoginId = this.contributor.loginId; |
|
483 |
+ issueEvent.newValue = newValue; |
|
484 |
+ issueEvent.created = new Date(); |
|
485 |
+ issueEvent.eventType = EventType.ISSUE_REFERRED; |
|
486 |
+ issueEvent.save(); |
|
487 |
+ } |
|
488 |
+ } |
|
489 |
+ |
|
490 |
+ private String getNewEventValue() { |
|
491 |
+ return Messages.get("issue.event.referred.from.pullrequest", |
|
492 |
+ this.contributor.loginId, this.fromBranch, this.toBranch, |
|
493 |
+ routes.PullRequestApp.pullRequest(this.toProject.owner, this.toProject.name, this.number)); |
|
494 |
+ } |
|
495 |
+ |
|
496 |
+ /** |
|
497 |
+ * 풀리퀘가 수정될 때 기존의 모든 이슈 이벤트를 삭제하고 새로 추가한다. |
|
498 |
+ */ |
|
499 |
+ public void updateIssueEvents() { |
|
500 |
+ deleteIssueEvents(); |
|
501 |
+ addNewIssueEvents(); |
|
502 |
+ } |
|
503 |
+ |
|
504 |
+ /** |
|
505 |
+ * 풀리퀘가 삭제될 때 관련있는 모든 이슈 이벤트를 삭제한다. |
|
506 |
+ */ |
|
507 |
+ public void deleteIssueEvents() { |
|
508 |
+ String newValue = getNewEventValue(); |
|
509 |
+ |
|
510 |
+ List<IssueEvent> oldEvents = IssueEvent.find.where() |
|
511 |
+ .eq("newValue", newValue) |
|
512 |
+ .eq("senderLoginId", this.contributor.loginId) |
|
513 |
+ .eq("eventType", EventType.ISSUE_REFERRED) |
|
514 |
+ .findList(); |
|
515 |
+ |
|
516 |
+ for(IssueEvent event : oldEvents) { |
|
517 |
+ event.delete(); |
|
518 |
+ } |
|
519 |
+ } |
|
520 |
+ |
|
521 |
+ @Override |
|
522 |
+ public void delete() { |
|
523 |
+ deleteIssueEvents(); |
|
524 |
+ super.delete(); |
|
525 |
+ } |
|
526 |
+ |
|
468 | 527 |
} |
--- app/models/enumeration/EventType.java
+++ app/models/enumeration/EventType.java
... | ... | @@ -15,7 +15,8 @@ |
15 | 15 |
NEW_COMMENT("notification.type.new.comment", 7), |
16 | 16 |
NEW_SIMPLE_COMMENT("notification.type.new.simple.comment", 8), |
17 | 17 |
MEMBER_ENROLL_REQUEST("notification.type.member.enroll", 9), |
18 |
- PULL_REQUEST_CONFLICTS("notification.type.pull.request.conflicts", 10); |
|
18 |
+ PULL_REQUEST_CONFLICTS("notification.type.pull.request.conflicts", 10), |
|
19 |
+ ISSUE_REFERRED("notification.type.issue.referred", 11); |
|
19 | 20 |
|
20 | 21 |
private String descr; |
21 | 22 |
|
--- app/playRepository/GitRepository.java
+++ app/playRepository/GitRepository.java
... | ... | @@ -120,9 +120,13 @@ |
120 | 120 |
* @return |
121 | 121 |
* @throws IOException |
122 | 122 |
*/ |
123 |
- public static Repository buildGitRepository(String ownerName, String projectName) throws IOException { |
|
124 |
- return new RepositoryBuilder().setGitDir( |
|
125 |
- new File(getGitDirectory(ownerName, projectName))).build(); |
|
123 |
+ public static Repository buildGitRepository(String ownerName, String projectName) { |
|
124 |
+ try { |
|
125 |
+ return new RepositoryBuilder().setGitDir( |
|
126 |
+ new File(getGitDirectory(ownerName, projectName))).build(); |
|
127 |
+ } catch (IOException e) { |
|
128 |
+ throw new RuntimeException(e); |
|
129 |
+ } |
|
126 | 130 |
} |
127 | 131 |
|
128 | 132 |
/** |
... | ... | @@ -133,7 +137,7 @@ |
133 | 137 |
* @throws IOException |
134 | 138 |
* @see #buildGitRepository(String, String) |
135 | 139 |
*/ |
136 |
- public static Repository buildGitRepository(Project project) throws IOException { |
|
140 |
+ public static Repository buildGitRepository(Project project) { |
|
137 | 141 |
return buildGitRepository(project.owner, project.name); |
138 | 142 |
} |
139 | 143 |
|
--- app/playRepository/RepositoryService.java
+++ app/playRepository/RepositoryService.java
... | ... | @@ -1,20 +1,28 @@ |
1 | 1 |
package playRepository; |
2 | 2 |
|
3 |
+import actors.CommitCheckActor; |
|
3 | 4 |
import actors.ConflictCheckActor; |
4 | 5 |
import akka.actor.Props; |
5 | 6 |
import controllers.ProjectApp; |
6 | 7 |
import controllers.UserApp; |
8 |
+import models.CommitCheckMessage; |
|
7 | 9 |
import models.ConflictCheckMessage; |
8 | 10 |
import models.Project; |
9 | 11 |
import models.User; |
10 | 12 |
|
13 |
+import org.apache.commons.lang.StringUtils; |
|
11 | 14 |
import org.codehaus.jackson.node.ObjectNode; |
12 | 15 |
import org.eclipse.jgit.api.errors.GitAPIException; |
13 | 16 |
import org.eclipse.jgit.api.errors.NoHeadException; |
14 | 17 |
import org.eclipse.jgit.errors.AmbiguousObjectException; |
15 | 18 |
import org.eclipse.jgit.errors.IncorrectObjectTypeException; |
16 | 19 |
import org.eclipse.jgit.errors.MissingObjectException; |
20 |
+import org.eclipse.jgit.lib.Constants; |
|
21 |
+import org.eclipse.jgit.lib.ObjectId; |
|
17 | 22 |
import org.eclipse.jgit.lib.Repository; |
23 |
+import org.eclipse.jgit.revwalk.RevCommit; |
|
24 |
+import org.eclipse.jgit.revwalk.RevWalk; |
|
25 |
+import org.eclipse.jgit.revwalk.filter.CommitTimeRevFilter; |
|
18 | 26 |
import org.eclipse.jgit.transport.PacketLineOut; |
19 | 27 |
import org.eclipse.jgit.transport.PostReceiveHook; |
20 | 28 |
import org.eclipse.jgit.transport.ReceiveCommand; |
... | ... | @@ -35,15 +43,8 @@ |
35 | 43 |
import javax.servlet.ServletContext; |
36 | 44 |
import javax.servlet.ServletException; |
37 | 45 |
import java.io.*; |
38 |
-import java.util.Collection; |
|
39 |
-import java.util.Date; |
|
40 |
-import java.util.Enumeration; |
|
41 |
-import java.util.HashMap; |
|
42 |
-import java.util.HashSet; |
|
43 |
-import java.util.Map; |
|
44 |
-import java.util.Set; |
|
45 |
-import java.util.ArrayList; |
|
46 |
-import java.util.List; |
|
46 |
+ |
|
47 |
+import java.util.*; |
|
47 | 48 |
|
48 | 49 |
/** |
49 | 50 |
* 저장소 관련 서비스를 제공하는 클래스 |
... | ... | @@ -382,6 +383,12 @@ |
382 | 383 |
public void onPostReceive(ReceivePack receivePack, Collection<ReceiveCommand> commands) { |
383 | 384 |
updateLastPushedDate(); |
384 | 385 |
conflictCheck(commands); |
386 |
+ commitCheck(commands); |
|
387 |
+ } |
|
388 |
+ |
|
389 |
+ private void commitCheck(Collection<ReceiveCommand> commands) { |
|
390 |
+ CommitCheckMessage message = new CommitCheckMessage(commands, project); |
|
391 |
+ Akka.system().actorOf(new Props(CommitCheckActor.class)).tell(message, null); |
|
385 | 392 |
} |
386 | 393 |
|
387 | 394 |
/* |
--- app/views/issue/view.scala.html
+++ app/views/issue/view.scala.html
... | ... | @@ -29,8 +29,8 @@ |
29 | 29 |
} |
30 | 30 |
|
31 | 31 |
@linkToUser(loginId: String, loginName: String, showAvatar: Boolean = true) = { |
32 |
- @loginId match { |
|
33 |
- case (loginId: String) => { |
|
32 |
+ @loginId match { |
|
33 |
+ case (loginId: String) => { |
|
34 | 34 |
@if(showAvatar){ @avatarByLoginId(loginId, loginName) } |
35 | 35 |
<a href="@routes.UserApp.userInfo(loginId)" class="usf-group" data-toggle="tooltip" data-placement="top" title="@loginName"> |
36 | 36 |
<strong>@loginId</strong> |
... | ... | @@ -49,7 +49,7 @@ |
49 | 49 |
} |
50 | 50 |
} |
51 | 51 |
@isAuthorComment(commentId: String) = @{ |
52 |
- if(commentId == UserApp.currentUser().loginId) {"author"} |
|
52 |
+ if(commentId == UserApp.currentUser().loginId) {"author"} |
|
53 | 53 |
} |
54 | 54 |
|
55 | 55 |
@getissueStateIcons(state: String) = @{ |
... | ... | @@ -101,11 +101,11 @@ |
101 | 101 |
<div class="content markdown-wrap markdown-before" markdown="true">@issue.body</div> |
102 | 102 |
<div class="attachments" data-resourceType="@ResourceType.ISSUE_POST" data-resourceId="@issue.id"></div> |
103 | 103 |
</div> |
104 |
- |
|
104 |
+ |
|
105 | 105 |
<div class="span3 mb20"> |
106 | 106 |
<div class="author-info"> |
107 | 107 |
<form id="issueUpdateForm" action="@routes.IssueApp.massUpdate(project.owner, project.name)" method="post" class="frm-wrap"> |
108 |
- <input type="hidden" name="issues[0].id" value="@issue.id" /> |
|
108 |
+ <input type="hidden" name="issues[0].id" value="@issue.id" /> |
|
109 | 109 |
|
110 | 110 |
@**<!-- author -->**@ |
111 | 111 |
<dl class="author"> |
... | ... | @@ -125,7 +125,7 @@ |
125 | 125 |
</dd> |
126 | 126 |
</dl> |
127 | 127 |
@**<!-- // -->**@ |
128 |
- |
|
128 |
+ |
|
129 | 129 |
@**<!-- state -->**@ |
130 | 130 |
<!-- |
131 | 131 |
<dl> |
... | ... | @@ -144,7 +144,7 @@ |
144 | 144 |
</dd> |
145 | 145 |
</dl> |
146 | 146 |
--> |
147 |
- |
|
147 |
+ |
|
148 | 148 |
@**<!-- assignee -->**@ |
149 | 149 |
<dl> |
150 | 150 |
<dt>@Messages("issue.assignee")</dt> |
... | ... | @@ -191,7 +191,7 @@ |
191 | 191 |
<a href="@routes.UserApp.userInfo(issue.assignee.user.loginId)" class="usf-group"> |
192 | 192 |
<span class="avatar-wrap smaller"> |
193 | 193 |
<img src="@User.findByLoginId(issue.assignee.user.loginId).avatarUrl" width="20" height="20"> |
194 |
- </span> |
|
194 |
+ </span> |
|
195 | 195 |
<strong class="name">@issue.assigneeName</strong> |
196 | 196 |
<span class="loginid"> <strong>@{"@"}</strong>@issue.assignee.user.loginId</span> |
197 | 197 |
</a> |
... | ... | @@ -240,10 +240,10 @@ |
240 | 240 |
</dd> |
241 | 241 |
</dl> |
242 | 242 |
@**<!-- // -->**@ |
243 |
- |
|
243 |
+ |
|
244 | 244 |
</form> |
245 | 245 |
</div> |
246 |
- </div> |
|
246 |
+ </div> |
|
247 | 247 |
</div> |
248 | 248 |
|
249 | 249 |
<div class="board-footer board-actrow"> |
... | ... | @@ -258,8 +258,9 @@ |
258 | 258 |
@if(isAllowed(UserApp.currentUser(), issue.asResource(), Operation.UPDATE)) { |
259 | 259 |
<a href="@routes.IssueApp.editIssueForm(project.owner, project.name, issue.getNumber)" class="ybtn">@Messages("button.edit")</a> |
260 | 260 |
} |
261 |
- |
|
261 |
+ |
|
262 | 262 |
<button id="watch-button" type="button" class="ybtn" data-toggle="button" data-watching="@if(issue.getWatchers.contains(UserApp.currentUser())){true}else{false}"> |
263 |
+ |
|
263 | 264 |
@if(issue.getWatchers.contains(UserApp.currentUser())) { |
264 | 265 |
@Messages("project.unwatch") |
265 | 266 |
} else { |
... | ... | @@ -282,7 +283,7 @@ |
282 | 283 |
<a href="@routes.UserApp.userInfo(comment.authorLoginId)" class="avatar-wrap" data-toggle="tooltip" data-placement="top" title="@comment.authorName"> |
283 | 284 |
<img src="@User.findByLoginId(comment.authorLoginId).avatarUrl" width="32" height="32" alt="@comment.authorLoginId"> |
284 | 285 |
</a> |
285 |
- </div> |
|
286 |
+ </div> |
|
286 | 287 |
<div class="media-body"> |
287 | 288 |
<div class="meta-info"> |
288 | 289 |
<span class="comment_author pull-left"> |
... | ... | @@ -297,9 +298,9 @@ |
297 | 298 |
</span> |
298 | 299 |
} |
299 | 300 |
</div> |
300 |
- |
|
301 |
+ |
|
301 | 302 |
<div class="comment-body markdown-wrap markdown-before" markdown="true">@comment.contents</div> |
302 |
- |
|
303 |
+ |
|
303 | 304 |
<div class="attachments pull-right" data-resourceType="@ResourceType.ISSUE_COMMENT" data-resourceId="@comment.id"></div> |
304 | 305 |
</div> |
305 | 306 |
</li> |
... | ... | @@ -316,6 +317,9 @@ |
316 | 317 |
<span class="state changed"><i class="yobicon-ftpaccounts"></i> @Messages("issue.state.assigned")</span> |
317 | 318 |
@Html(Messages(assginedMesssage(event.newValue, user), linkToUser(user.loginId, user.name), linkToUser(event.newValue,user.name, true))) |
318 | 319 |
} |
320 |
+ case EventType.ISSUE_REFERRED => { |
|
321 |
+ @Html(event.newValue) by @linkToUser(user.loginId, user.name) |
|
322 |
+ } |
|
319 | 323 |
case _ => { |
320 | 324 |
@event.newValue by @linkToUser(user.loginId, user.name) |
321 | 325 |
} |
+++ conf/evolutions/default/36.sql
... | ... | @@ -0,0 +1,9 @@ |
1 | +# --- !Ups | |
2 | + | |
3 | +ALTER TABLE issue_event DROP CONSTRAINT IF EXISTS ck_issue_event_event_type; | |
4 | +ALTER TABLE issue_event ADD constraint ck_issue_event_event_type check (event_type in ('NEW_ISSUE','NEW_POSTING','ISSUE_ASSIGNEE_CHANGED','ISSUE_STATE_CHANGED','NEW_COMMENT','NEW_PULL_REQUEST','NEW_SIMPLE_COMMENT','PULL_REQUEST_STATE_CHANGED', 'ISSUE_REFERRED')); | |
5 | + | |
6 | +# --- !Downs | |
7 | + | |
8 | +ALTER TABLE issue_event DROP CONSTRAINT IF EXISTS ck_issue_event_event_type; | |
9 | +ALTER TABLE issue_event ADD CONSTRAINT ck_issue_event_event_type check (event_type in ('NEW_ISSUE','NEW_POSTING','ISSUE_ASSIGNEE_CHANGED','ISSUE_STATE_CHANGED','NEW_COMMENT','NEW_PULL_REQUEST','NEW_SIMPLE_COMMENT','PULL_REQUEST_STATE_CHANGED'));(No newline at end of file) |
--- conf/messages.en
+++ conf/messages.en
... | ... | @@ -20,7 +20,7 @@ |
20 | 20 |
# - user |
21 | 21 |
# - userinfo |
22 | 22 |
# - validation |
23 |
-# |
|
23 |
+# |
|
24 | 24 |
app.description = Make it better and simpler! |
25 | 25 |
app.name = Yobi |
26 | 26 |
app.restart.notice = The server needs to be restarted. |
... | ... | @@ -154,6 +154,8 @@ |
154 | 154 |
issue.event.closed = {0} closed this issue |
155 | 155 |
issue.event.open = {0} reopened this issue |
156 | 156 |
issue.event.unassigned = {0} unassinged this issue |
157 |
+issue.event.referred.from.pullrequest = {0} referred this issue in a pullrequest, <a href="{3}">from {1} to {2}</a>. |
|
158 |
+issue.event.referred.from.commit = {0} referred this issue in a commit, <a href="{2}">{1}</a>. |
|
157 | 159 |
issue.is.empty = There is no Issue. |
158 | 160 |
issue.list.all = All Issues |
159 | 161 |
issue.list.assignedToMe = Assigned to me |
... | ... | @@ -228,6 +230,7 @@ |
228 | 230 |
issue.update.milestone = Update milestone |
229 | 231 |
issue.update.state = Update state |
230 | 232 |
issue.watch.start = You will receive notifications of this issue |
233 |
+<<<<<<< HEAD |
|
231 | 234 |
label = Label |
232 | 235 |
label.add = Add Label |
233 | 236 |
label.addNewCategory = Add new category |
--- conf/messages.ko
+++ conf/messages.ko
... | ... | @@ -20,7 +20,7 @@ |
20 | 20 |
# - user |
21 | 21 |
# - userinfo |
22 | 22 |
# - validation |
23 |
-# |
|
23 |
+# |
|
24 | 24 |
app.description = Make it better and simpler! |
25 | 25 |
app.name = Yobi |
26 | 26 |
app.restart.notice = 서버를 재시작해야합니다. |
... | ... | @@ -154,6 +154,8 @@ |
154 | 154 |
issue.event.closed = {0}님이 이 이슈를 닫았습니다. |
155 | 155 |
issue.event.open = {0}님이 이 이슈를 다시 열었습니다. |
156 | 156 |
issue.event.unassigned = {0}님이 이 이슈의 담당자를 "없음"으로 설정하였습니다. |
157 |
+issue.event.referred.from.pullrequest = {0}님이 <a href="{3}">{1}에서 {2}로 보내는 코드</a>에서 이 이슈를 언급했습니다. |
|
158 |
+issue.event.referred.from.commit = {0}님이 <a href="{2}">{1}</a>에서 이 이슈를 언급했습니다. |
|
157 | 159 |
issue.is.empty = 등록된 이슈가 없습니다. |
158 | 160 |
issue.list.all = 전체 이슈 |
159 | 161 |
issue.list.assignedToMe = 나에게 할당된 이슈 |
... | ... | @@ -228,6 +230,7 @@ |
228 | 230 |
issue.update.milestone = 마일스톤 변경 |
229 | 231 |
issue.update.state = 상태 변경 |
230 | 232 |
issue.watch.start = 이제 이 이슈에 관한 알림을 받습니다 |
233 |
+<<<<<<< HEAD |
|
231 | 234 |
label = 라벨 |
232 | 235 |
label.add = 라벨 추가 |
233 | 236 |
label.addNewCategory = 새 분류 추가 |
+++ test/models/PullRequestTest.java
... | ... | @@ -0,0 +1,33 @@ |
1 | +package models; | |
2 | + | |
3 | +import org.junit.Test; | |
4 | + | |
5 | +import java.util.ArrayList; | |
6 | +import java.util.List; | |
7 | +import java.util.regex.Matcher; | |
8 | +import java.util.regex.Pattern; | |
9 | + | |
10 | +import static org.fest.assertions.Assertions.assertThat; | |
11 | + | |
12 | +/** | |
13 | + * @author Keesun Baik | |
14 | + */ | |
15 | +public class PullRequestTest { | |
16 | + | |
17 | + @Test | |
18 | + public void addIssueEvent() { | |
19 | + // Given | |
20 | + Pattern issuePattern = Pattern.compile("#\\d+"); | |
21 | + | |
22 | + // When | |
23 | + Matcher m = issuePattern.matcher("blah blah #12, sdl #13 sldkfjsd"); | |
24 | + | |
25 | + // Then | |
26 | + List<String> numberTexts = new ArrayList<>(); | |
27 | + while(m.find()) { | |
28 | + numberTexts.add(m.group()); | |
29 | + } | |
30 | + assertThat(numberTexts.size()).isEqualTo(2); | |
31 | + } | |
32 | + | |
33 | +} |
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?