Wonjun Hong Wonjun Hong 2017-08-21
Webhook: Add webhook support for Slack
When
---
- new pull request
- pull request state changed
- pull request merged
- pull request commit changed
- issue body changed
- issue moved
- assignee changed
- new comment

And..
---
- Provide option for commit only
@d179aaa9c3963e5c8b5fe5e712abfe08b5dae86a
app/controllers/ProjectApp.java
--- app/controllers/ProjectApp.java
+++ app/controllers/ProjectApp.java
@@ -1254,7 +1254,8 @@
 
         Webhook.create(project.id,
                         addWebhookForm.field("payloadUrl").value(),
-                        addWebhookForm.field("secret").value());
+                        addWebhookForm.field("secret").value(),
+                Boolean.valueOf(addWebhookForm.field("gitPushOnly").value()));
 
         return redirect(routes.ProjectApp.webhooks(project.owner, project.name));
     }
app/models/NotificationEvent.java
--- app/models/NotificationEvent.java
+++ app/models/NotificationEvent.java
@@ -428,6 +428,7 @@
         notiEvent.oldValue = null;
         notiEvent.newValue = pullRequest.body;
         NotificationEvent.add(notiEvent);
+
         return notiEvent;
     }
 
@@ -484,6 +485,55 @@
         return resourceId;
     }
 
+    private static void webhookRequest(EventType eventTypes, PullRequest pullRequest, Boolean gitPushOnly) {
+        List<Webhook> webhookList = Webhook.findByProject(pullRequest.toProjectId);
+        for (Webhook webhook : webhookList) {
+            if (gitPushOnly == webhook.gitPushOnly) {
+                // Send push event via webhook payload URLs.
+                webhook.sendRequestToPayloadUrl(eventTypes, UserApp.currentUser(), pullRequest);
+            }
+        }
+    }
+
+    private static void webhookRequest(EventType eventTypes, PullRequest pullRequest, PullRequestReviewAction reviewAction, Boolean gitPushOnly) {
+        List<Webhook> webhookList = Webhook.findByProject(pullRequest.toProject.id);
+        for (Webhook webhook : webhookList) {
+            if (gitPushOnly == webhook.gitPushOnly) {
+                // Send push event via webhook payload URLs.
+                webhook.sendRequestToPayloadUrl(eventTypes, UserApp.currentUser(), pullRequest, reviewAction);
+            }
+        }
+    }
+
+    private static void webhookRequest(EventType eventTypes, Issue issue, Boolean gitPushOnly) {
+        List<Webhook> webhookList = Webhook.findByProject(issue.project.id);
+        for (Webhook webhook : webhookList) {
+            if (gitPushOnly == webhook.gitPushOnly) {
+                // Send push event via webhook payload URLs.
+                webhook.sendRequestToPayloadUrl(eventTypes, UserApp.currentUser(), issue);
+            }
+        }
+    }
+
+    private static void webhookRequest(EventType eventTypes, Comment comment, Boolean gitPushOnly) {
+        List<Webhook> webhookList = Webhook.findByProject(comment.projectId);
+        for (Webhook webhook : webhookList) {
+            if (gitPushOnly == webhook.gitPushOnly) {
+                // Send push event via webhook payload URLs.
+                webhook.sendRequestToPayloadUrl(eventTypes, UserApp.currentUser(), comment);
+            }
+        }
+    }
+
+    private static void webhookRequest(Project project, List<RevCommit> commits, List<String> refNames, User sender, String title, Boolean gitPushOnly) {
+        List<Webhook> webhookList = Webhook.findByProject(project.id);
+        for (Webhook webhook : webhookList) {
+            if (gitPushOnly == webhook.gitPushOnly) {
+                // Send push event via webhook payload URLs.
+                webhook.sendRequestToPayloadUrl(commits, refNames, sender, title);
+            }
+        }
+    }
 
     /**
      * @see {@link models.PullRequest#merge(models.PullRequestEventMessage)}
@@ -508,6 +558,9 @@
         notiEvent.oldValue = null;
         notiEvent.newValue = newPullRequestCommitChangedMessage(pullRequest);
         NotificationEvent.add(notiEvent);
+
+        webhookRequest(PULL_REQUEST_COMMIT_CHANGED, pullRequest, false);
+
         return notiEvent;
     }
 
@@ -532,6 +585,8 @@
      * @see {@link actors.PullRequestActor#processPullRequestMerging(models.PullRequestEventMessage, models.PullRequest)}
      */
     public static NotificationEvent afterMerge(User sender, PullRequest pullRequest, State state) {
+        webhookRequest(PULL_REQUEST_MERGED, pullRequest, false);
+
         NotificationEvent notiEvent = createFrom(sender, pullRequest);
         notiEvent.title = formatReplyTitle(pullRequest);
         notiEvent.receivers = state == State.MERGED ? getReceiversWithRelatedAuthors(sender, pullRequest) : getReceivers(sender, pullRequest);
@@ -563,14 +618,17 @@
     }
 
     public static NotificationEvent afterNewPullRequest(PullRequest pullRequest) {
+        webhookRequest(NEW_PULL_REQUEST, pullRequest, false);
         return afterNewPullRequest(UserApp.currentUser(), pullRequest);
     }
 
     public static NotificationEvent afterPullRequestUpdated(PullRequest pullRequest, State oldState, State newState) {
+        webhookRequest(PULL_REQUEST_STATE_CHANGED, pullRequest, false);
         return afterPullRequestUpdated(UserApp.currentUser(), pullRequest, oldState, newState);
     }
 
     public static void afterNewComment(Comment comment) {
+        webhookRequest(NEW_COMMENT, comment, false);
         NotificationEvent.add(forNewComment(comment, UserApp.currentUser()));
     }
 
@@ -618,15 +676,15 @@
     }
 
     public static NotificationEvent afterStateChanged(State oldState, Issue issue) {
+        webhookRequest(ISSUE_STATE_CHANGED, issue, false);
+
         NotificationEvent notiEvent = createFromCurrentUser(issue);
         notiEvent.title = formatReplyTitle(issue);
         notiEvent.receivers = getReceivers(issue);
         notiEvent.eventType = ISSUE_STATE_CHANGED;
         notiEvent.oldValue = oldState != null ? oldState.state() : null;
         notiEvent.newValue = issue.state.state();
-
         NotificationEvent.add(notiEvent);
-
         return notiEvent;
     }
 
@@ -666,6 +724,8 @@
     }
 
     public static NotificationEvent afterAssigneeChanged(User oldAssignee, Issue issue) {
+        webhookRequest(ISSUE_ASSIGNEE_CHANGED, issue, false);
+
         NotificationEvent notiEvent = createFromCurrentUser(issue);
 
         Set<User> receivers = getReceivers(issue);
@@ -690,6 +750,7 @@
 
     public static void afterNewIssue(Issue issue) {
         NotificationEvent.add(forNewIssue(issue, UserApp.currentUser()));
+        webhookRequest(NEW_ISSUE, issue, false);
     }
 
     public static NotificationEvent forNewIssue(Issue issue, User author) {
@@ -703,6 +764,8 @@
     }
 
     public static NotificationEvent afterIssueBodyChanged(String oldBody, Issue issue) {
+        webhookRequest(ISSUE_BODY_CHANGED, issue, false);
+
         NotificationEvent notiEvent = createFromCurrentUser(issue);
         notiEvent.title = formatReplyTitle(issue);
         notiEvent.receivers = getReceiversForIssueBodyChanged(oldBody, issue);
@@ -716,6 +779,8 @@
     }
 
     public static NotificationEvent afterIssueMoved(Project previous, Issue issue) {
+        webhookRequest(ISSUE_MOVED, issue, false);
+
         NotificationEvent notiEvent = createFromCurrentUser(issue);
         notiEvent.title = formatReplyTitle(issue);
         notiEvent.receivers = getReceivers(issue);
@@ -866,15 +931,12 @@
         notiEvent.resourceId = project.asResource().getId();
         NotificationEvent.add(notiEvent);
 
-        List<Webhook> webhookList = Webhook.findByProject(project.id);
-        for (Webhook webhook : webhookList) {
-            // Send push event via webhook payload URLs.
-            String[] eventTypes = {"push"};
-            webhook.sendRequestToPayloadUrl(eventTypes, commits, refNames, sender, title);
-        }
+        webhookRequest(project, commits, refNames, sender, title, true);
     }
 
     public static NotificationEvent afterReviewed(PullRequest pullRequest, PullRequestReviewAction reviewAction) {
+        webhookRequest(EventType.PULL_REQUEST_REVIEW_STATE_CHANGED, pullRequest, reviewAction, false);
+
         String title = formatReplyTitle(pullRequest);
         Resource resource = pullRequest.asResource();
         Set<User> receivers = pullRequest.getWatchers();
@@ -1201,6 +1263,7 @@
     }
 
     public static void afterCommentUpdated(Comment comment) {
+        webhookRequest(COMMENT_UPDATED, comment, false);
         NotificationEvent.add(forUpdatedComment(comment, UserApp.currentUser()));
     }
 }
app/models/Webhook.java
--- app/models/Webhook.java
+++ app/models/Webhook.java
@@ -23,14 +23,18 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
+import models.enumeration.EventType;
+import models.enumeration.PullRequestReviewAction;
 import models.enumeration.ResourceType;
 import models.resource.GlobalResource;
 import models.resource.Resource;
 import models.resource.ResourceConvertible;
 import org.eclipse.jgit.revwalk.RevCommit;
 import play.Logger;
+import play.api.i18n.Lang;
 import play.data.validation.Constraints.Required;
 import play.db.ebean.Model;
+import play.i18n.Messages;
 import play.libs.F.Function;
 import play.libs.Json;
 import play.libs.ws.WS;
@@ -82,24 +86,32 @@
     public String secret;
 
     /**
+     * Type of webhook (true = git only push, false = all cases)
+     */
+    public Boolean gitPushOnly;
+
+    /**
      * Payload URL of webhook.
      */
     public Date createdAt;
+
 
     /**
      * Construct a webhook by the given {@code payloadUrl} and {@code secret}.
      *
      * @param projectId the ID of project which will have this webhook
      * @param payloadUrl the payload URL for this webhook
+     * @param gitPushOnly type of webhook (true = git only push, false = all cases)
      * @param secret the secret token for server identity
      */
-    public Webhook(Long projectId, String payloadUrl, String secret) {
+    public Webhook(Long projectId, String payloadUrl, String secret, Boolean gitPushOnly) {
         if (secret == null) {
             secret = "";
         }
         this.project = Project.find.byId(projectId);
         this.payloadUrl = payloadUrl;
         this.secret = secret;
+        this.gitPushOnly = gitPushOnly;
         this.createdAt = new Date();
     }
 
@@ -130,9 +142,9 @@
         return find.where().eq("project.id", projectId).findList();
     }
 
-    public static void create(Long projectId, String payloadUrl, String secret) {
+    public static void create(Long projectId, String payloadUrl, String secret, Boolean gitPushOnly) {
         if (!payloadUrl.isEmpty()) {
-            Webhook webhook = new Webhook(projectId, payloadUrl, secret);
+            Webhook webhook = new Webhook(projectId, payloadUrl, secret, gitPushOnly);
             webhook.save();
         }
         // TODO : Raise appropriate error when required field is empty
@@ -148,16 +160,14 @@
                 .eq("project.id", projectId)
                 .findUnique();
     }
-    
-    public void sendRequestToPayloadUrl(String[] eventTypes, List<RevCommit> commits, List<String> refNames, User sender, String title) {
-        String requestBodyString = buildRequestBody(eventTypes, commits, refNames, sender, title);
 
+    private void sendRequest(String payload) {
         try {
             WSRequestHolder requestHolder = WS.url(this.payloadUrl);
             requestHolder
                     .setHeader("Content-Type", "application/json")
                     .setHeader("User-Agent", "Yobi-Hookshot")
-                    .post(requestBodyString)
+                    .post(payload)
                     .map(
                             new Function<WSResponse, Integer>() {
                                 public Integer apply(WSResponse response) {
@@ -166,7 +176,7 @@
                                     if (statusCode < 200 || statusCode >= 300) {
                                         // Unsuccessful status code - log some information in server.
                                         Logger.info("[Webhook] Request responded code " + Integer.toString(statusCode) + ": " + statusText);
-                                        Logger.info("[Webhook] Request payload: " + requestBodyString);
+                                        Logger.info("[Webhook] Request payload: " + payload);
                                     }
                                     return 0;
                                 }
@@ -177,48 +187,168 @@
             Logger.info("[Webhook] Request failed at given payload URL: " + this.payloadUrl);
         }
     }
+    
+    public void sendRequestToPayloadUrl(List<RevCommit> commits, List<String> refNames, User sender, String title) {
+        String requestBodyString = buildRequestBody(commits, refNames, sender, title);
+        sendRequest(requestBodyString);
+    }
 
-    private String buildRequestBody(String[] eventTypes, List<RevCommit> commits, List<String> refNames, User sender, String title) {
+    public void sendRequestToPayloadUrl(EventType eventType, User sender, Issue eventIssue) {
+        String requestBodyString = buildRequestBody(eventType, sender, eventIssue);
+        sendRequest(requestBodyString);
+    }
+
+    public void sendRequestToPayloadUrl(EventType eventType, User sender, PullRequest eventPullRequest) {
+        String requestBodyString = buildRequestBody(eventType, sender, eventPullRequest);
+        sendRequest(requestBodyString);
+    }
+
+    public void sendRequestToPayloadUrl(EventType eventType, User sender, PullRequest eventPullRequest, PullRequestReviewAction reviewAction) {
+        String requestBodyString = buildRequestBody(eventType, sender, eventPullRequest, reviewAction);
+        sendRequest(requestBodyString);
+    }
+
+    public void sendRequestToPayloadUrl(EventType eventType, User sender, Comment eventComment) {
+        String requestBodyString = buildRequestBody(eventType, sender, eventComment);
+        sendRequest(requestBodyString);
+    }
+
+    private String buildRequestBody(List<RevCommit> commits, List<String> refNames, User sender, String title) {
         ObjectNode requestBody = Json.newObject();
-        ObjectNode senderJSON = Json.newObject();
-        ObjectNode pusherJSON = Json.newObject();
-        ObjectNode repositoryJSON = Json.newObject();
         ObjectMapper mapper = new ObjectMapper();
-        ArrayNode eventTypesArray = mapper.createArrayNode();
-        ArrayNode refNamesArray = mapper.createArrayNode();
-        ArrayNode commitsArray = mapper.createArrayNode();
+        ArrayNode refNamesNodes = mapper.createArrayNode();
+        ArrayNode commitsNodes = mapper.createArrayNode();
 
         for (String refName : refNames) {
-            refNamesArray.add(refName);
+            refNamesNodes.add(refName);
         }
-        requestBody.put("ref", refNamesArray);
+        requestBody.put("ref", refNamesNodes);
 
         for (RevCommit commit : commits) {
-            commitsArray.add(buildJSONFromCommit(project, commit));
+            commitsNodes.add(buildJSONFromCommit(project, commit));
         }
-        requestBody.put("commits", commitsArray);
-        requestBody.put("head_commit", commitsArray.get(0));
+        requestBody.put("commits", commitsNodes);
+        requestBody.put("head_commit", commitsNodes.get(0));
 
-        senderJSON.put("login", sender.loginId);
-        senderJSON.put("id", sender.id);
-        senderJSON.put("avatar_url", sender.avatarUrl());
-        senderJSON.put("type", "User");
-        senderJSON.put("site_admin", sender.isSiteManager());
-        requestBody.put("sender", senderJSON);
-
-        pusherJSON.put("name", sender.name);
-        pusherJSON.put("email", sender.email);
-        requestBody.put("pusher", pusherJSON);
-
-        repositoryJSON.put("id", project.id);
-        repositoryJSON.put("name", project.name);
-        repositoryJSON.put("owner", project.owner);
-        repositoryJSON.put("html_url", RouteUtil.getUrl(project));
-        repositoryJSON.put("overview", project.overview);   // Description.
-        repositoryJSON.put("private", project.isPrivate());
-        requestBody.put("repository", repositoryJSON);
+        requestBody.put("sender", buildSenderJSON(sender));
+        requestBody.put("pusher", buildPusherJSON(sender));
+        requestBody.put("repository", buildRepositoryJSON());
 
         return Json.stringify(requestBody);
+    }
+
+    private String buildRequestBody(EventType eventType, User sender, PullRequest eventPullRequest) {
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayNode detailFields = mapper.createArrayNode();
+        ArrayNode attachments = mapper.createArrayNode();
+
+        String requestMessage = "[" + project.name + "] "+ sender.name + " ";
+        switch (eventType) {
+            case NEW_PULL_REQUEST:
+                requestMessage += Messages.get(Lang.defaultLang(), "notification.type.new.pullrequest");
+                break;
+            case PULL_REQUEST_STATE_CHANGED:
+                requestMessage += Messages.get(Lang.defaultLang(), "notification.type.pullrequest.state.changed");
+                break;
+            case PULL_REQUEST_MERGED:
+                requestMessage += Messages.get(Lang.defaultLang(), "notification.type.pullrequest.merged");
+                break;
+            case PULL_REQUEST_COMMIT_CHANGED:
+                requestMessage += Messages.get(Lang.defaultLang(), "notification.type.pullrequest.commit.changed");
+                break;
+        }
+        requestMessage += " <" + project.siteurl + "/pullRequest/" + eventPullRequest.number + "|#" + eventPullRequest.number + ": " + eventPullRequest.title + ">";
+
+        detailFields.add(buildTitleValueJSON(Messages.get(Lang.defaultLang(), "pullRequest.sender"), eventPullRequest.contributor.name, false));
+        detailFields.add(buildTitleValueJSON(Messages.get(Lang.defaultLang(), "pullRequest.from"), eventPullRequest.fromBranch, true));
+        detailFields.add(buildTitleValueJSON(Messages.get(Lang.defaultLang(), "pullRequest.to"), eventPullRequest.toBranch,true));
+        attachments.add(buildAttachmentJSON(eventPullRequest.body, detailFields));
+
+        return Json.stringify(buildRequestJSON(requestMessage, attachments));
+    }
+
+    private String buildRequestBody(EventType eventType, User sender, PullRequest eventPullRequest, PullRequestReviewAction reviewAction) {
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayNode detailFields = mapper.createArrayNode();
+        ArrayNode attachments = mapper.createArrayNode();
+
+        String requestMessage = "[" + project.name + "] ";
+        switch (eventType) {
+            case PULL_REQUEST_REVIEW_STATE_CHANGED:
+                if (PullRequestReviewAction.DONE.equals(reviewAction)) {
+                    requestMessage += Messages.get(Lang.defaultLang(), "notification.pullrequest.reviewed", sender.name);
+                } else {
+                    requestMessage += Messages.get(Lang.defaultLang(), "notification.pullrequest.unreviewed", sender.name);;
+                }
+                break;
+        }
+        requestMessage += " <" + project.siteurl + "/pullRequest/" + eventPullRequest.number + "|#" + eventPullRequest.number + ": " + eventPullRequest.title + ">";
+
+        detailFields.add(buildTitleValueJSON(Messages.get(Lang.defaultLang(), "pullRequest.sender"), eventPullRequest.contributor.name, false));
+        detailFields.add(buildTitleValueJSON(Messages.get(Lang.defaultLang(), "pullRequest.from"), eventPullRequest.fromBranch, true));
+        detailFields.add(buildTitleValueJSON(Messages.get(Lang.defaultLang(), "pullRequest.to"), eventPullRequest.toBranch, true));
+        attachments.add(buildAttachmentJSON(eventPullRequest.body, detailFields));
+
+        return Json.stringify(buildRequestJSON(requestMessage, attachments));
+    }
+
+    private String buildRequestBody(EventType eventType, User sender, Issue eventIssue) {
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayNode detailFields = mapper.createArrayNode();
+        ArrayNode attachments = mapper.createArrayNode();
+
+        String requestMessage = "[" + project.name + "] "+ sender.name + " ";
+        switch (eventType) {
+            case NEW_ISSUE:
+                requestMessage += Messages.get(Lang.defaultLang(), "notification.type.new.issue");
+                break;
+            case ISSUE_STATE_CHANGED:
+                requestMessage += Messages.get(Lang.defaultLang(), "notification.type.issue.state.changed");
+                break;
+            case ISSUE_ASSIGNEE_CHANGED:
+                requestMessage += Messages.get(Lang.defaultLang(), "notification.type.issue.assignee.changed");
+                break;
+            case ISSUE_BODY_CHANGED:
+                requestMessage += Messages.get(Lang.defaultLang(), "notification.type.issue.body.changed");
+                break;
+            case ISSUE_MOVED:
+                requestMessage += Messages.get(Lang.defaultLang(), "notification.type.issue.moved");
+                break;
+        }
+        requestMessage += " <" + project.siteurl + "/issue/" + eventIssue.number + "|#" + eventIssue.number + ": " + eventIssue.title + ">";
+
+        detailFields.add(buildTitleValueJSON(Messages.get(Lang.defaultLang(), "issue.assignee"), eventIssue.assigneeName(), true));
+        detailFields.add(buildTitleValueJSON(Messages.get(Lang.defaultLang(), "issue.state"), eventIssue.state.toString(), true));
+        attachments.add(buildAttachmentJSON(eventIssue.body, detailFields));
+
+        return Json.stringify(buildRequestJSON(requestMessage, attachments));
+    }
+
+    private String buildRequestBody(EventType eventType, User sender, Comment eventComment) {
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayNode attachments = mapper.createArrayNode();
+
+        String requestMessage = "[" + project.name + "] "+ sender.name + " ";
+        switch (eventType) {
+            case NEW_COMMENT:
+                requestMessage += Messages.get(Lang.defaultLang(), "notification.type.new.comment");
+                break;
+            case COMMENT_UPDATED:
+                requestMessage += Messages.get(Lang.defaultLang(), "notification.type.comment.updated");
+                break;
+        }
+        switch (eventComment.asResource().getType()) {
+            case ISSUE_COMMENT:
+                requestMessage += " <" + project.siteurl + "/issue/" + eventComment.getParent().number + "#comment-" + eventComment.id + "|#" + eventComment.getParent().number + ": " + eventComment.getParent().title + ">";
+                break;
+            case NONISSUE_COMMENT:
+                requestMessage += " <" + project.siteurl + "/post/" + eventComment.getParent().number + "#comment-" + eventComment.id + "|#" + eventComment.getParent().number + ": " + eventComment.getParent().title + ">";
+                break;
+        }
+
+        attachments.add(buildAttachmentJSON(eventComment.contents, null));
+
+        return Json.stringify(buildRequestJSON(requestMessage, attachments));
     }
 
     private ObjectNode buildJSONFromCommit(Project project, RevCommit commit) {
@@ -248,6 +378,57 @@
         return commitJSON;
     }
 
+    private ObjectNode buildTitleValueJSON(String title, String value, Boolean shorten) {
+        ObjectNode outputJSON = Json.newObject();
+        outputJSON.put("title", title);
+        outputJSON.put("value", value);
+        outputJSON.put("short", shorten);
+        return outputJSON;
+    }
+
+    private ObjectNode buildAttachmentJSON(String text, ArrayNode detailFields) {
+        ObjectNode attachmentsJSON = Json.newObject();
+        attachmentsJSON.put("text", text);
+        attachmentsJSON.put("fields", detailFields);
+        return attachmentsJSON;
+    }
+
+    private ObjectNode buildRequestJSON(String requestMessage, ArrayNode attachments) {
+        ObjectNode requestBody = Json.newObject();
+        requestBody.put("text", requestMessage);
+        requestBody.put("username", "YonaBot");
+        requestBody.put("attachments", attachments);
+        return requestBody;
+    }
+
+    private ObjectNode buildSenderJSON(User sender) {
+        ObjectNode senderJSON = Json.newObject();
+        senderJSON.put("login", sender.loginId);
+        senderJSON.put("id", sender.id);
+        senderJSON.put("avatar_url", sender.avatarUrl());
+        senderJSON.put("type", "User");
+        senderJSON.put("site_admin", sender.isSiteManager());
+        return senderJSON;
+    }
+
+    private ObjectNode buildPusherJSON(User sender) {
+        ObjectNode pusherJSON = Json.newObject();
+        pusherJSON.put("name", sender.name);
+        pusherJSON.put("email", sender.email);
+        return pusherJSON;
+    }
+
+    private ObjectNode buildRepositoryJSON() {
+        ObjectNode repositoryJSON = Json.newObject();
+        repositoryJSON.put("id", project.id);
+        repositoryJSON.put("name", project.name);
+        repositoryJSON.put("owner", project.owner);
+        repositoryJSON.put("html_url", RouteUtil.getUrl(project));
+        repositoryJSON.put("overview", project.overview);   // Description.
+        repositoryJSON.put("private", project.isPrivate());
+        return repositoryJSON;
+    }
+
     /**
      * Remove this webhook from a project.
      *
app/views/project/webhooks.scala.html
--- app/views/project/webhooks.scala.html
+++ app/views/project/webhooks.scala.html
@@ -36,6 +36,8 @@
           <div>
             <input type="text" name="payloadUrl" class="input-webhook-payload" maxlength="2000" autocomplete="off" placeholder="@Messages("project.webhook.payloadUrl")">
             <input type="text" name="secret" class="input-webhook-secret" maxlength="250" autocomplete="off" placeholder="@Messages("project.webhook.secret")">
+            <label for="gitPushOnly">@Messages("project.webhook.gitPushOnly")</label>
+            <input type="checkbox" id="gitPushOnly" name="gitPushOnly" class="form-check-input">
           </div>
         </div>
         <button type="submit" class="ybtn ybtn-primary btn-submit">@Messages("project.webhook.add")</button>
 
conf/evolutions/default/17.sql (added)
+++ conf/evolutions/default/17.sql
@@ -0,0 +1,6 @@
+# --- !Ups
+ALTER TABLE webhook ADD COLUMN git_push_only tinyint(1) default 1;
+CREATE INDEX ix_webhook_git_push_only ON webhook (git_push_only);
+
+# --- !Downs
+ALTER TABLE webhook DROP COLUMN git_push_only;(파일 끝에 줄바꿈 문자 없음)
conf/messages
--- conf/messages
+++ conf/messages
@@ -675,12 +675,13 @@
 project.webhook = Webhooks
 project.webhook.add = Add webhook
 project.webhook.list.empty = No webhook exists.
-project.webhook.new = Create new webhook (Invoked by git push)
+project.webhook.new = Create new webhook
 project.webhook.payloadUrl = Payload URL
 project.webhook.payloadUrl.empty = Paylaod URL is required field.
 project.webhook.payloadUrl.tooLong = Given payload URL is too long. (Maximum 2000 Characters)
 project.webhook.secret = Secret token
 project.webhook.secret.tooLong = Given secret token is too long. (Maximum 250 Characters)
+project.webhook.gitPushOnly = Git push only
 project.you.are.not.watching = You are not watching {0} project.
 project.you.are.watching = You are watching {0} project.
 project.you.may.want.to.be.a.member = You can send a sign-up request for {0} project.
conf/messages.ko-KR
--- conf/messages.ko-KR
+++ conf/messages.ko-KR
@@ -440,7 +440,7 @@
 notification.type.issue.referred.from.pullrequest = 코드 주고받기에서의 이슈 언급
 notification.type.issue.state.changed = 이슈 상태 변경
 notification.type.member.enroll = 멤버 등록 요청
-notification.type.new.comment = 게시물과 이슈 새 댓글 등록
+notification.type.new.comment = 새 댓글 등록
 notification.type.new.commit = 새 커밋
 notification.type.new.issue = 새 이슈 등록
 notification.type.new.posting = 새 게시물 등록
@@ -675,7 +675,7 @@
 project.webhook = 웹후크
 project.webhook.add = 웹후크 추가
 project.webhook.list.empty = 등록된 웹후크가 없습니다.
-project.webhook.new = 새 웹후크 생성 (Git push 시에 실행됩니다)
+project.webhook.new = 새 웹후크 생성
 project.webhook.payloadUrl = 전송할 주소
 project.webhook.payloadUrl.empty = Paylaod URL 은 필수 필드입니다.
 project.webhook.payloadUrl.tooLong = Payload URL 이 너무 깁니다 (최대 2000글자)
Add a comment
List