Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Default Webhooks #4299

Merged
merged 7 commits into from
Mar 19, 2019
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions models/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -1359,6 +1359,10 @@ func createRepository(e *xorm.Session, doer, u *User, repo *Repository) (err err
return fmt.Errorf("newRepoAction: %v", err)
}

if err = copyDefaultWebhooksToRepo(e, repo.ID); err != nil {
return fmt.Errorf("copyDefaultWebhooksToRepo: %v", err)
}

return nil
}

Expand Down
73 changes: 72 additions & 1 deletion models/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,11 @@ func (w *Webhook) EventsArray() []string {

// CreateWebhook creates a new web hook.
func CreateWebhook(w *Webhook) error {
_, err := x.Insert(w)
return createWebhook(x, w)
}

func createWebhook(e Engine, w *Webhook) error {
_, err := e.Insert(w)
return err
}

Expand Down Expand Up @@ -316,6 +320,32 @@ func GetWebhooksByOrgID(orgID int64) (ws []*Webhook, err error) {
return ws, err
}

// GetDefaultWebhook returns admin-default webhook by given ID.
func GetDefaultWebhook(id int64) (*Webhook, error) {
webhook := &Webhook{ID: id}
has, err := x.
Where("repo_id=? AND org_id=?", 0, 0).
Get(webhook)
if err != nil {
return nil, err
} else if !has {
return nil, ErrWebhookNotExist{id}
}
return webhook, nil
}

// GetDefaultWebhooks returns all admin-default webhooks.
func GetDefaultWebhooks() ([]*Webhook, error) {
return getDefaultWebhooks(x)
}

func getDefaultWebhooks(e Engine) ([]*Webhook, error) {
webhooks := make([]*Webhook, 0, 5)
return webhooks, e.
Where("repo_id=? AND org_id=?", 0, 0).
Find(&webhooks)
}

// UpdateWebhook updates information of webhook.
func UpdateWebhook(w *Webhook) error {
_, err := x.ID(w.ID).AllCols().Update(w)
Expand Down Expand Up @@ -364,6 +394,47 @@ func DeleteWebhookByOrgID(orgID, id int64) error {
})
}

// DeleteDefaultWebhook deletes an admin-default webhook by given ID.
func DeleteDefaultWebhook(id int64) error {
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}

count, err := sess.
Where("repo_id=? AND org_id=?", 0, 0).
Delete(&Webhook{ID: id})
if err != nil {
return err
} else if count == 0 {
return ErrWebhookNotExist{ID: id}
}

if _, err := sess.Delete(&HookTask{HookID: id}); err != nil {
return err
}

return sess.Commit()
}

// copyDefaultWebhooksToRepo creates copies of the default webhooks in a new repo
func copyDefaultWebhooksToRepo(e Engine, repoID int64) error {
ws, err := getDefaultWebhooks(e)
if err != nil {
return fmt.Errorf("GetDefaultWebhooks: %v", err)
}

for _, w := range ws {
w.ID = 0
w.RepoID = repoID
if err := createWebhook(e, w); err != nil {
return fmt.Errorf("CreateWebhook: %v", err)
}
}
return nil
}

// ___ ___ __ ___________ __
// / | \ ____ ____ | | _\__ ___/____ _____| | __
// / ~ \/ _ \ / _ \| |/ / | | \__ \ / ___/ |/ /
Expand Down
5 changes: 5 additions & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -1368,6 +1368,7 @@ dashboard = Dashboard
users = User Accounts
organizations = Organizations
repositories = Repositories
hooks = Default Webhooks
authentication = Authentication Sources
config = Configuration
notices = System Notices
Expand Down Expand Up @@ -1481,6 +1482,10 @@ repos.forks = Forks
repos.issues = Issues
repos.size = Size

hooks.desc = Webhooks automatically make HTTP POST requests to a server when certain Gitea events trigger. Webhooks defined here are defaults and will be copied into all new repositories. Read more in the <a target="_blank" rel="noopener" href="https://docs.gitea.io/en-us/webhooks/">webhooks guide</a>.
hooks.add_webhook = Add Default Webhook
hooks.update_webhook = Update Default Webhook

auths.auth_manage_panel = Authentication Source Management
auths.new = Add Authentication Source
auths.name = Name
Expand Down
47 changes: 47 additions & 0 deletions routers/admin/hooks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package admin

import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
)

const (
// tplAdminHooks template path for render hook settings
tplAdminHooks base.TplName = "admin/hooks"
)

// DefaultWebhooks render admin-default webhook list page
func DefaultWebhooks(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("admin.hooks")
ctx.Data["PageIsAdminHooks"] = true
ctx.Data["BaseLink"] = setting.AppSubURL + "/admin/hooks"
ctx.Data["Description"] = ctx.Tr("admin.hooks.desc")

ws, err := models.GetDefaultWebhooks()
if err != nil {
ctx.ServerError("GetWebhooksDefaults", err)
return
}

ctx.Data["Webhooks"] = ws
ctx.HTML(200, tplAdminHooks)
}

// DeleteDefaultWebhook response for delete admin-default webhook
func DeleteDefaultWebhook(ctx *context.Context) {
if err := models.DeleteDefaultWebhook(ctx.QueryInt64("id")); err != nil {
ctx.Flash.Error("DeleteDefaultWebhook: " + err.Error())
} else {
ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
}

ctx.JSON(200, map[string]interface{}{
"redirect": setting.AppSubURL + "/admin/hooks",
})
}
2 changes: 1 addition & 1 deletion routers/org/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ func SettingsDelete(ctx *context.Context) {
func Webhooks(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("org.settings")
ctx.Data["PageIsSettingsHooks"] = true
ctx.Data["BaseLink"] = ctx.Org.OrgLink
ctx.Data["BaseLink"] = ctx.Org.OrgLink + "/settings/hooks"
ctx.Data["Description"] = ctx.Tr("org.settings.hooks_desc")

ws, err := models.GetWebhooksByOrgID(ctx.Org.Organization.ID)
Expand Down
59 changes: 39 additions & 20 deletions routers/repo/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"encoding/json"
"errors"
"fmt"
"path"
"strings"

"code.gitea.io/git"
Expand All @@ -23,16 +24,17 @@ import (
)

const (
tplHooks base.TplName = "repo/settings/webhook/base"
tplHookNew base.TplName = "repo/settings/webhook/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/hook_new"
tplAdminHookNew base.TplName = "admin/hook_new"
)

// Webhooks render web hooks list page
func Webhooks(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.settings.hooks")
ctx.Data["PageIsSettingsHooks"] = true
ctx.Data["BaseLink"] = ctx.Repo.RepoLink
ctx.Data["BaseLink"] = ctx.Repo.RepoLink + "/settings/hooks"
ctx.Data["Description"] = ctx.Tr("repo.settings.hooks_desc", "https://docs.gitea.io/en-us/webhooks/")

ws, err := models.GetWebhooksByRepoID(ctx.Repo.Repository.ID)
Expand All @@ -48,28 +50,37 @@ func Webhooks(ctx *context.Context) {
type orgRepoCtx struct {
OrgID int64
RepoID int64
IsAdmin bool
Link string
NewTemplate base.TplName
}

// getOrgRepoCtx determines whether this is a repo context or organization context.
// getOrgRepoCtx determines whether this is a repo, organization, or admin context.
func getOrgRepoCtx(ctx *context.Context) (*orgRepoCtx, error) {
if len(ctx.Repo.RepoLink) > 0 {
return &orgRepoCtx{
RepoID: ctx.Repo.Repository.ID,
Link: ctx.Repo.RepoLink,
Link: path.Join(ctx.Repo.RepoLink, "settings/hooks"),
NewTemplate: tplHookNew,
}, nil
}

if len(ctx.Org.OrgLink) > 0 {
return &orgRepoCtx{
OrgID: ctx.Org.Organization.ID,
Link: ctx.Org.OrgLink,
Link: path.Join(ctx.Org.OrgLink, "settings/hooks"),
NewTemplate: tplOrgHookNew,
}, nil
}

if ctx.User.IsAdmin {
return &orgRepoCtx{
IsAdmin: true,
Link: path.Join(setting.AppSubURL, "/admin/hooks"),
NewTemplate: tplAdminHookNew,
}, nil
}

return nil, errors.New("Unable to set OrgRepo context")
}

Expand All @@ -85,8 +96,6 @@ func checkHookType(ctx *context.Context) string {
// WebhooksNew render creating webhook page
func WebhooksNew(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
ctx.Data["PageIsSettingsHooks"] = true
ctx.Data["PageIsSettingsHooksNew"] = true
ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}

orCtx, err := getOrgRepoCtx(ctx)
Expand All @@ -95,6 +104,14 @@ func WebhooksNew(ctx *context.Context) {
return
}

if orCtx.IsAdmin {
ctx.Data["PageIsAdminHooks"] = true
ctx.Data["PageIsAdminHooksNew"] = true
} else {
ctx.Data["PageIsSettingsHooks"] = true
ctx.Data["PageIsSettingsHooksNew"] = true
}

hookType := checkHookType(ctx)
ctx.Data["HookType"] = hookType
if ctx.Written() {
Expand Down Expand Up @@ -175,7 +192,7 @@ func WebHooksNewPost(ctx *context.Context, form auth.NewWebhookForm) {
}

ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
ctx.Redirect(orCtx.Link + "/settings/hooks")
ctx.Redirect(orCtx.Link)
}

// GogsHooksNewPost response for creating webhook
Expand Down Expand Up @@ -222,7 +239,7 @@ func GogsHooksNewPost(ctx *context.Context, form auth.NewGogshookForm) {
}

ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
ctx.Redirect(orCtx.Link + "/settings/hooks")
ctx.Redirect(orCtx.Link)
}

// DiscordHooksNewPost response for creating discord hook
Expand Down Expand Up @@ -271,7 +288,7 @@ func DiscordHooksNewPost(ctx *context.Context, form auth.NewDiscordHookForm) {
}

ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
ctx.Redirect(orCtx.Link + "/settings/hooks")
ctx.Redirect(orCtx.Link)
}

// DingtalkHooksNewPost response for creating dingtalk hook
Expand Down Expand Up @@ -311,7 +328,7 @@ func DingtalkHooksNewPost(ctx *context.Context, form auth.NewDingtalkHookForm) {
}

ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
ctx.Redirect(orCtx.Link + "/settings/hooks")
ctx.Redirect(orCtx.Link)
}

// SlackHooksNewPost response for creating slack hook
Expand Down Expand Up @@ -368,7 +385,7 @@ func SlackHooksNewPost(ctx *context.Context, form auth.NewSlackHookForm) {
}

ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
ctx.Redirect(orCtx.Link + "/settings/hooks")
ctx.Redirect(orCtx.Link)
}

func checkWebhook(ctx *context.Context) (*orgRepoCtx, *models.Webhook) {
Expand All @@ -384,8 +401,10 @@ func checkWebhook(ctx *context.Context) (*orgRepoCtx, *models.Webhook) {
var w *models.Webhook
if orCtx.RepoID > 0 {
w, err = models.GetWebhookByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id"))
} else {
} else if orCtx.OrgID > 0 {
w, err = models.GetWebhookByOrgID(ctx.Org.Organization.ID, ctx.ParamsInt64(":id"))
} else {
w, err = models.GetDefaultWebhook(ctx.ParamsInt64(":id"))
}
if err != nil {
if models.IsErrWebhookNotExist(err) {
Expand Down Expand Up @@ -462,7 +481,7 @@ func WebHooksEditPost(ctx *context.Context, form auth.NewWebhookForm) {
}

ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
}

// GogsHooksEditPost response for editing gogs hook
Expand Down Expand Up @@ -501,7 +520,7 @@ func GogsHooksEditPost(ctx *context.Context, form auth.NewGogshookForm) {
}

ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
}

// SlackHooksEditPost response for editing slack hook
Expand Down Expand Up @@ -551,7 +570,7 @@ func SlackHooksEditPost(ctx *context.Context, form auth.NewSlackHookForm) {
}

ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
}

// DiscordHooksEditPost response for editing discord hook
Expand Down Expand Up @@ -593,7 +612,7 @@ func DiscordHooksEditPost(ctx *context.Context, form auth.NewDiscordHookForm) {
}

ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
}

// DingtalkHooksEditPost response for editing discord hook
Expand Down Expand Up @@ -625,7 +644,7 @@ func DingtalkHooksEditPost(ctx *context.Context, form auth.NewDingtalkHookForm)
}

ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
}

// TestWebhook test if web hook is work fine
Expand Down
Loading