| @@ -174,6 +174,7 @@ func runWeb(*cli.Context) { | |||
| r.Post("/settings/collaboration", repo.CollaborationPost) | |||
| r.Get("/settings/hooks", repo.WebHooks) // TODO | |||
| r.Get("/settings/hooks/add", repo.WebHooksAdd) | |||
| r.Post("/settings/hooks/add", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksAddPost) | |||
| r.Get("/settings/hooks/id", repo.WebHooksEdit) | |||
| }, reqSignIn, middleware.RepoAssignment(true), reqOwner()) | |||
| @@ -34,7 +34,7 @@ var ( | |||
| func init() { | |||
| tables = append(tables, new(User), new(PublicKey), new(Repository), new(Watch), | |||
| new(Action), new(Access), new(Issue), new(Comment), new(Oauth2), new(Follow), | |||
| new(Mirror), new(Release), new(LoginSource)) | |||
| new(Mirror), new(Release), new(LoginSource), new(Webhook)) | |||
| } | |||
| func LoadModelsConfig() { | |||
| @@ -0,0 +1,58 @@ | |||
| // Copyright 2014 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 models | |||
| import ( | |||
| "encoding/json" | |||
| "github.com/gogits/gogs/modules/log" | |||
| ) | |||
| // Content types. | |||
| const ( | |||
| CT_JSON = iota + 1 | |||
| CT_FORM | |||
| ) | |||
| type HookEvent struct { | |||
| PushOnly bool `json:"push_only"` | |||
| } | |||
| type Webhook struct { | |||
| Id int64 | |||
| RepoId int64 | |||
| Payload string `xorm:"TEXT"` | |||
| ContentType int | |||
| Secret string `xorm:"TEXT"` | |||
| Events string `xorm:"TEXT"` | |||
| IsSsl bool | |||
| IsActive bool | |||
| } | |||
| func (w *Webhook) GetEvent() *HookEvent { | |||
| h := &HookEvent{} | |||
| if err := json.Unmarshal([]byte(w.Events), h); err != nil { | |||
| log.Error("webhook.GetEvent(%d): %v", w.Id, err) | |||
| } | |||
| return h | |||
| } | |||
| func (w *Webhook) SaveEvent(h *HookEvent) error { | |||
| data, err := json.Marshal(h) | |||
| w.Events = string(data) | |||
| return err | |||
| } | |||
| // CreateWebhook creates new webhook. | |||
| func CreateWebhook(w *Webhook) error { | |||
| _, err := orm.Insert(w) | |||
| return err | |||
| } | |||
| // GetWebhooksByRepoId returns all webhooks of repository. | |||
| func GetWebhooksByRepoId(repoId int64) (ws []*Webhook, err error) { | |||
| err = orm.Find(&ws, &Webhook{RepoId: repoId}) | |||
| return ws, err | |||
| } | |||
| @@ -83,3 +83,24 @@ func (f *RepoSettingForm) Validate(errors *binding.BindingErrors, req *http.Requ | |||
| data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| validate(errors, data, f) | |||
| } | |||
| type NewWebhookForm struct { | |||
| Url string `form:"url" binding:"Required;Url"` | |||
| ContentType string `form:"content_type" binding:"Required"` | |||
| Secret string `form:"secret""` | |||
| PushOnly bool `form:"push_only"` | |||
| Active bool `form:"active"` | |||
| } | |||
| func (f *NewWebhookForm) Name(field string) string { | |||
| names := map[string]string{ | |||
| "Url": "Payload URL", | |||
| "ContentType": "Content type", | |||
| } | |||
| return names[field] | |||
| } | |||
| func (f *NewWebhookForm) Validate(errors *binding.BindingErrors, req *http.Request, context martini.Context) { | |||
| data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| validate(errors, data, f) | |||
| } | |||
| @@ -199,18 +199,62 @@ func CollaborationPost(ctx *middleware.Context) { | |||
| func WebHooks(ctx *middleware.Context) { | |||
| ctx.Data["IsRepoToolbarWebHooks"] = true | |||
| ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - Web Hooks" | |||
| ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - Webhooks" | |||
| ws, err := models.GetWebhooksByRepoId(ctx.Repo.Repository.Id) | |||
| if err != nil { | |||
| ctx.Handle(500, "repo.WebHooks(GetWebhooksByRepoId)", err) | |||
| return | |||
| } | |||
| ctx.Data["Webhooks"] = ws | |||
| ctx.HTML(200, "repo/hooks") | |||
| } | |||
| func WebHooksAdd(ctx *middleware.Context) { | |||
| ctx.Data["IsRepoToolbarWebHooks"] = true | |||
| ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - Add Web Hook" | |||
| ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - Add Webhook" | |||
| ctx.HTML(200, "repo/hooks_add") | |||
| } | |||
| func WebHooksAddPost(ctx *middleware.Context, form auth.NewWebhookForm) { | |||
| ctx.Data["IsRepoToolbarWebHooks"] = true | |||
| ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - Add Webhook" | |||
| if ctx.HasError() { | |||
| ctx.HTML(200, "repo/hooks_add") | |||
| return | |||
| } | |||
| ct := models.CT_JSON | |||
| if form.ContentType == "form" { | |||
| ct = models.CT_FORM | |||
| } | |||
| w := &models.Webhook{ | |||
| RepoId: ctx.Repo.Repository.Id, | |||
| Payload: form.Url, | |||
| ContentType: ct, | |||
| Secret: form.Secret, | |||
| IsActive: form.Active, | |||
| } | |||
| h := &models.HookEvent{ | |||
| PushOnly: form.PushOnly, | |||
| } | |||
| if err := w.SaveEvent(h); err != nil { | |||
| ctx.Handle(500, "repo.WebHooksAddPost(SaveEvent)", err) | |||
| return | |||
| } else if err := models.CreateWebhook(w); err != nil { | |||
| ctx.Handle(500, "repo.WebHooksAddPost(CreateWebhook)", err) | |||
| return | |||
| } | |||
| ctx.Flash.Success("New webhook has been added.") | |||
| ctx.Redirect(ctx.Repo.RepoLink + "/settings/hooks") | |||
| } | |||
| func WebHooksEdit(ctx *middleware.Context) { | |||
| ctx.Data["IsRepoToolbarWebHooks"] = true | |||
| ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - Web Hook Name" | |||
| ctx.Data["Title"] = strings.TrimPrefix(ctx.Repo.RepoLink, "/") + " - Webhook" | |||
| ctx.HTML(200, "repo/hooks_edit") | |||
| } | |||
| @@ -13,22 +13,18 @@ | |||
| <div class="panel-body"> | |||
| <p>Webhooks allow external services to be notified when certain events happen on GitHub. When the specified events happen, we'll send a POST request to each of the URLs you provide. Learn more in our Webhooks Guide.<br/> </p> | |||
| <ul id="repo-hooks-list" class="list-unstyled"> | |||
| {{range .Webhooks}} | |||
| <li> | |||
| <span class="pull-left status text-success"><i class="fa fa-check"></i></span> | |||
| <a class="link" href="#">https://inbox-app.wercker.com/commits/11aea6e535af5eefc2a29bc25a23e89a</a> | |||
| <a href="{{$.RepoLink}}/settings/hooks?remove=" class="remove-hook pull-right"><i class="fa fa-times"></i></a> | |||
| <a href="{{$.RepoLink}}/settings/hooks/id" class="edit-hook pull-right"><i class="fa fa-pencil"></i></a> | |||
| </li> | |||
| <li> | |||
| <span class="pull-left status text-success"><i class="fa fa-check"></i></span> | |||
| <a class="link" href="#">https://inbox-app.wercker.com/commits/11aea6e535af5eefc2a29bc25a23e89a</a> | |||
| <a href="{{$.RepoLink}}/settings/hooks?remove=" class="remove-hook pull-right"><i class="fa fa-times"></i></a> | |||
| <a href="{{$.RepoLink}}/settings/hooks/id" class="edit-hook pull-right"><i class="fa fa-pencil"></i></a> | |||
| <a class="link" href="{{$.RepoLink}}/settings/hooks/{{.Id}}">{{.Payload}}</a> | |||
| <a href="{{$.RepoLink}}/settings/hooks?remove={{.Id}}" class="remove-hook pull-right"><i class="fa fa-times"></i></a> | |||
| <a href="{{$.RepoLink}}/settings/hooks/{{.Id}}" class="edit-hook pull-right"><i class="fa fa-pencil"></i></a> | |||
| </li> | |||
| {{end}} | |||
| </ul> | |||
| </div> | |||
| <div class="panel-footer"> | |||
| <a href="/{{.Owner.Name}}/{{.Repository.Name}}/settings/hooks/add"><button class="btn btn-primary">Add Webhook</button></a> | |||
| <a href="{{.RepoLink}}/settings/hooks/add"><button class="btn btn-primary">Add Webhook</button></a> | |||
| </div> | |||
| </div> | |||
| @@ -6,7 +6,8 @@ | |||
| {{template "repo/setting_nav" .}} | |||
| <div id="repo-setting-container" class="col-md-10"> | |||
| {{template "base/alert" .}} | |||
| <form id="repo-hooks-add-form" action="/{{.Owner.Name}}/{{.Repository.Name}}/settings/hooks/add" method="post"> | |||
| <form id="repo-hooks-add-form" action="{{.RepoLink}}/settings/hooks/add" method="post"> | |||
| {{.CsrfTokenHtml}} | |||
| <div class="panel panel-default"> | |||
| <div class="panel-heading"> | |||
| Add Webhook | |||
| @@ -18,31 +19,31 @@ | |||
| <hr/> | |||
| <div class="form-group"> | |||
| <label for="payload-url">Payload URL</label> | |||
| <input id="payload-url" class="form-control" type="url" required="required" name="url"/> | |||
| <input id="payload-url" name="url" class="form-control" type="url" required="required"/> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label for="payload-version">Payload Version</label> | |||
| <select name="version" id="payload-version" class="form-control"> | |||
| <label for="content-type">Content type</label> | |||
| <select id="content-type" name="content_type" class="form-control"> | |||
| <option value="json">application/json</option> | |||
| </select> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label for="payload-secret">Secret</label> | |||
| <input id="payload-secret" class="form-control" type="text" required="required" name="secret"/> | |||
| <input id="payload-secret" name="secret" class="form-control" type="text"/> | |||
| </div> | |||
| <hr/> | |||
| <div class="form-group"> | |||
| <label>Which events would you like to trigger this webhook?</label> | |||
| <div class="radio"> | |||
| <label> | |||
| <input class="form-control" type="radio" value="push" checked name="trigger"/> Just the <i>push</i> event. | |||
| <input class="form-control" name="push_only" type="radio" checked name="trigger"/> Just the <i>push</i> event. | |||
| </label> | |||
| </div> | |||
| </div> | |||
| <hr/> | |||
| <div class="form-group"> | |||
| <label> | |||
| <input type="checkbox" name="active" value="Active" checked/> | |||
| <input type="checkbox" name="active" checked/> | |||
| Active | |||
| </label> | |||
| <p class="help-block">We will deliver event details when this hook is triggered.</p> | |||
| @@ -36,7 +36,7 @@ | |||
| <div class="radio"> | |||
| <label> | |||
| <input class="form-control" type="radio" value="push" name="trigger"/> Just the | |||
| <input class="form-control" type="radio" value="push" checked name="trigger"/> Just the | |||
| <i>push</i> event. | |||
| </label> | |||
| </div> | |||