From 24941a10464dc27eaebafda2a208fa827b74ff8d Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Wed, 16 May 2018 22:01:55 +0800
Subject: [PATCH] Add more webhooks support and refactor webhook templates
 directory (#3929)

* add more webhook support

* move hooks templates to standalone dir and add more webhooks ui

* fix tests

* update vendor checksum

* add more webhook support

* move hooks templates to standalone dir and add more webhooks ui

* fix tests

* update vendor checksum

* update vendor

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>

* load attributes when created release

* update comparsion doc
---
 docs/content/doc/features/comparison.en-us.md |   2 +-
 models/action.go                              |  20 ++
 models/issue_comment.go                       | 110 +++++++++--
 models/issue_milestone.go                     |  47 ++++-
 models/issue_milestone_test.go                |   2 +
 models/release.go                             |  22 ++-
 models/repo.go                                |  11 ++
 models/webhook.go                             | 112 +++++++----
 models/webhook_dingtalk.go                    | 137 +++++++++++++
 models/webhook_discord.go                     | 186 ++++++++++++++++++
 models/webhook_slack.go                       | 126 ++++++++++++
 models/webhook_test.go                        |   2 +-
 modules/auth/repo_form.go                     |  17 +-
 options/locale/locale_en-US.ini               |  10 +
 routers/api/v1/repo/issue_comment.go          |   5 +-
 routers/api/v1/utils/hook.go                  |  27 ++-
 routers/repo/issue.go                         |   5 +-
 routers/repo/webhook.go                       |  19 +-
 .../{hooks.tmpl => webhook/base.tmpl}         |   2 +-
 .../delete_modal.tmpl}                        |   0
 .../dingtalk.tmpl}                            |   2 +-
 .../discord.tmpl}                             |   2 +-
 .../{hook_gitea.tmpl => webhook/gitea.tmpl}   |   2 +-
 .../{hook_gogs.tmpl => webhook/gogs.tmpl}     |   2 +-
 .../history.tmpl}                             |   0
 .../{hook_list.tmpl => webhook/list.tmpl}     |   2 +-
 .../{hook_new.tmpl => webhook/new.tmpl}       |  12 +-
 .../settings.tmpl}                            |  52 ++++-
 .../{hook_slack.tmpl => webhook/slack.tmpl}   |   2 +-
 vendor/code.gitea.io/sdk/gitea/hook.go        | 122 ++++++++++++
 vendor/code.gitea.io/sdk/gitea/issue.go       |  28 ++-
 vendor/code.gitea.io/sdk/gitea/pull.go        |  34 ++--
 vendor/vendor.json                            |   6 +-
 33 files changed, 1010 insertions(+), 118 deletions(-)
 rename templates/repo/settings/{hooks.tmpl => webhook/base.tmpl} (82%)
 rename templates/repo/settings/{hook_delete_modal.tmpl => webhook/delete_modal.tmpl} (100%)
 rename templates/repo/settings/{hook_dingtalk.tmpl => webhook/dingtalk.tmpl} (91%)
 rename templates/repo/settings/{hook_discord.tmpl => webhook/discord.tmpl} (95%)
 rename templates/repo/settings/{hook_gitea.tmpl => webhook/gitea.tmpl} (94%)
 rename templates/repo/settings/{hook_gogs.tmpl => webhook/gogs.tmpl} (96%)
 rename templates/repo/settings/{hook_history.tmpl => webhook/history.tmpl} (100%)
 rename templates/repo/settings/{hook_list.tmpl => webhook/list.tmpl} (97%)
 rename templates/repo/settings/{hook_new.tmpl => webhook/new.tmpl} (77%)
 rename templates/repo/settings/{hook_settings.tmpl => webhook/settings.tmpl} (63%)
 rename templates/repo/settings/{hook_slack.tmpl => webhook/slack.tmpl} (96%)

diff --git a/docs/content/doc/features/comparison.en-us.md b/docs/content/doc/features/comparison.en-us.md
index c2b7b037c3..db0d3b5553 100644
--- a/docs/content/doc/features/comparison.en-us.md
+++ b/docs/content/doc/features/comparison.en-us.md
@@ -537,7 +537,7 @@ _Symbols used in table:_
     </tr>
     <tr>
       <td>Webhook support</td>
-      <td>⁄</td>
+      <td>✓</td>
       <td>✓</td>
       <td>✓</td>
       <td>✓</td>
diff --git a/models/action.go b/models/action.go
index 4f357cb2c5..c3ed9c7c02 100644
--- a/models/action.go
+++ b/models/action.go
@@ -618,6 +618,16 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
 	case ActionDeleteBranch: // Delete Branch
 		isHookEventPush = true
 
+		if err = PrepareWebhooks(repo, HookEventDelete, &api.DeletePayload{
+			Ref:        refName,
+			RefType:    "branch",
+			PusherType: api.PusherTypeUser,
+			Repo:       apiRepo,
+			Sender:     apiPusher,
+		}); err != nil {
+			return fmt.Errorf("PrepareWebhooks.(delete branch): %v", err)
+		}
+
 	case ActionPushTag: // Create
 		isHookEventPush = true
 
@@ -640,6 +650,16 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
 		}
 	case ActionDeleteTag: // Delete Tag
 		isHookEventPush = true
+
+		if err = PrepareWebhooks(repo, HookEventDelete, &api.DeletePayload{
+			Ref:        refName,
+			RefType:    "tag",
+			PusherType: api.PusherTypeUser,
+			Repo:       apiRepo,
+			Sender:     apiPusher,
+		}); err != nil {
+			return fmt.Errorf("PrepareWebhooks.(delete tag): %v", err)
+		}
 	}
 
 	if isHookEventPush {
diff --git a/models/issue_comment.go b/models/issue_comment.go
index 2c5875c29c..ad200934bc 100644
--- a/models/issue_comment.go
+++ b/models/issue_comment.go
@@ -83,9 +83,10 @@ const (
 type Comment struct {
 	ID              int64 `xorm:"pk autoincr"`
 	Type            CommentType
-	PosterID        int64 `xorm:"INDEX"`
-	Poster          *User `xorm:"-"`
-	IssueID         int64 `xorm:"INDEX"`
+	PosterID        int64  `xorm:"INDEX"`
+	Poster          *User  `xorm:"-"`
+	IssueID         int64  `xorm:"INDEX"`
+	Issue           *Issue `xorm:"-"`
 	LabelID         int64
 	Label           *Label `xorm:"-"`
 	OldMilestoneID  int64
@@ -116,6 +117,15 @@ type Comment struct {
 	ShowTag CommentTag `xorm:"-"`
 }
 
+// LoadIssue loads issue from database
+func (c *Comment) LoadIssue() (err error) {
+	if c.Issue != nil {
+		return nil
+	}
+	c.Issue, err = GetIssueByID(c.IssueID)
+	return
+}
+
 // AfterLoad is invoked from XORM after setting the values of all fields of this object.
 func (c *Comment) AfterLoad(session *xorm.Session) {
 	var err error
@@ -146,40 +156,40 @@ func (c *Comment) AfterDelete() {
 
 // HTMLURL formats a URL-string to the issue-comment
 func (c *Comment) HTMLURL() string {
-	issue, err := GetIssueByID(c.IssueID)
+	err := c.LoadIssue()
 	if err != nil { // Silently dropping errors :unamused:
-		log.Error(4, "GetIssueByID(%d): %v", c.IssueID, err)
+		log.Error(4, "LoadIssue(%d): %v", c.IssueID, err)
 		return ""
 	}
-	return fmt.Sprintf("%s#%s", issue.HTMLURL(), c.HashTag())
+	return fmt.Sprintf("%s#%s", c.Issue.HTMLURL(), c.HashTag())
 }
 
 // IssueURL formats a URL-string to the issue
 func (c *Comment) IssueURL() string {
-	issue, err := GetIssueByID(c.IssueID)
+	err := c.LoadIssue()
 	if err != nil { // Silently dropping errors :unamused:
-		log.Error(4, "GetIssueByID(%d): %v", c.IssueID, err)
+		log.Error(4, "LoadIssue(%d): %v", c.IssueID, err)
 		return ""
 	}
 
-	if issue.IsPull {
+	if c.Issue.IsPull {
 		return ""
 	}
-	return issue.HTMLURL()
+	return c.Issue.HTMLURL()
 }
 
 // PRURL formats a URL-string to the pull-request
 func (c *Comment) PRURL() string {
-	issue, err := GetIssueByID(c.IssueID)
+	err := c.LoadIssue()
 	if err != nil { // Silently dropping errors :unamused:
-		log.Error(4, "GetIssueByID(%d): %v", c.IssueID, err)
+		log.Error(4, "LoadIssue(%d): %v", c.IssueID, err)
 		return ""
 	}
 
-	if !issue.IsPull {
+	if !c.Issue.IsPull {
 		return ""
 	}
-	return issue.HTMLURL()
+	return c.Issue.HTMLURL()
 }
 
 // APIFormat converts a Comment to the api.Comment format
@@ -196,9 +206,14 @@ func (c *Comment) APIFormat() *api.Comment {
 	}
 }
 
+// CommentHashTag returns unique hash tag for comment id.
+func CommentHashTag(id int64) string {
+	return fmt.Sprintf("issuecomment-%d", id)
+}
+
 // HashTag returns unique hash tag for comment.
 func (c *Comment) HashTag() string {
-	return "issuecomment-" + com.ToStr(c.ID)
+	return CommentHashTag(c.ID)
 }
 
 // EventTag returns unique event hash tag for comment.
@@ -576,7 +591,7 @@ func CreateComment(opts *CreateCommentOptions) (comment *Comment, err error) {
 
 // CreateIssueComment creates a plain issue comment.
 func CreateIssueComment(doer *User, repo *Repository, issue *Issue, content string, attachments []string) (*Comment, error) {
-	return CreateComment(&CreateCommentOptions{
+	comment, err := CreateComment(&CreateCommentOptions{
 		Type:        CommentTypeComment,
 		Doer:        doer,
 		Repo:        repo,
@@ -584,6 +599,21 @@ func CreateIssueComment(doer *User, repo *Repository, issue *Issue, content stri
 		Content:     content,
 		Attachments: attachments,
 	})
+	if err != nil {
+		return nil, fmt.Errorf("CreateComment: %v", err)
+	}
+
+	mode, _ := AccessLevel(doer.ID, repo)
+	if err = PrepareWebhooks(repo, HookEventIssueComment, &api.IssueCommentPayload{
+		Action:     api.HookIssueCommentCreated,
+		Issue:      issue.APIFormat(),
+		Comment:    comment.APIFormat(),
+		Repository: repo.APIFormat(mode),
+		Sender:     doer.APIFormat(),
+	}); err != nil {
+		log.Error(2, "PrepareWebhooks [comment_id: %d]: %v", comment.ID, err)
+	}
+	return comment, nil
 }
 
 // CreateRefComment creates a commit reference comment to issue.
@@ -696,17 +726,41 @@ func GetCommentsByRepoIDSince(repoID, since int64) ([]*Comment, error) {
 }
 
 // UpdateComment updates information of comment.
-func UpdateComment(c *Comment) error {
+func UpdateComment(doer *User, c *Comment, oldContent string) error {
 	if _, err := x.ID(c.ID).AllCols().Update(c); err != nil {
 		return err
 	} else if c.Type == CommentTypeComment {
 		UpdateIssueIndexer(c.IssueID)
 	}
+
+	if err := c.LoadIssue(); err != nil {
+		return err
+	}
+	if err := c.Issue.LoadAttributes(); err != nil {
+		return err
+	}
+
+	mode, _ := AccessLevel(doer.ID, c.Issue.Repo)
+	if err := PrepareWebhooks(c.Issue.Repo, HookEventIssueComment, &api.IssueCommentPayload{
+		Action:  api.HookIssueCommentEdited,
+		Issue:   c.Issue.APIFormat(),
+		Comment: c.APIFormat(),
+		Changes: &api.ChangesPayload{
+			Body: &api.ChangesFromPayload{
+				From: oldContent,
+			},
+		},
+		Repository: c.Issue.Repo.APIFormat(mode),
+		Sender:     doer.APIFormat(),
+	}); err != nil {
+		log.Error(2, "PrepareWebhooks [comment_id: %d]: %v", c.ID, err)
+	}
+
 	return nil
 }
 
 // DeleteComment deletes the comment
-func DeleteComment(comment *Comment) error {
+func DeleteComment(doer *User, comment *Comment) error {
 	sess := x.NewSession()
 	defer sess.Close()
 	if err := sess.Begin(); err != nil {
@@ -733,5 +787,25 @@ func DeleteComment(comment *Comment) error {
 	} else if comment.Type == CommentTypeComment {
 		UpdateIssueIndexer(comment.IssueID)
 	}
+
+	if err := comment.LoadIssue(); err != nil {
+		return err
+	}
+	if err := comment.Issue.LoadAttributes(); err != nil {
+		return err
+	}
+
+	mode, _ := AccessLevel(doer.ID, comment.Issue.Repo)
+
+	if err := PrepareWebhooks(comment.Issue.Repo, HookEventIssueComment, &api.IssueCommentPayload{
+		Action:     api.HookIssueCommentDeleted,
+		Issue:      comment.Issue.APIFormat(),
+		Comment:    comment.APIFormat(),
+		Repository: comment.Issue.Repo.APIFormat(mode),
+		Sender:     doer.APIFormat(),
+	}); err != nil {
+		log.Error(2, "PrepareWebhooks [comment_id: %d]: %v", comment.ID, err)
+	}
+
 	return nil
 }
diff --git a/models/issue_milestone.go b/models/issue_milestone.go
index 8de1f97571..be55dc4f5b 100644
--- a/models/issue_milestone.go
+++ b/models/issue_milestone.go
@@ -5,6 +5,9 @@
 package models
 
 import (
+	"fmt"
+
+	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/util"
 	api "code.gitea.io/sdk/gitea"
@@ -358,7 +361,49 @@ func ChangeMilestoneAssign(issue *Issue, doer *User, oldMilestoneID int64) (err
 	if err = changeMilestoneAssign(sess, doer, issue, oldMilestoneID); err != nil {
 		return err
 	}
-	return sess.Commit()
+
+	if err = sess.Commit(); err != nil {
+		return fmt.Errorf("Commit: %v", err)
+	}
+
+	var hookAction api.HookIssueAction
+	if issue.MilestoneID > 0 {
+		hookAction = api.HookIssueMilestoned
+	} else {
+		hookAction = api.HookIssueDemilestoned
+	}
+
+	if err = issue.LoadAttributes(); err != nil {
+		return err
+	}
+
+	mode, _ := AccessLevel(doer.ID, issue.Repo)
+	if issue.IsPull {
+		err = issue.PullRequest.LoadIssue()
+		if err != nil {
+			log.Error(2, "LoadIssue: %v", err)
+			return
+		}
+		err = PrepareWebhooks(issue.Repo, HookEventPullRequest, &api.PullRequestPayload{
+			Action:      hookAction,
+			Index:       issue.Index,
+			PullRequest: issue.PullRequest.APIFormat(),
+			Repository:  issue.Repo.APIFormat(mode),
+			Sender:      doer.APIFormat(),
+		})
+	} else {
+		err = PrepareWebhooks(issue.Repo, HookEventIssues, &api.IssuePayload{
+			Action:     hookAction,
+			Index:      issue.Index,
+			Issue:      issue.APIFormat(),
+			Repository: issue.Repo.APIFormat(mode),
+			Sender:     doer.APIFormat(),
+		})
+	}
+	if err != nil {
+		log.Error(2, "PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err)
+	}
+	return nil
 }
 
 // DeleteMilestoneByRepoID deletes a milestone from a repository.
diff --git a/models/issue_milestone_test.go b/models/issue_milestone_test.go
index c9b53f4f4a..3ea63d2d6b 100644
--- a/models/issue_milestone_test.go
+++ b/models/issue_milestone_test.go
@@ -232,6 +232,8 @@ func TestChangeMilestoneAssign(t *testing.T) {
 	assert.NoError(t, PrepareTestDatabase())
 	issue := AssertExistsAndLoadBean(t, &Issue{RepoID: 1}).(*Issue)
 	doer := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
+	assert.NotNil(t, issue)
+	assert.NotNil(t, doer)
 
 	oldMilestoneID := issue.MilestoneID
 	issue.MilestoneID = 2
diff --git a/models/release.go b/models/release.go
index 586f494e7d..bc0260c71d 100644
--- a/models/release.go
+++ b/models/release.go
@@ -10,6 +10,7 @@ import (
 	"strings"
 
 	"code.gitea.io/git"
+	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/process"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/util"
@@ -190,8 +191,27 @@ func CreateRelease(gitRepo *git.Repository, rel *Release, attachmentUUIDs []stri
 	}
 
 	err = addReleaseAttachments(rel.ID, attachmentUUIDs)
+	if err != nil {
+		return err
+	}
 
-	return err
+	if !rel.IsDraft {
+		if err := rel.LoadAttributes(); err != nil {
+			log.Error(2, "LoadAttributes: %v", err)
+		} else {
+			mode, _ := AccessLevel(rel.PublisherID, rel.Repo)
+			if err := PrepareWebhooks(rel.Repo, HookEventRelease, &api.ReleasePayload{
+				Action:     api.HookReleasePublished,
+				Release:    rel.APIFormat(),
+				Repository: rel.Repo.APIFormat(mode),
+				Sender:     rel.Publisher.APIFormat(),
+			}); err != nil {
+				log.Error(2, "PrepareWebhooks: %v", err)
+			}
+		}
+	}
+
+	return nil
 }
 
 // GetRelease returns release by given ID.
diff --git a/models/repo.go b/models/repo.go
index 4a7eb859c4..f5ec1a9fdd 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -2456,6 +2456,17 @@ func ForkRepository(doer, u *User, oldRepo *Repository, name, desc string) (_ *R
 		return nil, err
 	}
 
+	oldMode, _ := AccessLevel(doer.ID, oldRepo)
+	mode, _ := AccessLevel(doer.ID, repo)
+
+	if err = PrepareWebhooks(oldRepo, HookEventFork, &api.ForkPayload{
+		Forkee: repo.APIFormat(mode),
+		Repo:   oldRepo.APIFormat(oldMode),
+		Sender: doer.APIFormat(),
+	}); err != nil {
+		log.Error(2, "PrepareWebhooks [repo_id: %d]: %v", oldRepo.ID, err)
+	}
+
 	if err = repo.UpdateSize(); err != nil {
 		log.Error(4, "Failed to update size for repository: %v", err)
 	}
diff --git a/models/webhook.go b/models/webhook.go
index 62db84f86a..c44ca2960d 100644
--- a/models/webhook.go
+++ b/models/webhook.go
@@ -66,10 +66,15 @@ func IsValidHookContentType(name string) bool {
 
 // HookEvents is a set of web hook events
 type HookEvents struct {
-	Create      bool `json:"create"`
-	Push        bool `json:"push"`
-	PullRequest bool `json:"pull_request"`
-	Repository  bool `json:"repository"`
+	Create       bool `json:"create"`
+	Delete       bool `json:"delete"`
+	Fork         bool `json:"fork"`
+	Issues       bool `json:"issues"`
+	IssueComment bool `json:"issue_comment"`
+	Push         bool `json:"push"`
+	PullRequest  bool `json:"pull_request"`
+	Repository   bool `json:"repository"`
+	Release      bool `json:"release"`
 }
 
 // HookEvent represents events that will delivery hook.
@@ -155,6 +160,30 @@ func (w *Webhook) HasCreateEvent() bool {
 		(w.ChooseEvents && w.HookEvents.Create)
 }
 
+// HasDeleteEvent returns true if hook enabled delete event.
+func (w *Webhook) HasDeleteEvent() bool {
+	return w.SendEverything ||
+		(w.ChooseEvents && w.HookEvents.Delete)
+}
+
+// HasForkEvent returns true if hook enabled fork event.
+func (w *Webhook) HasForkEvent() bool {
+	return w.SendEverything ||
+		(w.ChooseEvents && w.HookEvents.Fork)
+}
+
+// HasIssuesEvent returns true if hook enabled issues event.
+func (w *Webhook) HasIssuesEvent() bool {
+	return w.SendEverything ||
+		(w.ChooseEvents && w.HookEvents.Issues)
+}
+
+// HasIssueCommentEvent returns true if hook enabled issue_comment event.
+func (w *Webhook) HasIssueCommentEvent() bool {
+	return w.SendEverything ||
+		(w.ChooseEvents && w.HookEvents.IssueComment)
+}
+
 // HasPushEvent returns true if hook enabled push event.
 func (w *Webhook) HasPushEvent() bool {
 	return w.PushOnly || w.SendEverything ||
@@ -167,23 +196,46 @@ func (w *Webhook) HasPullRequestEvent() bool {
 		(w.ChooseEvents && w.HookEvents.PullRequest)
 }
 
+// HasReleaseEvent returns if hook enabled release event.
+func (w *Webhook) HasReleaseEvent() bool {
+	return w.SendEverything ||
+		(w.ChooseEvents && w.HookEvents.Release)
+}
+
 // HasRepositoryEvent returns if hook enabled repository event.
 func (w *Webhook) HasRepositoryEvent() bool {
 	return w.SendEverything ||
 		(w.ChooseEvents && w.HookEvents.Repository)
 }
 
+func (w *Webhook) eventCheckers() []struct {
+	has func() bool
+	typ HookEventType
+} {
+	return []struct {
+		has func() bool
+		typ HookEventType
+	}{
+		{w.HasCreateEvent, HookEventCreate},
+		{w.HasDeleteEvent, HookEventDelete},
+		{w.HasForkEvent, HookEventFork},
+		{w.HasPushEvent, HookEventPush},
+		{w.HasIssuesEvent, HookEventIssues},
+		{w.HasIssueCommentEvent, HookEventIssueComment},
+		{w.HasPullRequestEvent, HookEventPullRequest},
+		{w.HasRepositoryEvent, HookEventRepository},
+		{w.HasReleaseEvent, HookEventRelease},
+	}
+}
+
 // EventsArray returns an array of hook events
 func (w *Webhook) EventsArray() []string {
-	events := make([]string, 0, 3)
-	if w.HasCreateEvent() {
-		events = append(events, "create")
-	}
-	if w.HasPushEvent() {
-		events = append(events, "push")
-	}
-	if w.HasPullRequestEvent() {
-		events = append(events, "pull_request")
+	events := make([]string, 0, 7)
+
+	for _, c := range w.eventCheckers() {
+		if c.has() {
+			events = append(events, string(c.typ))
+		}
 	}
 	return events
 }
@@ -373,10 +425,15 @@ type HookEventType string
 
 // Types of hook events
 const (
-	HookEventCreate      HookEventType = "create"
-	HookEventPush        HookEventType = "push"
-	HookEventPullRequest HookEventType = "pull_request"
-	HookEventRepository  HookEventType = "repository"
+	HookEventCreate       HookEventType = "create"
+	HookEventDelete       HookEventType = "delete"
+	HookEventFork         HookEventType = "fork"
+	HookEventPush         HookEventType = "push"
+	HookEventIssues       HookEventType = "issues"
+	HookEventIssueComment HookEventType = "issue_comment"
+	HookEventPullRequest  HookEventType = "pull_request"
+	HookEventRepository   HookEventType = "repository"
+	HookEventRelease      HookEventType = "release"
 )
 
 // HookRequest represents hook task request information.
@@ -488,22 +545,11 @@ func PrepareWebhook(w *Webhook, repo *Repository, event HookEventType, p api.Pay
 }
 
 func prepareWebhook(e Engine, w *Webhook, repo *Repository, event HookEventType, p api.Payloader) error {
-	switch event {
-	case HookEventCreate:
-		if !w.HasCreateEvent() {
-			return nil
-		}
-	case HookEventPush:
-		if !w.HasPushEvent() {
-			return nil
-		}
-	case HookEventPullRequest:
-		if !w.HasPullRequestEvent() {
-			return nil
-		}
-	case HookEventRepository:
-		if !w.HasRepositoryEvent() {
-			return nil
+	for _, e := range w.eventCheckers() {
+		if event == e.typ {
+			if !e.has() {
+				return nil
+			}
 		}
 	}
 
diff --git a/models/webhook_dingtalk.go b/models/webhook_dingtalk.go
index 719ffcae73..7eb189f9bb 100644
--- a/models/webhook_dingtalk.go
+++ b/models/webhook_dingtalk.go
@@ -49,6 +49,38 @@ func getDingtalkCreatePayload(p *api.CreatePayload) (*DingtalkPayload, error) {
 	}, nil
 }
 
+func getDingtalkDeletePayload(p *api.DeletePayload) (*DingtalkPayload, error) {
+	// created tag/branch
+	refName := git.RefEndName(p.Ref)
+	title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName)
+
+	return &DingtalkPayload{
+		MsgType: "actionCard",
+		ActionCard: dingtalk.ActionCard{
+			Text:        title,
+			Title:       title,
+			HideAvatar:  "0",
+			SingleTitle: fmt.Sprintf("view branch %s", refName),
+			SingleURL:   p.Repo.HTMLURL + "/src/" + refName,
+		},
+	}, nil
+}
+
+func getDingtalkForkPayload(p *api.ForkPayload) (*DingtalkPayload, error) {
+	title := fmt.Sprintf("%s is forked to %s", p.Forkee.FullName, p.Repo.FullName)
+
+	return &DingtalkPayload{
+		MsgType: "actionCard",
+		ActionCard: dingtalk.ActionCard{
+			Text:        title,
+			Title:       title,
+			HideAvatar:  "0",
+			SingleTitle: fmt.Sprintf("view forked repo %s", p.Repo.FullName),
+			SingleURL:   p.Repo.HTMLURL,
+		},
+	}, nil
+}
+
 func getDingtalkPushPayload(p *api.PushPayload) (*DingtalkPayload, error) {
 	var (
 		branchName = git.RefEndName(p.Ref)
@@ -98,6 +130,80 @@ func getDingtalkPushPayload(p *api.PushPayload) (*DingtalkPayload, error) {
 	}, nil
 }
 
+func getDingtalkIssuesPayload(p *api.IssuePayload) (*DingtalkPayload, error) {
+	var text, title string
+	switch p.Action {
+	case api.HookIssueOpened:
+		title = fmt.Sprintf("[%s] Issue opened: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+		text = p.Issue.Body
+	case api.HookIssueClosed:
+		title = fmt.Sprintf("[%s] Issue closed: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+		text = p.Issue.Body
+	case api.HookIssueReOpened:
+		title = fmt.Sprintf("[%s] Issue re-opened: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+		text = p.Issue.Body
+	case api.HookIssueEdited:
+		title = fmt.Sprintf("[%s] Issue edited: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+		text = p.Issue.Body
+	case api.HookIssueAssigned:
+		title = fmt.Sprintf("[%s] Issue assigned to %s: #%d %s", p.Repository.FullName,
+			p.Issue.Assignee.UserName, p.Index, p.Issue.Title)
+		text = p.Issue.Body
+	case api.HookIssueUnassigned:
+		title = fmt.Sprintf("[%s] Issue unassigned: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+		text = p.Issue.Body
+	case api.HookIssueLabelUpdated:
+		title = fmt.Sprintf("[%s] Pull request labels updated: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+		text = p.Issue.Body
+	case api.HookIssueLabelCleared:
+		title = fmt.Sprintf("[%s] Pull request labels cleared: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+		text = p.Issue.Body
+	case api.HookIssueSynchronized:
+		title = fmt.Sprintf("[%s] Pull request synchronized: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+		text = p.Issue.Body
+	}
+
+	return &DingtalkPayload{
+		MsgType: "actionCard",
+		ActionCard: dingtalk.ActionCard{
+			Text:        text,
+			Title:       title,
+			HideAvatar:  "0",
+			SingleTitle: "view pull request",
+			SingleURL:   p.Issue.URL,
+		},
+	}, nil
+}
+
+func getDingtalkIssueCommentPayload(p *api.IssueCommentPayload) (*DingtalkPayload, error) {
+	title := fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title)
+	url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID))
+	var content string
+	switch p.Action {
+	case api.HookIssueCommentCreated:
+		title = "New comment: " + title
+		content = p.Comment.Body
+	case api.HookIssueCommentEdited:
+		title = "Comment edited: " + title
+		content = p.Comment.Body
+	case api.HookIssueCommentDeleted:
+		title = "Comment deleted: " + title
+		url = fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index)
+		content = p.Comment.Body
+	}
+
+	return &DingtalkPayload{
+		MsgType: "actionCard",
+		ActionCard: dingtalk.ActionCard{
+			Text:        content,
+			Title:       title,
+			HideAvatar:  "0",
+			SingleTitle: "view pull request",
+			SingleURL:   url,
+		},
+	}, nil
+}
+
 func getDingtalkPullRequestPayload(p *api.PullRequestPayload) (*DingtalkPayload, error) {
 	var text, title string
 	switch p.Action {
@@ -182,6 +288,27 @@ func getDingtalkRepositoryPayload(p *api.RepositoryPayload) (*DingtalkPayload, e
 	return nil, nil
 }
 
+func getDingtalkReleasePayload(p *api.ReleasePayload) (*DingtalkPayload, error) {
+	var title, url string
+	switch p.Action {
+	case api.HookReleasePublished:
+		title = fmt.Sprintf("[%s] Release created", p.Release.TagName)
+		url = p.Release.URL
+		return &DingtalkPayload{
+			MsgType: "actionCard",
+			ActionCard: dingtalk.ActionCard{
+				Text:        title,
+				Title:       title,
+				HideAvatar:  "0",
+				SingleTitle: "view repository",
+				SingleURL:   url,
+			},
+		}, nil
+	}
+
+	return nil, nil
+}
+
 // GetDingtalkPayload converts a ding talk webhook into a DingtalkPayload
 func GetDingtalkPayload(p api.Payloader, event HookEventType, meta string) (*DingtalkPayload, error) {
 	s := new(DingtalkPayload)
@@ -189,12 +316,22 @@ func GetDingtalkPayload(p api.Payloader, event HookEventType, meta string) (*Din
 	switch event {
 	case HookEventCreate:
 		return getDingtalkCreatePayload(p.(*api.CreatePayload))
+	case HookEventDelete:
+		return getDingtalkDeletePayload(p.(*api.DeletePayload))
+	case HookEventFork:
+		return getDingtalkForkPayload(p.(*api.ForkPayload))
+	case HookEventIssues:
+		return getDingtalkIssuesPayload(p.(*api.IssuePayload))
+	case HookEventIssueComment:
+		return getDingtalkIssueCommentPayload(p.(*api.IssueCommentPayload))
 	case HookEventPush:
 		return getDingtalkPushPayload(p.(*api.PushPayload))
 	case HookEventPullRequest:
 		return getDingtalkPullRequestPayload(p.(*api.PullRequestPayload))
 	case HookEventRepository:
 		return getDingtalkRepositoryPayload(p.(*api.RepositoryPayload))
+	case HookEventRelease:
+		return getDingtalkReleasePayload(p.(*api.ReleasePayload))
 	}
 
 	return s, nil
diff --git a/models/webhook_discord.go b/models/webhook_discord.go
index 40d9d58992..04ebbc293f 100644
--- a/models/webhook_discord.go
+++ b/models/webhook_discord.go
@@ -115,6 +115,51 @@ func getDiscordCreatePayload(p *api.CreatePayload, meta *DiscordMeta) (*DiscordP
 	}, nil
 }
 
+func getDiscordDeletePayload(p *api.DeletePayload, meta *DiscordMeta) (*DiscordPayload, error) {
+	// deleted tag/branch
+	refName := git.RefEndName(p.Ref)
+	title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName)
+
+	return &DiscordPayload{
+		Username:  meta.Username,
+		AvatarURL: meta.IconURL,
+		Embeds: []DiscordEmbed{
+			{
+				Title: title,
+				URL:   p.Repo.HTMLURL + "/src/" + refName,
+				Color: warnColor,
+				Author: DiscordEmbedAuthor{
+					Name:    p.Sender.UserName,
+					URL:     setting.AppURL + p.Sender.UserName,
+					IconURL: p.Sender.AvatarURL,
+				},
+			},
+		},
+	}, nil
+}
+
+func getDiscordForkPayload(p *api.ForkPayload, meta *DiscordMeta) (*DiscordPayload, error) {
+	// fork
+	title := fmt.Sprintf("%s is forked to %s", p.Forkee.FullName, p.Repo.FullName)
+
+	return &DiscordPayload{
+		Username:  meta.Username,
+		AvatarURL: meta.IconURL,
+		Embeds: []DiscordEmbed{
+			{
+				Title: title,
+				URL:   p.Repo.HTMLURL,
+				Color: successColor,
+				Author: DiscordEmbedAuthor{
+					Name:    p.Sender.UserName,
+					URL:     setting.AppURL + p.Sender.UserName,
+					IconURL: p.Sender.AvatarURL,
+				},
+			},
+		},
+	}, nil
+}
+
 func getDiscordPushPayload(p *api.PushPayload, meta *DiscordMeta) (*DiscordPayload, error) {
 	var (
 		branchName = git.RefEndName(p.Ref)
@@ -165,6 +210,108 @@ func getDiscordPushPayload(p *api.PushPayload, meta *DiscordMeta) (*DiscordPaylo
 	}, nil
 }
 
+func getDiscordIssuesPayload(p *api.IssuePayload, meta *DiscordMeta) (*DiscordPayload, error) {
+	var text, title string
+	var color int
+	switch p.Action {
+	case api.HookIssueOpened:
+		title = fmt.Sprintf("[%s] Issue opened: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+		text = p.Issue.Body
+		color = warnColor
+	case api.HookIssueClosed:
+		title = fmt.Sprintf("[%s] Issue closed: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+		color = failedColor
+		text = p.Issue.Body
+	case api.HookIssueReOpened:
+		title = fmt.Sprintf("[%s] Issue re-opened: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+		text = p.Issue.Body
+		color = warnColor
+	case api.HookIssueEdited:
+		title = fmt.Sprintf("[%s] Issue edited: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+		text = p.Issue.Body
+		color = warnColor
+	case api.HookIssueAssigned:
+		title = fmt.Sprintf("[%s] Issue assigned to %s: #%d %s", p.Repository.FullName,
+			p.Issue.Assignee.UserName, p.Index, p.Issue.Title)
+		text = p.Issue.Body
+		color = successColor
+	case api.HookIssueUnassigned:
+		title = fmt.Sprintf("[%s] Issue unassigned: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+		text = p.Issue.Body
+		color = warnColor
+	case api.HookIssueLabelUpdated:
+		title = fmt.Sprintf("[%s] Issue labels updated: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+		text = p.Issue.Body
+		color = warnColor
+	case api.HookIssueLabelCleared:
+		title = fmt.Sprintf("[%s] Issue labels cleared: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+		text = p.Issue.Body
+		color = warnColor
+	case api.HookIssueSynchronized:
+		title = fmt.Sprintf("[%s] Issue synchronized: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+		text = p.Issue.Body
+		color = warnColor
+	}
+
+	return &DiscordPayload{
+		Username:  meta.Username,
+		AvatarURL: meta.IconURL,
+		Embeds: []DiscordEmbed{
+			{
+				Title:       title,
+				Description: text,
+				URL:         p.Issue.URL,
+				Color:       color,
+				Author: DiscordEmbedAuthor{
+					Name:    p.Sender.UserName,
+					URL:     setting.AppURL + p.Sender.UserName,
+					IconURL: p.Sender.AvatarURL,
+				},
+			},
+		},
+	}, nil
+}
+
+func getDiscordIssueCommentPayload(p *api.IssueCommentPayload, discord *DiscordMeta) (*DiscordPayload, error) {
+	title := fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title)
+	url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID))
+	content := ""
+	var color int
+	switch p.Action {
+	case api.HookIssueCommentCreated:
+		title = "New comment: " + title
+		content = p.Comment.Body
+		color = successColor
+	case api.HookIssueCommentEdited:
+		title = "Comment edited: " + title
+		content = p.Comment.Body
+		color = warnColor
+	case api.HookIssueCommentDeleted:
+		title = "Comment deleted: " + title
+		url = fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index)
+		content = p.Comment.Body
+		color = warnColor
+	}
+
+	return &DiscordPayload{
+		Username:  discord.Username,
+		AvatarURL: discord.IconURL,
+		Embeds: []DiscordEmbed{
+			{
+				Title:       title,
+				Description: content,
+				URL:         url,
+				Color:       color,
+				Author: DiscordEmbedAuthor{
+					Name:    p.Sender.UserName,
+					URL:     setting.AppURL + p.Sender.UserName,
+					IconURL: p.Sender.AvatarURL,
+				},
+			},
+		},
+	}, nil
+}
+
 func getDiscordPullRequestPayload(p *api.PullRequestPayload, meta *DiscordMeta) (*DiscordPayload, error) {
 	var text, title string
 	var color int
@@ -267,6 +414,35 @@ func getDiscordRepositoryPayload(p *api.RepositoryPayload, meta *DiscordMeta) (*
 	}, nil
 }
 
+func getDiscordReleasePayload(p *api.ReleasePayload, meta *DiscordMeta) (*DiscordPayload, error) {
+	var title, url string
+	var color int
+	switch p.Action {
+	case api.HookReleasePublished:
+		title = fmt.Sprintf("[%s] Release created", p.Release.TagName)
+		url = p.Release.URL
+		color = successColor
+	}
+
+	return &DiscordPayload{
+		Username:  meta.Username,
+		AvatarURL: meta.IconURL,
+		Embeds: []DiscordEmbed{
+			{
+				Title:       title,
+				Description: fmt.Sprintf("%s", p.Release.Note),
+				URL:         url,
+				Color:       color,
+				Author: DiscordEmbedAuthor{
+					Name:    p.Sender.UserName,
+					URL:     setting.AppURL + p.Sender.UserName,
+					IconURL: p.Sender.AvatarURL,
+				},
+			},
+		},
+	}, nil
+}
+
 // GetDiscordPayload converts a discord webhook into a DiscordPayload
 func GetDiscordPayload(p api.Payloader, event HookEventType, meta string) (*DiscordPayload, error) {
 	s := new(DiscordPayload)
@@ -279,12 +455,22 @@ func GetDiscordPayload(p api.Payloader, event HookEventType, meta string) (*Disc
 	switch event {
 	case HookEventCreate:
 		return getDiscordCreatePayload(p.(*api.CreatePayload), discord)
+	case HookEventDelete:
+		return getDiscordDeletePayload(p.(*api.DeletePayload), discord)
+	case HookEventFork:
+		return getDiscordForkPayload(p.(*api.ForkPayload), discord)
+	case HookEventIssues:
+		return getDiscordIssuesPayload(p.(*api.IssuePayload), discord)
+	case HookEventIssueComment:
+		return getDiscordIssueCommentPayload(p.(*api.IssueCommentPayload), discord)
 	case HookEventPush:
 		return getDiscordPushPayload(p.(*api.PushPayload), discord)
 	case HookEventPullRequest:
 		return getDiscordPullRequestPayload(p.(*api.PullRequestPayload), discord)
 	case HookEventRepository:
 		return getDiscordRepositoryPayload(p.(*api.RepositoryPayload), discord)
+	case HookEventRelease:
+		return getDiscordReleasePayload(p.(*api.ReleasePayload), discord)
 	}
 
 	return s, nil
diff --git a/models/webhook_slack.go b/models/webhook_slack.go
index 256819adc5..7b18fe3278 100644
--- a/models/webhook_slack.go
+++ b/models/webhook_slack.go
@@ -106,6 +106,122 @@ func getSlackCreatePayload(p *api.CreatePayload, slack *SlackMeta) (*SlackPayloa
 	}, nil
 }
 
+// getSlackDeletePayload composes Slack payload for delete a branch or tag.
+func getSlackDeletePayload(p *api.DeletePayload, slack *SlackMeta) (*SlackPayload, error) {
+	refName := git.RefEndName(p.Ref)
+	repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
+	text := fmt.Sprintf("[%s:%s] %s deleted by %s", repoLink, refName, p.RefType, p.Sender.UserName)
+	return &SlackPayload{
+		Channel:  slack.Channel,
+		Text:     text,
+		Username: slack.Username,
+		IconURL:  slack.IconURL,
+	}, nil
+}
+
+// getSlackForkPayload composes Slack payload for forked by a repository.
+func getSlackForkPayload(p *api.ForkPayload, slack *SlackMeta) (*SlackPayload, error) {
+	baseLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
+	forkLink := SlackLinkFormatter(p.Forkee.HTMLURL, p.Forkee.FullName)
+	text := fmt.Sprintf("%s is forked to %s", baseLink, forkLink)
+	return &SlackPayload{
+		Channel:  slack.Channel,
+		Text:     text,
+		Username: slack.Username,
+		IconURL:  slack.IconURL,
+	}, nil
+}
+
+func getSlackIssuesPayload(p *api.IssuePayload, slack *SlackMeta) (*SlackPayload, error) {
+	senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
+	titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
+		fmt.Sprintf("#%d %s", p.Index, p.Issue.Title))
+	var text, title, attachmentText string
+	switch p.Action {
+	case api.HookIssueOpened:
+		text = fmt.Sprintf("[%s] Issue submitted by %s", p.Repository.FullName, senderLink)
+		title = titleLink
+		attachmentText = SlackTextFormatter(p.Issue.Body)
+	case api.HookIssueClosed:
+		text = fmt.Sprintf("[%s] Issue closed: %s by %s", p.Repository.FullName, titleLink, senderLink)
+	case api.HookIssueReOpened:
+		text = fmt.Sprintf("[%s] Issue re-opened: %s by %s", p.Repository.FullName, titleLink, senderLink)
+	case api.HookIssueEdited:
+		text = fmt.Sprintf("[%s] Issue edited: %s by %s", p.Repository.FullName, titleLink, senderLink)
+		attachmentText = SlackTextFormatter(p.Issue.Body)
+	case api.HookIssueAssigned:
+		text = fmt.Sprintf("[%s] Issue assigned to %s: %s by %s", p.Repository.FullName,
+			SlackLinkFormatter(setting.AppURL+p.Issue.Assignee.UserName, p.Issue.Assignee.UserName),
+			titleLink, senderLink)
+	case api.HookIssueUnassigned:
+		text = fmt.Sprintf("[%s] Issue unassigned: %s by %s", p.Repository.FullName, titleLink, senderLink)
+	case api.HookIssueLabelUpdated:
+		text = fmt.Sprintf("[%s] Issue labels updated: %s by %s", p.Repository.FullName, titleLink, senderLink)
+	case api.HookIssueLabelCleared:
+		text = fmt.Sprintf("[%s] Issue labels cleared: %s by %s", p.Repository.FullName, titleLink, senderLink)
+	case api.HookIssueSynchronized:
+		text = fmt.Sprintf("[%s] Issue synchronized: %s by %s", p.Repository.FullName, titleLink, senderLink)
+	}
+
+	return &SlackPayload{
+		Channel:  slack.Channel,
+		Text:     text,
+		Username: slack.Username,
+		IconURL:  slack.IconURL,
+		Attachments: []SlackAttachment{{
+			Color: slack.Color,
+			Title: title,
+			Text:  attachmentText,
+		}},
+	}, nil
+}
+
+func getSlackIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) (*SlackPayload, error) {
+	senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
+	titleLink := SlackLinkFormatter(fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID)),
+		fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title))
+	var text, title, attachmentText string
+	switch p.Action {
+	case api.HookIssueCommentCreated:
+		text = fmt.Sprintf("[%s] New comment created by %s", p.Repository.FullName, senderLink)
+		title = titleLink
+		attachmentText = SlackTextFormatter(p.Comment.Body)
+	case api.HookIssueCommentEdited:
+		text = fmt.Sprintf("[%s] Comment edited by %s", p.Repository.FullName, senderLink)
+		title = titleLink
+		attachmentText = SlackTextFormatter(p.Comment.Body)
+	case api.HookIssueCommentDeleted:
+		text = fmt.Sprintf("[%s] Comment deleted by %s", p.Repository.FullName, senderLink)
+		title = SlackLinkFormatter(fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index),
+			fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title))
+		attachmentText = SlackTextFormatter(p.Comment.Body)
+	}
+
+	return &SlackPayload{
+		Channel:  slack.Channel,
+		Text:     text,
+		Username: slack.Username,
+		IconURL:  slack.IconURL,
+		Attachments: []SlackAttachment{{
+			Color: slack.Color,
+			Title: title,
+			Text:  attachmentText,
+		}},
+	}, nil
+}
+
+func getSlackReleasePayload(p *api.ReleasePayload, slack *SlackMeta) (*SlackPayload, error) {
+	repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.Name)
+	refLink := SlackLinkFormatter(p.Repository.HTMLURL+"/src/"+p.Release.TagName, p.Release.TagName)
+	text := fmt.Sprintf("[%s] new release %s published by %s", repoLink, refLink, p.Sender.UserName)
+	return &SlackPayload{
+		Channel:  slack.Channel,
+		Text:     text,
+		Username: slack.Username,
+		IconURL:  slack.IconURL,
+	}, nil
+}
+
 func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, error) {
 	// n new commits
 	var (
@@ -238,12 +354,22 @@ func GetSlackPayload(p api.Payloader, event HookEventType, meta string) (*SlackP
 	switch event {
 	case HookEventCreate:
 		return getSlackCreatePayload(p.(*api.CreatePayload), slack)
+	case HookEventDelete:
+		return getSlackDeletePayload(p.(*api.DeletePayload), slack)
+	case HookEventFork:
+		return getSlackForkPayload(p.(*api.ForkPayload), slack)
+	case HookEventIssues:
+		return getSlackIssuesPayload(p.(*api.IssuePayload), slack)
+	case HookEventIssueComment:
+		return getSlackIssueCommentPayload(p.(*api.IssueCommentPayload), slack)
 	case HookEventPush:
 		return getSlackPushPayload(p.(*api.PushPayload), slack)
 	case HookEventPullRequest:
 		return getSlackPullRequestPayload(p.(*api.PullRequestPayload), slack)
 	case HookEventRepository:
 		return getSlackRepositoryPayload(p.(*api.RepositoryPayload), slack)
+	case HookEventRelease:
+		return getSlackReleasePayload(p.(*api.ReleasePayload), slack)
 	}
 
 	return s, nil
diff --git a/models/webhook_test.go b/models/webhook_test.go
index eeae7efbcb..50106a3792 100644
--- a/models/webhook_test.go
+++ b/models/webhook_test.go
@@ -73,7 +73,7 @@ func TestWebhook_UpdateEvent(t *testing.T) {
 }
 
 func TestWebhook_EventsArray(t *testing.T) {
-	assert.Equal(t, []string{"create", "push", "pull_request"},
+	assert.Equal(t, []string{"create", "delete", "fork", "push", "issues", "issue_comment", "pull_request", "repository", "release"},
 		(&Webhook{
 			HookEvent: &HookEvent{SendEverything: true},
 		}).EventsArray(),
diff --git a/modules/auth/repo_form.go b/modules/auth/repo_form.go
index 431ab7f13e..e3471fff5d 100644
--- a/modules/auth/repo_form.go
+++ b/modules/auth/repo_form.go
@@ -155,12 +155,17 @@ func (f *ProtectBranchForm) Validate(ctx *macaron.Context, errs binding.Errors)
 
 // WebhookForm form for changing web hook
 type WebhookForm struct {
-	Events      string
-	Create      bool
-	Push        bool
-	PullRequest bool
-	Repository  bool
-	Active      bool
+	Events       string
+	Create       bool
+	Delete       bool
+	Fork         bool
+	Issues       bool
+	IssueComment bool
+	Release      bool
+	Push         bool
+	PullRequest  bool
+	Repository   bool
+	Active       bool
 }
 
 // PushOnly if the hook will be triggered when push
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 8ef0056608..30f07dc131 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1000,6 +1000,16 @@ settings.event_send_everything = All Events
 settings.event_choose = Custom Events…
 settings.event_create = Create
 settings.event_create_desc = Branch or tag created.
+settings.event_delete = Delete
+settings.event_delete_desc = Branch or tag deleted
+settings.event_fork = Fork
+settings.event_fork_desc = Repository forked
+settings.event_issues = Issues
+settings.event_issues_desc = Issue opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, milestoned, or demilestoned.
+settings.event_issue_comment = Issue Comment
+settings.event_issue_comment_desc = Issue comment created, edited, or deleted.
+settings.event_release = Release
+settings.event_release_desc = Release published in a repository.
 settings.event_pull_request = Pull Request
 settings.event_pull_request_desc = Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared or synchronized.
 settings.event_push = Push
diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go
index a9258849ea..2865ea9165 100644
--- a/routers/api/v1/repo/issue_comment.go
+++ b/routers/api/v1/repo/issue_comment.go
@@ -261,8 +261,9 @@ func editIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption)
 		return
 	}
 
+	oldContent := comment.Content
 	comment.Content = form.Body
-	if err := models.UpdateComment(comment); err != nil {
+	if err := models.UpdateComment(ctx.User, comment, oldContent); err != nil {
 		ctx.Error(500, "UpdateComment", err)
 		return
 	}
@@ -348,7 +349,7 @@ func deleteIssueComment(ctx *context.APIContext) {
 		return
 	}
 
-	if err = models.DeleteComment(comment); err != nil {
+	if err = models.DeleteComment(ctx.User, comment); err != nil {
 		ctx.Error(500, "DeleteCommentByID", err)
 		return
 	}
diff --git a/routers/api/v1/utils/hook.go b/routers/api/v1/utils/hook.go
index e1533da94c..d0538ec54f 100644
--- a/routers/api/v1/utils/hook.go
+++ b/routers/api/v1/utils/hook.go
@@ -7,12 +7,13 @@ package utils
 import (
 	api "code.gitea.io/sdk/gitea"
 
+	"encoding/json"
+	"net/http"
+
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/routers/api/v1/convert"
-	"encoding/json"
 	"github.com/Unknwon/com"
-	"net/http"
 )
 
 // GetOrgHook get an organization's webhook. If there is an error, write to
@@ -98,9 +99,15 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, orgID, repoID
 		HookEvent: &models.HookEvent{
 			ChooseEvents: true,
 			HookEvents: models.HookEvents{
-				Create:      com.IsSliceContainsStr(form.Events, string(models.HookEventCreate)),
-				Push:        com.IsSliceContainsStr(form.Events, string(models.HookEventPush)),
-				PullRequest: com.IsSliceContainsStr(form.Events, string(models.HookEventPullRequest)),
+				Create:       com.IsSliceContainsStr(form.Events, string(models.HookEventCreate)),
+				Delete:       com.IsSliceContainsStr(form.Events, string(models.HookEventDelete)),
+				Fork:         com.IsSliceContainsStr(form.Events, string(models.HookEventFork)),
+				Issues:       com.IsSliceContainsStr(form.Events, string(models.HookEventIssues)),
+				IssueComment: com.IsSliceContainsStr(form.Events, string(models.HookEventIssueComment)),
+				Push:         com.IsSliceContainsStr(form.Events, string(models.HookEventPush)),
+				PullRequest:  com.IsSliceContainsStr(form.Events, string(models.HookEventPullRequest)),
+				Repository:   com.IsSliceContainsStr(form.Events, string(models.HookEventRepository)),
+				Release:      com.IsSliceContainsStr(form.Events, string(models.HookEventRelease)),
 			},
 		},
 		IsActive:     form.Active,
@@ -211,6 +218,16 @@ func editHook(ctx *context.APIContext, form *api.EditHookOption, w *models.Webho
 	w.Create = com.IsSliceContainsStr(form.Events, string(models.HookEventCreate))
 	w.Push = com.IsSliceContainsStr(form.Events, string(models.HookEventPush))
 	w.PullRequest = com.IsSliceContainsStr(form.Events, string(models.HookEventPullRequest))
+	w.Create = com.IsSliceContainsStr(form.Events, string(models.HookEventCreate))
+	w.Delete = com.IsSliceContainsStr(form.Events, string(models.HookEventDelete))
+	w.Fork = com.IsSliceContainsStr(form.Events, string(models.HookEventFork))
+	w.Issues = com.IsSliceContainsStr(form.Events, string(models.HookEventIssues))
+	w.IssueComment = com.IsSliceContainsStr(form.Events, string(models.HookEventIssueComment))
+	w.Push = com.IsSliceContainsStr(form.Events, string(models.HookEventPush))
+	w.PullRequest = com.IsSliceContainsStr(form.Events, string(models.HookEventPullRequest))
+	w.Repository = com.IsSliceContainsStr(form.Events, string(models.HookEventRepository))
+	w.Release = com.IsSliceContainsStr(form.Events, string(models.HookEventRelease))
+
 	if err := w.UpdateEvent(); err != nil {
 		ctx.Error(500, "UpdateEvent", err)
 		return false
diff --git a/routers/repo/issue.go b/routers/repo/issue.go
index 33ba3e0d64..18ab1691cd 100644
--- a/routers/repo/issue.go
+++ b/routers/repo/issue.go
@@ -1086,6 +1086,7 @@ func UpdateCommentContent(ctx *context.Context) {
 		return
 	}
 
+	oldContent := comment.Content
 	comment.Content = ctx.Query("content")
 	if len(comment.Content) == 0 {
 		ctx.JSON(200, map[string]interface{}{
@@ -1093,7 +1094,7 @@ func UpdateCommentContent(ctx *context.Context) {
 		})
 		return
 	}
-	if err = models.UpdateComment(comment); err != nil {
+	if err = models.UpdateComment(ctx.User, comment, oldContent); err != nil {
 		ctx.ServerError("UpdateComment", err)
 		return
 	}
@@ -1119,7 +1120,7 @@ func DeleteComment(ctx *context.Context) {
 		return
 	}
 
-	if err = models.DeleteComment(comment); err != nil {
+	if err = models.DeleteComment(ctx.User, comment); err != nil {
 		ctx.ServerError("DeleteCommentByID", err)
 		return
 	}
diff --git a/routers/repo/webhook.go b/routers/repo/webhook.go
index 35fdf796b5..6994aa3344 100644
--- a/routers/repo/webhook.go
+++ b/routers/repo/webhook.go
@@ -23,9 +23,9 @@ import (
 )
 
 const (
-	tplHooks      base.TplName = "repo/settings/hooks"
-	tplHookNew    base.TplName = "repo/settings/hook_new"
-	tplOrgHookNew base.TplName = "org/settings/hook_new"
+	tplHooks      base.TplName = "repo/settings/webhook/base"
+	tplHookNew    base.TplName = "repo/settings/webhook/new"
+	tplOrgHookNew base.TplName = "org/settings/webhook/new"
 )
 
 // Webhooks render web hooks list page
@@ -118,10 +118,15 @@ func ParseHookEvent(form auth.WebhookForm) *models.HookEvent {
 		SendEverything: form.SendEverything(),
 		ChooseEvents:   form.ChooseEvents(),
 		HookEvents: models.HookEvents{
-			Create:      form.Create,
-			Push:        form.Push,
-			PullRequest: form.PullRequest,
-			Repository:  form.Repository,
+			Create:       form.Create,
+			Delete:       form.Delete,
+			Fork:         form.Fork,
+			Issues:       form.Issues,
+			IssueComment: form.IssueComment,
+			Release:      form.Release,
+			Push:         form.Push,
+			PullRequest:  form.PullRequest,
+			Repository:   form.Repository,
 		},
 	}
 }
diff --git a/templates/repo/settings/hooks.tmpl b/templates/repo/settings/webhook/base.tmpl
similarity index 82%
rename from templates/repo/settings/hooks.tmpl
rename to templates/repo/settings/webhook/base.tmpl
index 34c5cfbb6c..6f486131f7 100644
--- a/templates/repo/settings/hooks.tmpl
+++ b/templates/repo/settings/webhook/base.tmpl
@@ -3,7 +3,7 @@
 	{{template "repo/header" .}}
 	{{template "repo/settings/navbar" .}}
 	<div class="ui container">
-		{{template "repo/settings/hook_list" .}}
+		{{template "repo/settings/webhook/list" .}}
 	</div>
 </div>
 {{template "base/footer" .}}
diff --git a/templates/repo/settings/hook_delete_modal.tmpl b/templates/repo/settings/webhook/delete_modal.tmpl
similarity index 100%
rename from templates/repo/settings/hook_delete_modal.tmpl
rename to templates/repo/settings/webhook/delete_modal.tmpl
diff --git a/templates/repo/settings/hook_dingtalk.tmpl b/templates/repo/settings/webhook/dingtalk.tmpl
similarity index 91%
rename from templates/repo/settings/hook_dingtalk.tmpl
rename to templates/repo/settings/webhook/dingtalk.tmpl
index 37271a7db5..3e6504f651 100644
--- a/templates/repo/settings/hook_dingtalk.tmpl
+++ b/templates/repo/settings/webhook/dingtalk.tmpl
@@ -6,6 +6,6 @@
 			<label for="payload_url">{{.i18n.Tr "repo.settings.payload_url"}}</label>
 			<input id="payload_url" name="payload_url" type="url" value="{{.Webhook.URL}}" autofocus required>
 		</div>
-		{{template "repo/settings/hook_settings" .}}
+		{{template "repo/settings/webhook/settings" .}}
 	</form>
 {{end}}
diff --git a/templates/repo/settings/hook_discord.tmpl b/templates/repo/settings/webhook/discord.tmpl
similarity index 95%
rename from templates/repo/settings/hook_discord.tmpl
rename to templates/repo/settings/webhook/discord.tmpl
index 901e7e6311..75c31efb51 100644
--- a/templates/repo/settings/hook_discord.tmpl
+++ b/templates/repo/settings/webhook/discord.tmpl
@@ -14,6 +14,6 @@
 			<label for="icon_url">{{.i18n.Tr "repo.settings.discord_icon_url"}}</label>
 			<input id="icon_url" name="icon_url" value="{{.DiscordHook.IconURL}}" placeholder="e.g. https://example.com/img/favicon.png">
 		</div>
-		{{template "repo/settings/hook_settings" .}}
+		{{template "repo/settings/webhook/settings" .}}
 	</form>
 {{end}}
diff --git a/templates/repo/settings/hook_gitea.tmpl b/templates/repo/settings/webhook/gitea.tmpl
similarity index 94%
rename from templates/repo/settings/hook_gitea.tmpl
rename to templates/repo/settings/webhook/gitea.tmpl
index fc5e35e068..87a8813d0e 100644
--- a/templates/repo/settings/hook_gitea.tmpl
+++ b/templates/repo/settings/webhook/gitea.tmpl
@@ -23,6 +23,6 @@
 			<label for="secret">{{.i18n.Tr "repo.settings.secret"}}</label>
 			<input id="secret" name="secret" type="password" value="{{.Webhook.Secret}}" autocomplete="off">
 		</div>
-		{{template "repo/settings/hook_settings" .}}
+		{{template "repo/settings/webhook/settings" .}}
 	</form>
 {{end}}
diff --git a/templates/repo/settings/hook_gogs.tmpl b/templates/repo/settings/webhook/gogs.tmpl
similarity index 96%
rename from templates/repo/settings/hook_gogs.tmpl
rename to templates/repo/settings/webhook/gogs.tmpl
index 28098d14ec..649fb54aea 100644
--- a/templates/repo/settings/hook_gogs.tmpl
+++ b/templates/repo/settings/webhook/gogs.tmpl
@@ -23,6 +23,6 @@
 			<label for="secret">{{.i18n.Tr "repo.settings.secret"}}</label>
 			<input id="secret" name="secret" type="password" value="{{.Webhook.Secret}}" autocomplete="off">
 		</div>
-		{{template "repo/settings/hook_settings" .}}
+		{{template "repo/settings/webhook/settings" .}}
 	</form>
 {{end}}
diff --git a/templates/repo/settings/hook_history.tmpl b/templates/repo/settings/webhook/history.tmpl
similarity index 100%
rename from templates/repo/settings/hook_history.tmpl
rename to templates/repo/settings/webhook/history.tmpl
diff --git a/templates/repo/settings/hook_list.tmpl b/templates/repo/settings/webhook/list.tmpl
similarity index 97%
rename from templates/repo/settings/hook_list.tmpl
rename to templates/repo/settings/webhook/list.tmpl
index 4e61ba7a07..de6bd2c5f2 100644
--- a/templates/repo/settings/hook_list.tmpl
+++ b/templates/repo/settings/webhook/list.tmpl
@@ -48,4 +48,4 @@
 	</div>
 </div>
 
-{{template "repo/settings/hook_delete_modal" .}}
+{{template "repo/settings/webhook/delete_modal" .}}
diff --git a/templates/repo/settings/hook_new.tmpl b/templates/repo/settings/webhook/new.tmpl
similarity index 77%
rename from templates/repo/settings/hook_new.tmpl
rename to templates/repo/settings/webhook/new.tmpl
index 7e3cf3c8cf..1b3d114577 100644
--- a/templates/repo/settings/hook_new.tmpl
+++ b/templates/repo/settings/webhook/new.tmpl
@@ -21,14 +21,14 @@
 			</div>
 		</h4>
 		<div class="ui attached segment">
-			{{template "repo/settings/hook_gitea" .}}
-			{{template "repo/settings/hook_gogs" .}}
-			{{template "repo/settings/hook_slack" .}}
-			{{template "repo/settings/hook_discord" .}}
-			{{template "repo/settings/hook_dingtalk" .}}
+			{{template "repo/settings/webhook/gitea" .}}
+			{{template "repo/settings/webhook/gogs" .}}
+			{{template "repo/settings/webhook/slack" .}}
+			{{template "repo/settings/webhook/discord" .}}
+			{{template "repo/settings/webhook/dingtalk" .}}
 		</div>
 
-		{{template "repo/settings/hook_history" .}}
+		{{template "repo/settings/webhook/history" .}}
 	</div>
 </div>
 {{template "base/footer" .}}
diff --git a/templates/repo/settings/hook_settings.tmpl b/templates/repo/settings/webhook/settings.tmpl
similarity index 63%
rename from templates/repo/settings/hook_settings.tmpl
rename to templates/repo/settings/webhook/settings.tmpl
index 7f3406588f..f04c25a0a3 100644
--- a/templates/repo/settings/hook_settings.tmpl
+++ b/templates/repo/settings/webhook/settings.tmpl
@@ -32,6 +32,26 @@
 				</div>
 			</div>
 		</div>
+		<!-- Delete -->
+		<div class="seven wide column">
+			<div class="field">
+				<div class="ui checkbox">
+					<input class="hidden" name="delete" type="checkbox" tabindex="0" {{if .Webhook.Delete}}checked{{end}}>
+					<label>{{.i18n.Tr "repo.settings.event_delete"}}</label>
+					<span class="help">{{.i18n.Tr "repo.settings.event_delete_desc"}}</span>
+				</div>
+			</div>
+		</div>
+		<!-- Fork -->
+		<div class="seven wide column">
+			<div class="field">
+				<div class="ui checkbox">
+					<input class="hidden" name="fork" type="checkbox" tabindex="0" {{if .Webhook.Fork}}checked{{end}}>
+					<label>{{.i18n.Tr "repo.settings.event_fork"}}</label>
+					<span class="help">{{.i18n.Tr "repo.settings.event_fork_desc"}}</span>
+				</div>
+			</div>
+		</div>
 		<!-- Push -->
 		<div class="seven wide column">
 			<div class="field">
@@ -42,6 +62,26 @@
 				</div>
 			</div>
 		</div>
+		<!-- Issues -->
+		<div class="seven wide column">
+			<div class="field">
+				<div class="ui checkbox">
+					<input class="hidden" name="issues" type="checkbox" tabindex="0" {{if .Webhook.Issues}}checked{{end}}>
+					<label>{{.i18n.Tr "repo.settings.event_issues"}}</label>
+					<span class="help">{{.i18n.Tr "repo.settings.event_issues_desc"}}</span>
+				</div>
+			</div>
+		</div>
+		<!-- Issue Comment -->
+		<div class="seven wide column">
+			<div class="field">
+				<div class="ui checkbox">
+					<input class="hidden" name="issue_comment" type="checkbox" tabindex="0" {{if .Webhook.IssueComment}}checked{{end}}>
+					<label>{{.i18n.Tr "repo.settings.event_issue_comment"}}</label>
+					<span class="help">{{.i18n.Tr "repo.settings.event_issue_comment_desc"}}</span>
+				</div>
+			</div>
+		</div>
 		<!-- Pull Request -->
 		<div class="seven wide column">
 			<div class="field">
@@ -62,6 +102,16 @@
 				</div>
 			</div>
 		</div>
+		<!-- Release -->
+		<div class="seven wide column">
+			<div class="field">
+				<div class="ui checkbox">
+					<input class="hidden" name="release" type="checkbox" tabindex="0" {{if .Webhook.Release}}checked{{end}}>
+					<label>{{.i18n.Tr "repo.settings.event_release"}}</label>
+					<span class="help">{{.i18n.Tr "repo.settings.event_release_desc"}}</span>
+				</div>
+			</div>
+		</div>
 	</div>
 </div>
 
@@ -83,4 +133,4 @@
 	{{end}}
 </div>
 
-{{template "repo/settings/hook_delete_modal" .}}
+{{template "repo/settings/webhook/delete_modal" .}}
diff --git a/templates/repo/settings/hook_slack.tmpl b/templates/repo/settings/webhook/slack.tmpl
similarity index 96%
rename from templates/repo/settings/hook_slack.tmpl
rename to templates/repo/settings/webhook/slack.tmpl
index 16e1859470..c35a679da7 100644
--- a/templates/repo/settings/hook_slack.tmpl
+++ b/templates/repo/settings/webhook/slack.tmpl
@@ -23,6 +23,6 @@
 			<label for="color">{{.i18n.Tr "repo.settings.slack_color"}}</label>
 			<input id="color" name="color" value="{{.SlackHook.Color}}" placeholder="e.g. #dd4b39, good, warning, danger">
 		</div>
-		{{template "repo/settings/hook_settings" .}}
+		{{template "repo/settings/webhook/settings" .}}
 	</form>
 {{end}}
diff --git a/vendor/code.gitea.io/sdk/gitea/hook.go b/vendor/code.gitea.io/sdk/gitea/hook.go
index a9b0bdbd06..85d99652dc 100644
--- a/vendor/code.gitea.io/sdk/gitea/hook.go
+++ b/vendor/code.gitea.io/sdk/gitea/hook.go
@@ -172,9 +172,14 @@ type PayloadCommitVerification struct {
 
 var (
 	_ Payloader = &CreatePayload{}
+	_ Payloader = &DeletePayload{}
+	_ Payloader = &ForkPayload{}
 	_ Payloader = &PushPayload{}
 	_ Payloader = &IssuePayload{}
+	_ Payloader = &IssueCommentPayload{}
 	_ Payloader = &PullRequestPayload{}
+	_ Payloader = &RepositoryPayload{}
+	_ Payloader = &ReleasePayload{}
 )
 
 // _________                        __
@@ -224,6 +229,123 @@ func ParseCreateHook(raw []byte) (*CreatePayload, error) {
 	return hook, nil
 }
 
+// ________         .__          __
+// \______ \   ____ |  |   _____/  |_  ____
+//  |    |  \_/ __ \|  | _/ __ \   __\/ __ \
+//  |    `   \  ___/|  |_\  ___/|  | \  ___/
+// /_______  /\___  >____/\___  >__|  \___  >
+//         \/     \/          \/          \/
+
+// PusherType define the type to push
+type PusherType string
+
+// describe all the PusherTypes
+const (
+	PusherTypeUser PusherType = "user"
+)
+
+// DeletePayload represents delete payload
+type DeletePayload struct {
+	Ref        string      `json:"ref"`
+	RefType    string      `json:"ref_type"`
+	PusherType PusherType  `json:"pusher_type"`
+	Repo       *Repository `json:"repository"`
+	Sender     *User       `json:"sender"`
+}
+
+// SetSecret implements Payload
+func (p *DeletePayload) SetSecret(secret string) {
+}
+
+// JSONPayload implements Payload
+func (p *DeletePayload) JSONPayload() ([]byte, error) {
+	return json.MarshalIndent(p, "", "  ")
+}
+
+// ___________           __
+// \_   _____/__________|  | __
+//  |    __)/  _ \_  __ \  |/ /
+//  |     \(  <_> )  | \/    <
+//  \___  / \____/|__|  |__|_ \
+//      \/                   \/
+
+// ForkPayload represents fork payload
+type ForkPayload struct {
+	Forkee *Repository `json:"forkee"`
+	Repo   *Repository `json:"repository"`
+	Sender *User       `json:"sender"`
+}
+
+// SetSecret implements Payload
+func (p *ForkPayload) SetSecret(secret string) {
+}
+
+// JSONPayload implements Payload
+func (p *ForkPayload) JSONPayload() ([]byte, error) {
+	return json.MarshalIndent(p, "", "  ")
+}
+
+// HookIssueCommentAction defines hook issue comment action
+type HookIssueCommentAction string
+
+// all issue comment actions
+const (
+	HookIssueCommentCreated HookIssueCommentAction = "created"
+	HookIssueCommentEdited  HookIssueCommentAction = "edited"
+	HookIssueCommentDeleted HookIssueCommentAction = "deleted"
+)
+
+// IssueCommentPayload represents a payload information of issue comment event.
+type IssueCommentPayload struct {
+	Action     HookIssueCommentAction `json:"action"`
+	Issue      *Issue                 `json:"issue"`
+	Comment    *Comment               `json:"comment"`
+	Changes    *ChangesPayload        `json:"changes,omitempty"`
+	Repository *Repository            `json:"repository"`
+	Sender     *User                  `json:"sender"`
+}
+
+// SetSecret implements Payload
+func (p *IssueCommentPayload) SetSecret(secret string) {
+}
+
+// JSONPayload implements Payload
+func (p *IssueCommentPayload) JSONPayload() ([]byte, error) {
+	return json.MarshalIndent(p, "", "  ")
+}
+
+// __________       .__
+// \______   \ ____ |  |   ____ _____    ______ ____
+//  |       _// __ \|  | _/ __ \\__  \  /  ___// __ \
+//  |    |   \  ___/|  |_\  ___/ / __ \_\___ \\  ___/
+//  |____|_  /\___  >____/\___  >____  /____  >\___  >
+//         \/     \/          \/     \/     \/     \/
+
+// HookReleaseAction defines hook release action type
+type HookReleaseAction string
+
+// all release actions
+const (
+	HookReleasePublished HookReleaseAction = "published"
+)
+
+// ReleasePayload represents a payload information of release event.
+type ReleasePayload struct {
+	Action     HookReleaseAction `json:"action"`
+	Release    *Release          `json:"release"`
+	Repository *Repository       `json:"repository"`
+	Sender     *User             `json:"sender"`
+}
+
+// SetSecret implements Payload
+func (p *ReleasePayload) SetSecret(secret string) {
+}
+
+// JSONPayload implements Payload
+func (p *ReleasePayload) JSONPayload() ([]byte, error) {
+	return json.MarshalIndent(p, "", "  ")
+}
+
 // __________             .__
 // \______   \__ __  _____|  |__
 //  |     ___/  |  \/  ___/  |  \
diff --git a/vendor/code.gitea.io/sdk/gitea/issue.go b/vendor/code.gitea.io/sdk/gitea/issue.go
index 27809ca3b4..fee7cd6f9f 100644
--- a/vendor/code.gitea.io/sdk/gitea/issue.go
+++ b/vendor/code.gitea.io/sdk/gitea/issue.go
@@ -118,14 +118,14 @@ func (c *Client) CreateIssue(owner, repo string, opt CreateIssueOption) (*Issue,
 
 // EditIssueOption options for editing an issue
 type EditIssueOption struct {
-	Title     string     `json:"title"`
-	Body      *string    `json:"body"`
-	Assignee  *string    `json:"assignee"`
-	Assignees []string   `json:"assignees"`
-	Milestone *int64     `json:"milestone"`
-	State     *string    `json:"state"`
+	Title     string   `json:"title"`
+	Body      *string  `json:"body"`
+	Assignee  *string  `json:"assignee"`
+	Assignees []string `json:"assignees"`
+	Milestone *int64   `json:"milestone"`
+	State     *string  `json:"state"`
 	// swagger:strfmt date-time
-	Deadline  *time.Time `json:"due_date"`
+	Deadline *time.Time `json:"due_date"`
 }
 
 // EditIssue modify an existing issue for a given repository
@@ -138,3 +138,17 @@ func (c *Client) EditIssue(owner, repo string, index int64, opt EditIssueOption)
 	return issue, c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s/issues/%d", owner, repo, index),
 		jsonHeader, bytes.NewReader(body), issue)
 }
+
+// EditDeadlineOption options for creating a deadline
+type EditDeadlineOption struct {
+	// required:true
+	// swagger:strfmt date-time
+	Deadline *time.Time `json:"due_date"`
+}
+
+// IssueDeadline represents an issue deadline
+// swagger:model
+type IssueDeadline struct {
+	// swagger:strfmt date-time
+	Deadline *time.Time `json:"due_date"`
+}
diff --git a/vendor/code.gitea.io/sdk/gitea/pull.go b/vendor/code.gitea.io/sdk/gitea/pull.go
index 7d38b14d7d..6fcdd1d41b 100644
--- a/vendor/code.gitea.io/sdk/gitea/pull.go
+++ b/vendor/code.gitea.io/sdk/gitea/pull.go
@@ -85,16 +85,16 @@ func (c *Client) GetPullRequest(owner, repo string, index int64) (*PullRequest,
 
 // CreatePullRequestOption options when creating a pull request
 type CreatePullRequestOption struct {
-	Head      string     `json:"head" binding:"Required"`
-	Base      string     `json:"base" binding:"Required"`
-	Title     string     `json:"title" binding:"Required"`
-	Body      string     `json:"body"`
-	Assignee  string     `json:"assignee"`
-	Assignees []string   `json:"assignees"`
-	Milestone int64      `json:"milestone"`
-	Labels    []int64    `json:"labels"`
+	Head      string   `json:"head" binding:"Required"`
+	Base      string   `json:"base" binding:"Required"`
+	Title     string   `json:"title" binding:"Required"`
+	Body      string   `json:"body"`
+	Assignee  string   `json:"assignee"`
+	Assignees []string `json:"assignees"`
+	Milestone int64    `json:"milestone"`
+	Labels    []int64  `json:"labels"`
 	// swagger:strfmt date-time
-	Deadline  *time.Time `json:"due_date"`
+	Deadline *time.Time `json:"due_date"`
 }
 
 // CreatePullRequest create pull request with options
@@ -110,15 +110,15 @@ func (c *Client) CreatePullRequest(owner, repo string, opt CreatePullRequestOpti
 
 // EditPullRequestOption options when modify pull request
 type EditPullRequestOption struct {
-	Title     string     `json:"title"`
-	Body      string     `json:"body"`
-	Assignee  string     `json:"assignee"`
-	Assignees []string   `json:"assignees"`
-	Milestone int64      `json:"milestone"`
-	Labels    []int64    `json:"labels"`
-	State     *string    `json:"state"`
+	Title     string   `json:"title"`
+	Body      string   `json:"body"`
+	Assignee  string   `json:"assignee"`
+	Assignees []string `json:"assignees"`
+	Milestone int64    `json:"milestone"`
+	Labels    []int64  `json:"labels"`
+	State     *string  `json:"state"`
 	// swagger:strfmt date-time
-	Deadline  *time.Time `json:"due_date"`
+	Deadline *time.Time `json:"due_date"`
 }
 
 // EditPullRequest modify pull request with PR id and options
diff --git a/vendor/vendor.json b/vendor/vendor.json
index 8b8972b16f..712706b96e 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -9,10 +9,10 @@
 			"revisionTime": "2018-04-21T01:08:19Z"
 		},
 		{
-			"checksumSHA1": "WMD6+Qh2+5hd9uiq910pF/Ihylw=",
+			"checksumSHA1": "LnxY/6xD4h9dCCJ5nxKEfZZs1Vk=",
 			"path": "code.gitea.io/sdk/gitea",
-			"revision": "1c8d12f79a51605ed91587aa6b86cf38fc0f987f",
-			"revisionTime": "2018-05-01T11:15:19Z"
+			"revision": "7fa627fa5d67d18c39d6dd3c6c4db836916bf234",
+			"revisionTime": "2018-05-10T12:54:05Z"
 		},
 		{
 			"checksumSHA1": "bOODD4Gbw3GfcuQPU2dI40crxxk=",