| @@ -5,7 +5,7 @@ Gogs - Go Git Service [ |  | ||||
| ##### Current version: 0.7.31 Beta | |||||
| ##### Current version: 0.7.32 Beta | |||||
| <table> | <table> | ||||
| <tr> | <tr> | ||||
| @@ -397,6 +397,7 @@ func runWeb(ctx *cli.Context) { | |||||
| m.Post("/gogs/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost) | m.Post("/gogs/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost) | ||||
| m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost) | m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost) | ||||
| m.Get("/:id", repo.WebHooksEdit) | m.Get("/:id", repo.WebHooksEdit) | ||||
| m.Post("/:id/test", repo.TestWebhook) | |||||
| m.Post("/gogs/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) | m.Post("/gogs/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) | ||||
| m.Post("/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost) | m.Post("/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost) | ||||
| @@ -597,6 +597,9 @@ settings.hooks_desc = Webhooks are much like basic HTTP POST event triggers. Whe | |||||
| settings.webhook_deletion = Delete Webhook | settings.webhook_deletion = Delete Webhook | ||||
| settings.webhook_deletion_desc = Delete this webhook will remove its information and all delivery history. Do you want to continue? | settings.webhook_deletion_desc = Delete this webhook will remove its information and all delivery history. Do you want to continue? | ||||
| settings.webhook_deletion_success = Webhook has been deleted successfully! | settings.webhook_deletion_success = Webhook has been deleted successfully! | ||||
| settings.webhook.test_delivery = Test Delivery | |||||
| settings.webhook.test_delivery_desc = Send a fake push event delivery to test your webhook settings | |||||
| settings.webhook.test_delivery_success = Test webhook has been added to delivery queue. It may taks few seconds before it shows up in the delivery history. | |||||
| settings.webhook.request = Request | settings.webhook.request = Request | ||||
| settings.webhook.response = Response | settings.webhook.response = Response | ||||
| settings.webhook.headers = Headers | settings.webhook.headers = Headers | ||||
| @@ -17,7 +17,7 @@ import ( | |||||
| "github.com/gogits/gogs/modules/setting" | "github.com/gogits/gogs/modules/setting" | ||||
| ) | ) | ||||
| const APP_VER = "0.7.31.1205 Beta" | |||||
| const APP_VER = "0.7.32.1205 Beta" | |||||
| func init() { | func init() { | ||||
| runtime.GOMAXPROCS(runtime.NumCPU()) | runtime.GOMAXPROCS(runtime.NumCPU()) | ||||
| @@ -451,24 +451,9 @@ func CommitRepoAction( | |||||
| IsPrivate: repo.IsPrivate, | IsPrivate: repo.IsPrivate, | ||||
| }); err != nil { | }); err != nil { | ||||
| return fmt.Errorf("NotifyWatchers: %v", err) | return fmt.Errorf("NotifyWatchers: %v", err) | ||||
| } | } | ||||
| repoLink := fmt.Sprintf("%s%s/%s", setting.AppUrl, repoUserName, repoName) | |||||
| payloadRepo := &api.PayloadRepo{ | |||||
| ID: repo.ID, | |||||
| Name: repo.LowerName, | |||||
| URL: repoLink, | |||||
| Description: repo.Description, | |||||
| Website: repo.Website, | |||||
| Watchers: repo.NumWatches, | |||||
| Owner: &api.PayloadAuthor{ | |||||
| Name: repo.Owner.DisplayName(), | |||||
| Email: repo.Owner.Email, | |||||
| UserName: repo.Owner.Name, | |||||
| }, | |||||
| Private: repo.IsPrivate, | |||||
| } | |||||
| payloadRepo := repo.ComposePayload() | |||||
| pusher_email, pusher_name := "", "" | pusher_email, pusher_name := "", "" | ||||
| pusher, err := GetUserByName(userName) | pusher, err := GetUserByName(userName) | ||||
| @@ -494,7 +479,7 @@ func CommitRepoAction( | |||||
| commits[i] = &api.PayloadCommit{ | commits[i] = &api.PayloadCommit{ | ||||
| ID: cmt.Sha1, | ID: cmt.Sha1, | ||||
| Message: cmt.Message, | Message: cmt.Message, | ||||
| URL: fmt.Sprintf("%s/commit/%s", repoLink, cmt.Sha1), | |||||
| URL: fmt.Sprintf("%s/commit/%s", repo.RepoLink(), cmt.Sha1), | |||||
| Author: &api.PayloadAuthor{ | Author: &api.PayloadAuthor{ | ||||
| Name: cmt.AuthorName, | Name: cmt.AuthorName, | ||||
| Email: cmt.AuthorEmail, | Email: cmt.AuthorEmail, | ||||
| @@ -28,6 +28,7 @@ import ( | |||||
| "gopkg.in/ini.v1" | "gopkg.in/ini.v1" | ||||
| "github.com/gogits/git-shell" | "github.com/gogits/git-shell" | ||||
| api "github.com/gogits/go-gogs-client" | |||||
| "github.com/gogits/gogs/modules/base" | "github.com/gogits/gogs/modules/base" | ||||
| "github.com/gogits/gogs/modules/bindata" | "github.com/gogits/gogs/modules/bindata" | ||||
| @@ -380,6 +381,27 @@ func (repo *Repository) SavePatch(index int64, patch []byte) error { | |||||
| return nil | return nil | ||||
| } | } | ||||
| // ComposePayload composes and returns *api.PayloadRepo corresponding to the repository. | |||||
| func (repo *Repository) ComposePayload() *api.PayloadRepo { | |||||
| cl := repo.CloneLink() | |||||
| return &api.PayloadRepo{ | |||||
| ID: repo.ID, | |||||
| Name: repo.LowerName, | |||||
| URL: repo.RepoLink(), | |||||
| SSHURL: cl.SSH, | |||||
| CloneURL: cl.HTTPS, | |||||
| Description: repo.Description, | |||||
| Website: repo.Website, | |||||
| Watchers: repo.NumWatches, | |||||
| Owner: &api.PayloadAuthor{ | |||||
| Name: repo.MustOwner().DisplayName(), | |||||
| Email: repo.MustOwner().Email, | |||||
| UserName: repo.MustOwner().Name, | |||||
| }, | |||||
| Private: repo.IsPrivate, | |||||
| } | |||||
| } | |||||
| func isRepositoryExist(e Engine, u *User, repoName string) (bool, error) { | func isRepositoryExist(e Engine, u *User, repoName string) (bool, error) { | ||||
| has, err := e.Get(&Repository{ | has, err := e.Get(&Repository{ | ||||
| OwnerID: u.Id, | OwnerID: u.Id, | ||||
| @@ -335,7 +335,7 @@ func (t *HookTask) AfterSet(colName string, _ xorm.Cell) { | |||||
| t.ResponseInfo = &HookResponse{} | t.ResponseInfo = &HookResponse{} | ||||
| if err = json.Unmarshal([]byte(t.ResponseContent), t.ResponseInfo); err != nil { | if err = json.Unmarshal([]byte(t.ResponseContent), t.ResponseInfo); err != nil { | ||||
| log.Error(3, "Unmarshal[%d]: %v", t.ID, err) | |||||
| log.Error(3, "Unmarshal [%d]: %v", t.ID, err) | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -343,7 +343,7 @@ func (t *HookTask) AfterSet(colName string, _ xorm.Cell) { | |||||
| func (t *HookTask) MarshalJSON(v interface{}) string { | func (t *HookTask) MarshalJSON(v interface{}) string { | ||||
| p, err := json.Marshal(v) | p, err := json.Marshal(v) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error(3, "Marshal[%d]: %v", t.ID, err) | |||||
| log.Error(3, "Marshal [%d]: %v", t.ID, err) | |||||
| } | } | ||||
| return string(p) | return string(p) | ||||
| } | } | ||||
| @@ -590,24 +590,24 @@ func DeliverHooks() { | |||||
| // Update hook task status. | // Update hook task status. | ||||
| for _, t := range tasks { | for _, t := range tasks { | ||||
| if err := UpdateHookTask(t); err != nil { | if err := UpdateHookTask(t); err != nil { | ||||
| log.Error(4, "UpdateHookTask(%d): %v", t.ID, err) | |||||
| log.Error(4, "UpdateHookTask [%d]: %v", t.ID, err) | |||||
| } | } | ||||
| } | } | ||||
| // Start listening on new hook requests. | // Start listening on new hook requests. | ||||
| for repoID := range HookQueue.Queue() { | for repoID := range HookQueue.Queue() { | ||||
| log.Trace("DeliverHooks[%v]: processing delivery hooks", repoID) | |||||
| log.Trace("DeliverHooks [%v]: processing delivery hooks", repoID) | |||||
| HookQueue.Remove(repoID) | HookQueue.Remove(repoID) | ||||
| tasks = make([]*HookTask, 0, 5) | tasks = make([]*HookTask, 0, 5) | ||||
| if err := x.Where("repo_id=? AND is_delivered=?", repoID, false).Find(&tasks); err != nil { | if err := x.Where("repo_id=? AND is_delivered=?", repoID, false).Find(&tasks); err != nil { | ||||
| log.Error(4, "Get repository(%d) hook tasks: %v", repoID, err) | |||||
| log.Error(4, "Get repository [%d] hook tasks: %v", repoID, err) | |||||
| continue | continue | ||||
| } | } | ||||
| for _, t := range tasks { | for _, t := range tasks { | ||||
| t.deliver() | t.deliver() | ||||
| if err := UpdateHookTask(t); err != nil { | if err := UpdateHookTask(t); err != nil { | ||||
| log.Error(4, "UpdateHookTask[%d]: %v", t.ID, err) | |||||
| log.Error(4, "UpdateHookTask [%d]: %v", t.ID, err) | |||||
| continue | continue | ||||
| } | } | ||||
| } | } | ||||
| @@ -548,6 +548,19 @@ function initWebhook() { | |||||
| $('.events.fields').hide(); | $('.events.fields').hide(); | ||||
| } | } | ||||
| }); | }); | ||||
| // Test delivery | |||||
| $('#test-delivery').click(function () { | |||||
| var $this = $(this); | |||||
| $this.addClass('loading disabled'); | |||||
| $.post($this.data('link'), { | |||||
| "_csrf": csrf | |||||
| }).done( | |||||
| setTimeout(function () { | |||||
| window.location.href = $this.data('redirect'); | |||||
| }, 5000) | |||||
| ) | |||||
| }); | |||||
| } | } | ||||
| @@ -5,14 +5,9 @@ | |||||
| package repo | package repo | ||||
| import ( | import ( | ||||
| "encoding/json" | |||||
| "errors" | |||||
| "fmt" | |||||
| "strings" | "strings" | ||||
| "time" | "time" | ||||
| "github.com/Unknwon/com" | |||||
| "github.com/gogits/gogs/models" | "github.com/gogits/gogs/models" | ||||
| "github.com/gogits/gogs/modules/auth" | "github.com/gogits/gogs/modules/auth" | ||||
| "github.com/gogits/gogs/modules/base" | "github.com/gogits/gogs/modules/base" | ||||
| @@ -26,9 +21,6 @@ import ( | |||||
| const ( | const ( | ||||
| SETTINGS_OPTIONS base.TplName = "repo/settings/options" | SETTINGS_OPTIONS base.TplName = "repo/settings/options" | ||||
| COLLABORATION base.TplName = "repo/settings/collaboration" | COLLABORATION base.TplName = "repo/settings/collaboration" | ||||
| HOOKS base.TplName = "repo/settings/hooks" | |||||
| HOOK_NEW base.TplName = "repo/settings/hook_new" | |||||
| ORG_HOOK_NEW base.TplName = "org/settings/hook_new" | |||||
| GITHOOKS base.TplName = "repo/settings/githooks" | GITHOOKS base.TplName = "repo/settings/githooks" | ||||
| GITHOOK_EDIT base.TplName = "repo/settings/githook_edit" | GITHOOK_EDIT base.TplName = "repo/settings/githook_edit" | ||||
| DEPLOY_KEYS base.TplName = "repo/settings/deploy_keys" | DEPLOY_KEYS base.TplName = "repo/settings/deploy_keys" | ||||
| @@ -270,330 +262,6 @@ func Collaboration(ctx *middleware.Context) { | |||||
| ctx.HTML(200, COLLABORATION) | ctx.HTML(200, COLLABORATION) | ||||
| } | } | ||||
| func Webhooks(ctx *middleware.Context) { | |||||
| ctx.Data["Title"] = ctx.Tr("repo.settings.hooks") | |||||
| ctx.Data["PageIsSettingsHooks"] = true | |||||
| ctx.Data["BaseLink"] = ctx.Repo.RepoLink | |||||
| ctx.Data["Description"] = ctx.Tr("repo.settings.hooks_desc", "https://github.com/gogits/go-gogs-client/wiki/Repositories-Webhooks") | |||||
| ws, err := models.GetWebhooksByRepoID(ctx.Repo.Repository.ID) | |||||
| if err != nil { | |||||
| ctx.Handle(500, "GetWebhooksByRepoID", err) | |||||
| return | |||||
| } | |||||
| ctx.Data["Webhooks"] = ws | |||||
| ctx.HTML(200, HOOKS) | |||||
| } | |||||
| type OrgRepoCtx struct { | |||||
| OrgID int64 | |||||
| RepoID int64 | |||||
| Link string | |||||
| NewTemplate base.TplName | |||||
| } | |||||
| // getOrgRepoCtx determines whether this is a repo context or organization context. | |||||
| func getOrgRepoCtx(ctx *middleware.Context) (*OrgRepoCtx, error) { | |||||
| if len(ctx.Repo.RepoLink) > 0 { | |||||
| return &OrgRepoCtx{ | |||||
| RepoID: ctx.Repo.Repository.ID, | |||||
| Link: ctx.Repo.RepoLink, | |||||
| NewTemplate: HOOK_NEW, | |||||
| }, nil | |||||
| } | |||||
| if len(ctx.Org.OrgLink) > 0 { | |||||
| return &OrgRepoCtx{ | |||||
| OrgID: ctx.Org.Organization.Id, | |||||
| Link: ctx.Org.OrgLink, | |||||
| NewTemplate: ORG_HOOK_NEW, | |||||
| }, nil | |||||
| } | |||||
| return nil, errors.New("Unable to set OrgRepo context") | |||||
| } | |||||
| func checkHookType(ctx *middleware.Context) string { | |||||
| hookType := strings.ToLower(ctx.Params(":type")) | |||||
| if !com.IsSliceContainsStr(setting.Webhook.Types, hookType) { | |||||
| ctx.Handle(404, "checkHookType", nil) | |||||
| return "" | |||||
| } | |||||
| return hookType | |||||
| } | |||||
| func WebhooksNew(ctx *middleware.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) | |||||
| if err != nil { | |||||
| ctx.Handle(500, "getOrgRepoCtx", err) | |||||
| return | |||||
| } | |||||
| ctx.Data["HookType"] = checkHookType(ctx) | |||||
| if ctx.Written() { | |||||
| return | |||||
| } | |||||
| ctx.Data["BaseLink"] = orCtx.Link | |||||
| ctx.HTML(200, orCtx.NewTemplate) | |||||
| } | |||||
| func ParseHookEvent(form auth.WebhookForm) *models.HookEvent { | |||||
| return &models.HookEvent{ | |||||
| PushOnly: form.PushOnly(), | |||||
| SendEverything: form.SendEverything(), | |||||
| ChooseEvents: form.ChooseEvents(), | |||||
| HookEvents: models.HookEvents{ | |||||
| Create: form.Create, | |||||
| Push: form.Push, | |||||
| }, | |||||
| } | |||||
| } | |||||
| func WebHooksNewPost(ctx *middleware.Context, form auth.NewWebhookForm) { | |||||
| 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{}} | |||||
| ctx.Data["HookType"] = "gogs" | |||||
| orCtx, err := getOrgRepoCtx(ctx) | |||||
| if err != nil { | |||||
| ctx.Handle(500, "getOrgRepoCtx", err) | |||||
| return | |||||
| } | |||||
| ctx.Data["BaseLink"] = orCtx.Link | |||||
| if ctx.HasError() { | |||||
| ctx.HTML(200, orCtx.NewTemplate) | |||||
| return | |||||
| } | |||||
| contentType := models.JSON | |||||
| if models.HookContentType(form.ContentType) == models.FORM { | |||||
| contentType = models.FORM | |||||
| } | |||||
| w := &models.Webhook{ | |||||
| RepoID: orCtx.RepoID, | |||||
| URL: form.PayloadURL, | |||||
| ContentType: contentType, | |||||
| Secret: form.Secret, | |||||
| HookEvent: ParseHookEvent(form.WebhookForm), | |||||
| IsActive: form.Active, | |||||
| HookTaskType: models.GOGS, | |||||
| OrgID: orCtx.OrgID, | |||||
| } | |||||
| if err := w.UpdateEvent(); err != nil { | |||||
| ctx.Handle(500, "UpdateEvent", err) | |||||
| return | |||||
| } else if err := models.CreateWebhook(w); err != nil { | |||||
| ctx.Handle(500, "CreateWebhook", err) | |||||
| return | |||||
| } | |||||
| ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success")) | |||||
| ctx.Redirect(orCtx.Link + "/settings/hooks") | |||||
| } | |||||
| func SlackHooksNewPost(ctx *middleware.Context, form auth.NewSlackHookForm) { | |||||
| ctx.Data["Title"] = ctx.Tr("repo.settings") | |||||
| ctx.Data["PageIsSettingsHooks"] = true | |||||
| ctx.Data["PageIsSettingsHooksNew"] = true | |||||
| ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}} | |||||
| orCtx, err := getOrgRepoCtx(ctx) | |||||
| if err != nil { | |||||
| ctx.Handle(500, "getOrgRepoCtx", err) | |||||
| return | |||||
| } | |||||
| if ctx.HasError() { | |||||
| ctx.HTML(200, orCtx.NewTemplate) | |||||
| return | |||||
| } | |||||
| meta, err := json.Marshal(&models.SlackMeta{ | |||||
| Channel: form.Channel, | |||||
| Username: form.Username, | |||||
| IconURL: form.IconURL, | |||||
| Color: form.Color, | |||||
| }) | |||||
| if err != nil { | |||||
| ctx.Handle(500, "Marshal", err) | |||||
| return | |||||
| } | |||||
| w := &models.Webhook{ | |||||
| RepoID: orCtx.RepoID, | |||||
| URL: form.PayloadURL, | |||||
| ContentType: models.JSON, | |||||
| HookEvent: ParseHookEvent(form.WebhookForm), | |||||
| IsActive: form.Active, | |||||
| HookTaskType: models.SLACK, | |||||
| Meta: string(meta), | |||||
| OrgID: orCtx.OrgID, | |||||
| } | |||||
| if err := w.UpdateEvent(); err != nil { | |||||
| ctx.Handle(500, "UpdateEvent", err) | |||||
| return | |||||
| } else if err := models.CreateWebhook(w); err != nil { | |||||
| ctx.Handle(500, "CreateWebhook", err) | |||||
| return | |||||
| } | |||||
| ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success")) | |||||
| ctx.Redirect(orCtx.Link + "/settings/hooks") | |||||
| } | |||||
| func checkWebhook(ctx *middleware.Context) (*OrgRepoCtx, *models.Webhook) { | |||||
| ctx.Data["RequireHighlightJS"] = true | |||||
| orCtx, err := getOrgRepoCtx(ctx) | |||||
| if err != nil { | |||||
| ctx.Handle(500, "getOrgRepoCtx", err) | |||||
| return nil, nil | |||||
| } | |||||
| ctx.Data["BaseLink"] = orCtx.Link | |||||
| w, err := models.GetWebhookByID(ctx.ParamsInt64(":id")) | |||||
| if err != nil { | |||||
| if models.IsErrWebhookNotExist(err) { | |||||
| ctx.Handle(404, "GetWebhookByID", nil) | |||||
| } else { | |||||
| ctx.Handle(500, "GetWebhookByID", err) | |||||
| } | |||||
| return nil, nil | |||||
| } | |||||
| switch w.HookTaskType { | |||||
| case models.SLACK: | |||||
| ctx.Data["SlackHook"] = w.GetSlackHook() | |||||
| ctx.Data["HookType"] = "slack" | |||||
| default: | |||||
| ctx.Data["HookType"] = "gogs" | |||||
| } | |||||
| ctx.Data["History"], err = w.History(1) | |||||
| if err != nil { | |||||
| ctx.Handle(500, "History", err) | |||||
| } | |||||
| return orCtx, w | |||||
| } | |||||
| func WebHooksEdit(ctx *middleware.Context) { | |||||
| ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook") | |||||
| ctx.Data["PageIsSettingsHooks"] = true | |||||
| ctx.Data["PageIsSettingsHooksEdit"] = true | |||||
| orCtx, w := checkWebhook(ctx) | |||||
| if ctx.Written() { | |||||
| return | |||||
| } | |||||
| ctx.Data["Webhook"] = w | |||||
| ctx.HTML(200, orCtx.NewTemplate) | |||||
| } | |||||
| func WebHooksEditPost(ctx *middleware.Context, form auth.NewWebhookForm) { | |||||
| ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook") | |||||
| ctx.Data["PageIsSettingsHooks"] = true | |||||
| ctx.Data["PageIsSettingsHooksEdit"] = true | |||||
| orCtx, w := checkWebhook(ctx) | |||||
| if ctx.Written() { | |||||
| return | |||||
| } | |||||
| ctx.Data["Webhook"] = w | |||||
| if ctx.HasError() { | |||||
| ctx.HTML(200, orCtx.NewTemplate) | |||||
| return | |||||
| } | |||||
| contentType := models.JSON | |||||
| if models.HookContentType(form.ContentType) == models.FORM { | |||||
| contentType = models.FORM | |||||
| } | |||||
| w.URL = form.PayloadURL | |||||
| w.ContentType = contentType | |||||
| w.Secret = form.Secret | |||||
| w.HookEvent = ParseHookEvent(form.WebhookForm) | |||||
| w.IsActive = form.Active | |||||
| if err := w.UpdateEvent(); err != nil { | |||||
| ctx.Handle(500, "UpdateEvent", err) | |||||
| return | |||||
| } else if err := models.UpdateWebhook(w); err != nil { | |||||
| ctx.Handle(500, "WebHooksEditPost", err) | |||||
| return | |||||
| } | |||||
| ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success")) | |||||
| ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID)) | |||||
| } | |||||
| func SlackHooksEditPost(ctx *middleware.Context, form auth.NewSlackHookForm) { | |||||
| ctx.Data["Title"] = ctx.Tr("repo.settings") | |||||
| ctx.Data["PageIsSettingsHooks"] = true | |||||
| ctx.Data["PageIsSettingsHooksEdit"] = true | |||||
| orCtx, w := checkWebhook(ctx) | |||||
| if ctx.Written() { | |||||
| return | |||||
| } | |||||
| ctx.Data["Webhook"] = w | |||||
| if ctx.HasError() { | |||||
| ctx.HTML(200, orCtx.NewTemplate) | |||||
| return | |||||
| } | |||||
| meta, err := json.Marshal(&models.SlackMeta{ | |||||
| Channel: form.Channel, | |||||
| Username: form.Username, | |||||
| IconURL: form.IconURL, | |||||
| Color: form.Color, | |||||
| }) | |||||
| if err != nil { | |||||
| ctx.Handle(500, "Marshal", err) | |||||
| return | |||||
| } | |||||
| w.URL = form.PayloadURL | |||||
| w.Meta = string(meta) | |||||
| w.HookEvent = ParseHookEvent(form.WebhookForm) | |||||
| w.IsActive = form.Active | |||||
| if err := w.UpdateEvent(); err != nil { | |||||
| ctx.Handle(500, "UpdateEvent", err) | |||||
| return | |||||
| } else if err := models.UpdateWebhook(w); err != nil { | |||||
| ctx.Handle(500, "UpdateWebhook", err) | |||||
| return | |||||
| } | |||||
| ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success")) | |||||
| ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID)) | |||||
| } | |||||
| func DeleteWebhook(ctx *middleware.Context) { | |||||
| if err := models.DeleteWebhook(ctx.QueryInt64("id")); err != nil { | |||||
| ctx.Flash.Error("DeleteWebhook: " + err.Error()) | |||||
| } else { | |||||
| ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success")) | |||||
| } | |||||
| ctx.JSON(200, map[string]interface{}{ | |||||
| "redirect": ctx.Repo.RepoLink + "/settings/hooks", | |||||
| }) | |||||
| } | |||||
| func parseOwnerAndRepo(ctx *middleware.Context) (*models.User, *models.Repository) { | func parseOwnerAndRepo(ctx *middleware.Context) (*models.User, *models.Repository) { | ||||
| owner, err := models.GetUserByName(ctx.Params(":username")) | owner, err := models.GetUserByName(ctx.Params(":username")) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -0,0 +1,385 @@ | |||||
| // Copyright 2015 The Gogs 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 repo | |||||
| import ( | |||||
| "encoding/json" | |||||
| "errors" | |||||
| "fmt" | |||||
| "strings" | |||||
| "github.com/Unknwon/com" | |||||
| api "github.com/gogits/go-gogs-client" | |||||
| "github.com/gogits/gogs/models" | |||||
| "github.com/gogits/gogs/modules/auth" | |||||
| "github.com/gogits/gogs/modules/base" | |||||
| "github.com/gogits/gogs/modules/middleware" | |||||
| "github.com/gogits/gogs/modules/setting" | |||||
| ) | |||||
| const ( | |||||
| HOOKS base.TplName = "repo/settings/hooks" | |||||
| HOOK_NEW base.TplName = "repo/settings/hook_new" | |||||
| ORG_HOOK_NEW base.TplName = "org/settings/hook_new" | |||||
| ) | |||||
| func Webhooks(ctx *middleware.Context) { | |||||
| ctx.Data["Title"] = ctx.Tr("repo.settings.hooks") | |||||
| ctx.Data["PageIsSettingsHooks"] = true | |||||
| ctx.Data["BaseLink"] = ctx.Repo.RepoLink | |||||
| ctx.Data["Description"] = ctx.Tr("repo.settings.hooks_desc", "https://github.com/gogits/go-gogs-client/wiki/Repositories-Webhooks") | |||||
| ws, err := models.GetWebhooksByRepoID(ctx.Repo.Repository.ID) | |||||
| if err != nil { | |||||
| ctx.Handle(500, "GetWebhooksByRepoID", err) | |||||
| return | |||||
| } | |||||
| ctx.Data["Webhooks"] = ws | |||||
| ctx.HTML(200, HOOKS) | |||||
| } | |||||
| type OrgRepoCtx struct { | |||||
| OrgID int64 | |||||
| RepoID int64 | |||||
| Link string | |||||
| NewTemplate base.TplName | |||||
| } | |||||
| // getOrgRepoCtx determines whether this is a repo context or organization context. | |||||
| func getOrgRepoCtx(ctx *middleware.Context) (*OrgRepoCtx, error) { | |||||
| if len(ctx.Repo.RepoLink) > 0 { | |||||
| return &OrgRepoCtx{ | |||||
| RepoID: ctx.Repo.Repository.ID, | |||||
| Link: ctx.Repo.RepoLink, | |||||
| NewTemplate: HOOK_NEW, | |||||
| }, nil | |||||
| } | |||||
| if len(ctx.Org.OrgLink) > 0 { | |||||
| return &OrgRepoCtx{ | |||||
| OrgID: ctx.Org.Organization.Id, | |||||
| Link: ctx.Org.OrgLink, | |||||
| NewTemplate: ORG_HOOK_NEW, | |||||
| }, nil | |||||
| } | |||||
| return nil, errors.New("Unable to set OrgRepo context") | |||||
| } | |||||
| func checkHookType(ctx *middleware.Context) string { | |||||
| hookType := strings.ToLower(ctx.Params(":type")) | |||||
| if !com.IsSliceContainsStr(setting.Webhook.Types, hookType) { | |||||
| ctx.Handle(404, "checkHookType", nil) | |||||
| return "" | |||||
| } | |||||
| return hookType | |||||
| } | |||||
| func WebhooksNew(ctx *middleware.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) | |||||
| if err != nil { | |||||
| ctx.Handle(500, "getOrgRepoCtx", err) | |||||
| return | |||||
| } | |||||
| ctx.Data["HookType"] = checkHookType(ctx) | |||||
| if ctx.Written() { | |||||
| return | |||||
| } | |||||
| ctx.Data["BaseLink"] = orCtx.Link | |||||
| ctx.HTML(200, orCtx.NewTemplate) | |||||
| } | |||||
| func ParseHookEvent(form auth.WebhookForm) *models.HookEvent { | |||||
| return &models.HookEvent{ | |||||
| PushOnly: form.PushOnly(), | |||||
| SendEverything: form.SendEverything(), | |||||
| ChooseEvents: form.ChooseEvents(), | |||||
| HookEvents: models.HookEvents{ | |||||
| Create: form.Create, | |||||
| Push: form.Push, | |||||
| }, | |||||
| } | |||||
| } | |||||
| func WebHooksNewPost(ctx *middleware.Context, form auth.NewWebhookForm) { | |||||
| 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{}} | |||||
| ctx.Data["HookType"] = "gogs" | |||||
| orCtx, err := getOrgRepoCtx(ctx) | |||||
| if err != nil { | |||||
| ctx.Handle(500, "getOrgRepoCtx", err) | |||||
| return | |||||
| } | |||||
| ctx.Data["BaseLink"] = orCtx.Link | |||||
| if ctx.HasError() { | |||||
| ctx.HTML(200, orCtx.NewTemplate) | |||||
| return | |||||
| } | |||||
| contentType := models.JSON | |||||
| if models.HookContentType(form.ContentType) == models.FORM { | |||||
| contentType = models.FORM | |||||
| } | |||||
| w := &models.Webhook{ | |||||
| RepoID: orCtx.RepoID, | |||||
| URL: form.PayloadURL, | |||||
| ContentType: contentType, | |||||
| Secret: form.Secret, | |||||
| HookEvent: ParseHookEvent(form.WebhookForm), | |||||
| IsActive: form.Active, | |||||
| HookTaskType: models.GOGS, | |||||
| OrgID: orCtx.OrgID, | |||||
| } | |||||
| if err := w.UpdateEvent(); err != nil { | |||||
| ctx.Handle(500, "UpdateEvent", err) | |||||
| return | |||||
| } else if err := models.CreateWebhook(w); err != nil { | |||||
| ctx.Handle(500, "CreateWebhook", err) | |||||
| return | |||||
| } | |||||
| ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success")) | |||||
| ctx.Redirect(orCtx.Link + "/settings/hooks") | |||||
| } | |||||
| func SlackHooksNewPost(ctx *middleware.Context, form auth.NewSlackHookForm) { | |||||
| ctx.Data["Title"] = ctx.Tr("repo.settings") | |||||
| ctx.Data["PageIsSettingsHooks"] = true | |||||
| ctx.Data["PageIsSettingsHooksNew"] = true | |||||
| ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}} | |||||
| orCtx, err := getOrgRepoCtx(ctx) | |||||
| if err != nil { | |||||
| ctx.Handle(500, "getOrgRepoCtx", err) | |||||
| return | |||||
| } | |||||
| if ctx.HasError() { | |||||
| ctx.HTML(200, orCtx.NewTemplate) | |||||
| return | |||||
| } | |||||
| meta, err := json.Marshal(&models.SlackMeta{ | |||||
| Channel: form.Channel, | |||||
| Username: form.Username, | |||||
| IconURL: form.IconURL, | |||||
| Color: form.Color, | |||||
| }) | |||||
| if err != nil { | |||||
| ctx.Handle(500, "Marshal", err) | |||||
| return | |||||
| } | |||||
| w := &models.Webhook{ | |||||
| RepoID: orCtx.RepoID, | |||||
| URL: form.PayloadURL, | |||||
| ContentType: models.JSON, | |||||
| HookEvent: ParseHookEvent(form.WebhookForm), | |||||
| IsActive: form.Active, | |||||
| HookTaskType: models.SLACK, | |||||
| Meta: string(meta), | |||||
| OrgID: orCtx.OrgID, | |||||
| } | |||||
| if err := w.UpdateEvent(); err != nil { | |||||
| ctx.Handle(500, "UpdateEvent", err) | |||||
| return | |||||
| } else if err := models.CreateWebhook(w); err != nil { | |||||
| ctx.Handle(500, "CreateWebhook", err) | |||||
| return | |||||
| } | |||||
| ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success")) | |||||
| ctx.Redirect(orCtx.Link + "/settings/hooks") | |||||
| } | |||||
| func checkWebhook(ctx *middleware.Context) (*OrgRepoCtx, *models.Webhook) { | |||||
| ctx.Data["RequireHighlightJS"] = true | |||||
| orCtx, err := getOrgRepoCtx(ctx) | |||||
| if err != nil { | |||||
| ctx.Handle(500, "getOrgRepoCtx", err) | |||||
| return nil, nil | |||||
| } | |||||
| ctx.Data["BaseLink"] = orCtx.Link | |||||
| w, err := models.GetWebhookByID(ctx.ParamsInt64(":id")) | |||||
| if err != nil { | |||||
| if models.IsErrWebhookNotExist(err) { | |||||
| ctx.Handle(404, "GetWebhookByID", nil) | |||||
| } else { | |||||
| ctx.Handle(500, "GetWebhookByID", err) | |||||
| } | |||||
| return nil, nil | |||||
| } | |||||
| switch w.HookTaskType { | |||||
| case models.SLACK: | |||||
| ctx.Data["SlackHook"] = w.GetSlackHook() | |||||
| ctx.Data["HookType"] = "slack" | |||||
| default: | |||||
| ctx.Data["HookType"] = "gogs" | |||||
| } | |||||
| ctx.Data["History"], err = w.History(1) | |||||
| if err != nil { | |||||
| ctx.Handle(500, "History", err) | |||||
| } | |||||
| return orCtx, w | |||||
| } | |||||
| func WebHooksEdit(ctx *middleware.Context) { | |||||
| ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook") | |||||
| ctx.Data["PageIsSettingsHooks"] = true | |||||
| ctx.Data["PageIsSettingsHooksEdit"] = true | |||||
| orCtx, w := checkWebhook(ctx) | |||||
| if ctx.Written() { | |||||
| return | |||||
| } | |||||
| ctx.Data["Webhook"] = w | |||||
| ctx.HTML(200, orCtx.NewTemplate) | |||||
| } | |||||
| func WebHooksEditPost(ctx *middleware.Context, form auth.NewWebhookForm) { | |||||
| ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook") | |||||
| ctx.Data["PageIsSettingsHooks"] = true | |||||
| ctx.Data["PageIsSettingsHooksEdit"] = true | |||||
| orCtx, w := checkWebhook(ctx) | |||||
| if ctx.Written() { | |||||
| return | |||||
| } | |||||
| ctx.Data["Webhook"] = w | |||||
| if ctx.HasError() { | |||||
| ctx.HTML(200, orCtx.NewTemplate) | |||||
| return | |||||
| } | |||||
| contentType := models.JSON | |||||
| if models.HookContentType(form.ContentType) == models.FORM { | |||||
| contentType = models.FORM | |||||
| } | |||||
| w.URL = form.PayloadURL | |||||
| w.ContentType = contentType | |||||
| w.Secret = form.Secret | |||||
| w.HookEvent = ParseHookEvent(form.WebhookForm) | |||||
| w.IsActive = form.Active | |||||
| if err := w.UpdateEvent(); err != nil { | |||||
| ctx.Handle(500, "UpdateEvent", err) | |||||
| return | |||||
| } else if err := models.UpdateWebhook(w); err != nil { | |||||
| ctx.Handle(500, "WebHooksEditPost", err) | |||||
| return | |||||
| } | |||||
| ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success")) | |||||
| ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID)) | |||||
| } | |||||
| func SlackHooksEditPost(ctx *middleware.Context, form auth.NewSlackHookForm) { | |||||
| ctx.Data["Title"] = ctx.Tr("repo.settings") | |||||
| ctx.Data["PageIsSettingsHooks"] = true | |||||
| ctx.Data["PageIsSettingsHooksEdit"] = true | |||||
| orCtx, w := checkWebhook(ctx) | |||||
| if ctx.Written() { | |||||
| return | |||||
| } | |||||
| ctx.Data["Webhook"] = w | |||||
| if ctx.HasError() { | |||||
| ctx.HTML(200, orCtx.NewTemplate) | |||||
| return | |||||
| } | |||||
| meta, err := json.Marshal(&models.SlackMeta{ | |||||
| Channel: form.Channel, | |||||
| Username: form.Username, | |||||
| IconURL: form.IconURL, | |||||
| Color: form.Color, | |||||
| }) | |||||
| if err != nil { | |||||
| ctx.Handle(500, "Marshal", err) | |||||
| return | |||||
| } | |||||
| w.URL = form.PayloadURL | |||||
| w.Meta = string(meta) | |||||
| w.HookEvent = ParseHookEvent(form.WebhookForm) | |||||
| w.IsActive = form.Active | |||||
| if err := w.UpdateEvent(); err != nil { | |||||
| ctx.Handle(500, "UpdateEvent", err) | |||||
| return | |||||
| } else if err := models.UpdateWebhook(w); err != nil { | |||||
| ctx.Handle(500, "UpdateWebhook", err) | |||||
| return | |||||
| } | |||||
| ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success")) | |||||
| ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID)) | |||||
| } | |||||
| func TestWebhook(ctx *middleware.Context) { | |||||
| p := &api.PushPayload{ | |||||
| Ref: ctx.Repo.Repository.DefaultBranch, | |||||
| Before: ctx.Repo.CommitID, | |||||
| After: ctx.Repo.CommitID, | |||||
| Commits: []*api.PayloadCommit{ | |||||
| { | |||||
| ID: ctx.Repo.CommitID, | |||||
| Message: ctx.Repo.Commit.Message(), | |||||
| URL: ctx.Repo.RepoLink + "/commit/" + ctx.Repo.CommitID, | |||||
| Author: &api.PayloadAuthor{ | |||||
| Name: ctx.Repo.Commit.Author.Name, | |||||
| Email: ctx.Repo.Commit.Author.Email, | |||||
| }, | |||||
| }, | |||||
| }, | |||||
| Repo: ctx.Repo.Repository.ComposePayload(), | |||||
| Sender: &api.PayloadUser{ | |||||
| UserName: ctx.User.Name, | |||||
| ID: ctx.User.Id, | |||||
| AvatarUrl: setting.AppUrl + ctx.User.RelAvatarLink(), | |||||
| }, | |||||
| } | |||||
| if err := models.PrepareWebhooks(ctx.Repo.Repository, models.HOOK_EVENT_PUSH, p); err != nil { | |||||
| ctx.Flash.Error("PrepareWebhooks: " + err.Error()) | |||||
| ctx.Status(500) | |||||
| } else { | |||||
| go models.HookQueue.Add(ctx.Repo.Repository.ID) | |||||
| ctx.Flash.Info(ctx.Tr("repo.settings.webhook.test_delivery_success")) | |||||
| ctx.Status(200) | |||||
| } | |||||
| } | |||||
| func DeleteWebhook(ctx *middleware.Context) { | |||||
| if err := models.DeleteWebhook(ctx.QueryInt64("id")); err != nil { | |||||
| ctx.Flash.Error("DeleteWebhook: " + err.Error()) | |||||
| } else { | |||||
| ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success")) | |||||
| } | |||||
| ctx.JSON(200, map[string]interface{}{ | |||||
| "redirect": ctx.Repo.RepoLink + "/settings/hooks", | |||||
| }) | |||||
| } | |||||
| @@ -1 +1 @@ | |||||
| 0.7.31.1205 Beta | |||||
| 0.7.32.1205 Beta | |||||
| @@ -1,6 +1,12 @@ | |||||
| {{if .PageIsSettingsHooksEdit}} | {{if .PageIsSettingsHooksEdit}} | ||||
| <h4 class="ui top attached header"> | <h4 class="ui top attached header"> | ||||
| {{.i18n.Tr "repo.settings.recent_deliveries"}} | {{.i18n.Tr "repo.settings.recent_deliveries"}} | ||||
| {{if .IsRepositoryAdmin}} | |||||
| <div class="ui right"> | |||||
| <button class="ui teal tiny button poping up" id="test-delivery" data-content= | |||||
| "{{.i18n.Tr "repo.settings.webhook.test_delivery_desc"}}" data-variation="inverted tiny" data-link="{{.Link}}/test" data-redirect="{{.Link}}">{{.i18n.Tr "repo.settings.webhook.test_delivery"}}</button> | |||||
| </div> | |||||
| {{end}} | |||||
| </h4> | </h4> | ||||
| <div class="ui attached table segment"> | <div class="ui attached table segment"> | ||||
| <div class="ui hook history list"> | <div class="ui hook history list"> | ||||