| @@ -531,6 +531,12 @@ settings.content_type = Content Type | |||||
| settings.secret = Secret | settings.secret = Secret | ||||
| settings.event_desc = Upon which events should this webhook be triggered? | settings.event_desc = Upon which events should this webhook be triggered? | ||||
| settings.event_push_only = Just the <code>push</code> event. | settings.event_push_only = Just the <code>push</code> event. | ||||
| settings.event_send_everything = I need <strong>everything</strong>. | |||||
| settings.event_choose = Let me choose what I need. | |||||
| settings.event_create = Create | |||||
| settings.event_create_desc = Branch, or tag created | |||||
| settings.event_push = Push | |||||
| settings.event_push_desc = Git push to a repository | |||||
| settings.active = Active | settings.active = Active | ||||
| settings.active_helper = Details regarding the event which triggered the hook will be delivered as well. | settings.active_helper = Details regarding the event which triggered the hook will be delivered as well. | ||||
| settings.add_hook_success = New webhook has been added. | settings.add_hook_success = New webhook has been added. | ||||
| @@ -16,6 +16,8 @@ import ( | |||||
| "github.com/go-xorm/xorm" | "github.com/go-xorm/xorm" | ||||
| api "github.com/gogits/go-gogs-client" | |||||
| "github.com/gogits/gogs/modules/base" | "github.com/gogits/gogs/modules/base" | ||||
| "github.com/gogits/gogs/modules/git" | "github.com/gogits/gogs/modules/git" | ||||
| "github.com/gogits/gogs/modules/log" | "github.com/gogits/gogs/modules/log" | ||||
| @@ -290,20 +292,50 @@ func updateIssuesCommit(u *User, repo *Repository, repoUserName, repoName string | |||||
| } | } | ||||
| // CommitRepoAction adds new action for committing repository. | // CommitRepoAction adds new action for committing repository. | ||||
| func CommitRepoAction(userID, repoUserID int64, userName, actEmail string, | |||||
| repoID int64, repoUserName, repoName string, refFullName string, commit *base.PushCommits, oldCommitID string, newCommitID string) error { | |||||
| func CommitRepoAction( | |||||
| userID, repoUserID int64, | |||||
| userName, actEmail string, | |||||
| repoID int64, | |||||
| repoUserName, repoName string, | |||||
| refFullName string, | |||||
| commit *base.PushCommits, | |||||
| oldCommitID string, newCommitID string) error { | |||||
| u, err := GetUserByID(userID) | |||||
| if err != nil { | |||||
| return fmt.Errorf("GetUserByID: %v", err) | |||||
| } | |||||
| repo, err := GetRepositoryByName(repoUserID, repoName) | |||||
| if err != nil { | |||||
| return fmt.Errorf("GetRepositoryByName: %v", err) | |||||
| } else if err = repo.GetOwner(); err != nil { | |||||
| return fmt.Errorf("GetOwner: %v", err) | |||||
| } | |||||
| isNewBranch := false | |||||
| opType := COMMIT_REPO | opType := COMMIT_REPO | ||||
| // Check it's tag push or branch. | // Check it's tag push or branch. | ||||
| if strings.HasPrefix(refFullName, "refs/tags/") { | if strings.HasPrefix(refFullName, "refs/tags/") { | ||||
| opType = PUSH_TAG | opType = PUSH_TAG | ||||
| commit = &base.PushCommits{} | commit = &base.PushCommits{} | ||||
| } | |||||
| } else { | |||||
| // if not the first commit, set the compareUrl | |||||
| if !strings.HasPrefix(oldCommitID, "0000000") { | |||||
| commit.CompareUrl = fmt.Sprintf("%s/%s/compare/%s...%s", repoUserName, repoName, oldCommitID, newCommitID) | |||||
| } else { | |||||
| isNewBranch = true | |||||
| } | |||||
| repoLink := fmt.Sprintf("%s%s/%s", setting.AppUrl, repoUserName, repoName) | |||||
| // if not the first commit, set the compareUrl | |||||
| if !strings.HasPrefix(oldCommitID, "0000000") { | |||||
| commit.CompareUrl = fmt.Sprintf("%s/%s/compare/%s...%s", repoUserName, repoName, oldCommitID, newCommitID) | |||||
| // Change repository bare status and update last updated time. | |||||
| repo.IsBare = false | |||||
| if err = UpdateRepository(repo, false); err != nil { | |||||
| return fmt.Errorf("UpdateRepository: %v", err) | |||||
| } | |||||
| if err = updateIssuesCommit(u, repo, repoUserName, repoName, commit.Commits); err != nil { | |||||
| log.Debug("updateIssuesCommit: %v", err) | |||||
| } | |||||
| } | } | ||||
| bs, err := json.Marshal(commit) | bs, err := json.Marshal(commit) | ||||
| @@ -313,26 +345,6 @@ func CommitRepoAction(userID, repoUserID int64, userName, actEmail string, | |||||
| refName := git.RefEndName(refFullName) | refName := git.RefEndName(refFullName) | ||||
| // Change repository bare status and update last updated time. | |||||
| repo, err := GetRepositoryByName(repoUserID, repoName) | |||||
| if err != nil { | |||||
| return fmt.Errorf("GetRepositoryByName: %v", err) | |||||
| } | |||||
| repo.IsBare = false | |||||
| if err = UpdateRepository(repo, false); err != nil { | |||||
| return fmt.Errorf("UpdateRepository: %v", err) | |||||
| } | |||||
| u, err := GetUserByID(userID) | |||||
| if err != nil { | |||||
| return fmt.Errorf("GetUserByID: %v", err) | |||||
| } | |||||
| err = updateIssuesCommit(u, repo, repoUserName, repoName, commit.Commits) | |||||
| if err != nil { | |||||
| log.Debug("updateIssuesCommit: ", err) | |||||
| } | |||||
| if err = NotifyWatchers(&Action{ | if err = NotifyWatchers(&Action{ | ||||
| ActUserID: u.Id, | ActUserID: u.Id, | ||||
| ActUserName: userName, | ActUserName: userName, | ||||
| @@ -345,32 +357,24 @@ func CommitRepoAction(userID, repoUserID int64, userName, actEmail string, | |||||
| RefName: refName, | RefName: refName, | ||||
| IsPrivate: repo.IsPrivate, | IsPrivate: repo.IsPrivate, | ||||
| }); err != nil { | }); err != nil { | ||||
| return errors.New("NotifyWatchers: " + err.Error()) | |||||
| } | |||||
| // New push event hook. | |||||
| if err := repo.GetOwner(); err != nil { | |||||
| return errors.New("GetOwner: " + err.Error()) | |||||
| } | |||||
| return fmt.Errorf("NotifyWatchers: %v", err) | |||||
| ws, err := GetActiveWebhooksByRepoId(repo.ID) | |||||
| if err != nil { | |||||
| return errors.New("GetActiveWebhooksByRepoId: " + err.Error()) | |||||
| } | } | ||||
| // check if repo belongs to org and append additional webhooks | |||||
| if repo.Owner.IsOrganization() { | |||||
| // get hooks for org | |||||
| orgws, err := GetActiveWebhooksByOrgId(repo.OwnerID) | |||||
| if err != nil { | |||||
| return errors.New("GetActiveWebhooksByOrgId: " + err.Error()) | |||||
| } | |||||
| ws = append(ws, orgws...) | |||||
| } | |||||
| if len(ws) == 0 { | |||||
| return nil | |||||
| 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, | |||||
| } | } | ||||
| pusher_email, pusher_name := "", "" | pusher_email, pusher_name := "", "" | ||||
| @@ -379,83 +383,66 @@ func CommitRepoAction(userID, repoUserID int64, userName, actEmail string, | |||||
| pusher_email = pusher.Email | pusher_email = pusher.Email | ||||
| pusher_name = pusher.DisplayName() | pusher_name = pusher.DisplayName() | ||||
| } | } | ||||
| payloadSender := &api.PayloadUser{ | |||||
| UserName: pusher.Name, | |||||
| ID: pusher.Id, | |||||
| AvatarUrl: setting.AppUrl + pusher.RelAvatarLink(), | |||||
| } | |||||
| commits := make([]*PayloadCommit, len(commit.Commits)) | |||||
| for i, cmt := range commit.Commits { | |||||
| author_username := "" | |||||
| author, err := GetUserByEmail(cmt.AuthorEmail) | |||||
| if err == nil { | |||||
| author_username = author.Name | |||||
| switch opType { | |||||
| case COMMIT_REPO: // Push | |||||
| commits := make([]*api.PayloadCommit, len(commit.Commits)) | |||||
| for i, cmt := range commit.Commits { | |||||
| author_username := "" | |||||
| author, err := GetUserByEmail(cmt.AuthorEmail) | |||||
| if err == nil { | |||||
| author_username = author.Name | |||||
| } | |||||
| commits[i] = &api.PayloadCommit{ | |||||
| ID: cmt.Sha1, | |||||
| Message: cmt.Message, | |||||
| URL: fmt.Sprintf("%s/commit/%s", repoLink, cmt.Sha1), | |||||
| Author: &api.PayloadAuthor{ | |||||
| Name: cmt.AuthorName, | |||||
| Email: cmt.AuthorEmail, | |||||
| UserName: author_username, | |||||
| }, | |||||
| } | |||||
| } | } | ||||
| commits[i] = &PayloadCommit{ | |||||
| Id: cmt.Sha1, | |||||
| Message: cmt.Message, | |||||
| Url: fmt.Sprintf("%s/commit/%s", repoLink, cmt.Sha1), | |||||
| Author: &PayloadAuthor{ | |||||
| Name: cmt.AuthorName, | |||||
| Email: cmt.AuthorEmail, | |||||
| UserName: author_username, | |||||
| p := &api.PushPayload{ | |||||
| Ref: refFullName, | |||||
| Before: oldCommitID, | |||||
| After: newCommitID, | |||||
| CompareUrl: setting.AppUrl + commit.CompareUrl, | |||||
| Commits: commits, | |||||
| Repo: payloadRepo, | |||||
| Pusher: &api.PayloadAuthor{ | |||||
| Name: pusher_name, | |||||
| Email: pusher_email, | |||||
| UserName: userName, | |||||
| }, | }, | ||||
| Sender: payloadSender, | |||||
| } | } | ||||
| } | |||||
| p := &Payload{ | |||||
| Ref: refFullName, | |||||
| Commits: commits, | |||||
| Repo: &PayloadRepo{ | |||||
| Id: repo.ID, | |||||
| Name: repo.LowerName, | |||||
| Url: repoLink, | |||||
| Description: repo.Description, | |||||
| Website: repo.Website, | |||||
| Watchers: repo.NumWatches, | |||||
| Owner: &PayloadAuthor{ | |||||
| Name: repo.Owner.DisplayName(), | |||||
| Email: repo.Owner.Email, | |||||
| UserName: repo.Owner.Name, | |||||
| }, | |||||
| Private: repo.IsPrivate, | |||||
| }, | |||||
| Pusher: &PayloadAuthor{ | |||||
| Name: pusher_name, | |||||
| Email: pusher_email, | |||||
| UserName: userName, | |||||
| }, | |||||
| Before: oldCommitID, | |||||
| After: newCommitID, | |||||
| CompareUrl: setting.AppUrl + commit.CompareUrl, | |||||
| } | |||||
| for _, w := range ws { | |||||
| w.GetEvent() | |||||
| if !w.HasPushEvent() { | |||||
| continue | |||||
| if err = PrepareWebhooks(repo, HOOK_EVENT_PUSH, p); err != nil { | |||||
| return fmt.Errorf("PrepareWebhooks: %v", err) | |||||
| } | } | ||||
| var payload BasePayload | |||||
| switch w.HookTaskType { | |||||
| case SLACK: | |||||
| s, err := GetSlackPayload(p, w.Meta) | |||||
| if err != nil { | |||||
| return errors.New("action.GetSlackPayload: " + err.Error()) | |||||
| } | |||||
| payload = s | |||||
| default: | |||||
| payload = p | |||||
| p.Secret = w.Secret | |||||
| if isNewBranch { | |||||
| return PrepareWebhooks(repo, HOOK_EVENT_CREATE, &api.CreatePayload{ | |||||
| Ref: refName, | |||||
| RefType: "branch", | |||||
| Repo: payloadRepo, | |||||
| Sender: payloadSender, | |||||
| }) | |||||
| } | } | ||||
| if err = CreateHookTask(&HookTask{ | |||||
| RepoID: repo.ID, | |||||
| HookID: w.ID, | |||||
| Type: w.HookTaskType, | |||||
| URL: w.URL, | |||||
| BasePayload: payload, | |||||
| ContentType: w.ContentType, | |||||
| EventType: HOOK_EVENT_PUSH, | |||||
| IsSSL: w.IsSSL, | |||||
| }); err != nil { | |||||
| return fmt.Errorf("CreateHookTask: %v", err) | |||||
| } | |||||
| case PUSH_TAG: // Create | |||||
| return PrepareWebhooks(repo, HOOK_EVENT_CREATE, &api.CreatePayload{ | |||||
| Ref: refName, | |||||
| RefType: "tag", | |||||
| Repo: payloadRepo, | |||||
| Sender: payloadSender, | |||||
| }) | |||||
| } | } | ||||
| return nil | return nil | ||||
| @@ -122,9 +122,8 @@ func (u *User) HomeLink() string { | |||||
| return setting.AppSubUrl + "/" + u.Name | return setting.AppSubUrl + "/" + u.Name | ||||
| } | } | ||||
| // AvatarLink returns user gravatar link. | |||||
| func (u *User) AvatarLink() string { | |||||
| defaultImgUrl := setting.AppSubUrl + "/img/avatar_default.jpg" | |||||
| func (u *User) RelAvatarLink() string { | |||||
| defaultImgUrl := "/img/avatar_default.jpg" | |||||
| if u.Id == -1 { | if u.Id == -1 { | ||||
| return defaultImgUrl | return defaultImgUrl | ||||
| } | } | ||||
| @@ -135,7 +134,7 @@ func (u *User) AvatarLink() string { | |||||
| if !com.IsExist(imgPath) { | if !com.IsExist(imgPath) { | ||||
| return defaultImgUrl | return defaultImgUrl | ||||
| } | } | ||||
| return setting.AppSubUrl + "/avatars/" + com.ToStr(u.Id) | |||||
| return "/avatars/" + com.ToStr(u.Id) | |||||
| case setting.DisableGravatar, setting.OfflineMode: | case setting.DisableGravatar, setting.OfflineMode: | ||||
| if !com.IsExist(imgPath) { | if !com.IsExist(imgPath) { | ||||
| img, err := avatar.RandomImage([]byte(u.Email)) | img, err := avatar.RandomImage([]byte(u.Email)) | ||||
| @@ -161,13 +160,22 @@ func (u *User) AvatarLink() string { | |||||
| log.Info("New random avatar created: %d", u.Id) | log.Info("New random avatar created: %d", u.Id) | ||||
| } | } | ||||
| return setting.AppSubUrl + "/avatars/" + com.ToStr(u.Id) | |||||
| return "/avatars/" + com.ToStr(u.Id) | |||||
| case setting.Service.EnableCacheAvatar: | case setting.Service.EnableCacheAvatar: | ||||
| return setting.AppSubUrl + "/avatar/" + u.Avatar | |||||
| return "/avatar/" + u.Avatar | |||||
| } | } | ||||
| return setting.GravatarSource + u.Avatar | return setting.GravatarSource + u.Avatar | ||||
| } | } | ||||
| // AvatarLink returns user gravatar link. | |||||
| func (u *User) AvatarLink() string { | |||||
| link := u.RelAvatarLink() | |||||
| if link[0] == '/' { | |||||
| return setting.AppSubUrl + link | |||||
| } | |||||
| return link | |||||
| } | |||||
| // NewGitSig generates and returns the signature of given user. | // NewGitSig generates and returns the signature of given user. | ||||
| func (u *User) NewGitSig() *git.Signature { | func (u *User) NewGitSig() *git.Signature { | ||||
| return &git.Signature{ | return &git.Signature{ | ||||
| @@ -15,6 +15,8 @@ import ( | |||||
| "github.com/go-xorm/xorm" | "github.com/go-xorm/xorm" | ||||
| api "github.com/gogits/go-gogs-client" | |||||
| "github.com/gogits/gogs/modules/httplib" | "github.com/gogits/gogs/modules/httplib" | ||||
| "github.com/gogits/gogs/modules/log" | "github.com/gogits/gogs/modules/log" | ||||
| "github.com/gogits/gogs/modules/setting" | "github.com/gogits/gogs/modules/setting" | ||||
| @@ -54,9 +56,18 @@ func IsValidHookContentType(name string) bool { | |||||
| return ok | return ok | ||||
| } | } | ||||
| type HookEvents struct { | |||||
| Create bool `json:"create"` | |||||
| Push bool `json:"push"` | |||||
| } | |||||
| // HookEvent represents events that will delivery hook. | // HookEvent represents events that will delivery hook. | ||||
| type HookEvent struct { | type HookEvent struct { | ||||
| PushOnly bool `json:"push_only"` | |||||
| PushOnly bool `json:"push_only"` | |||||
| SendEverything bool `json:"send_everything"` | |||||
| ChooseEvents bool `json:"choose_events"` | |||||
| HookEvents `json:"events"` | |||||
| } | } | ||||
| type HookStatus int | type HookStatus int | ||||
| @@ -94,8 +105,8 @@ func (w *Webhook) GetEvent() { | |||||
| } | } | ||||
| } | } | ||||
| func (w *Webhook) GetSlackHook() *Slack { | |||||
| s := &Slack{} | |||||
| func (w *Webhook) GetSlackHook() *SlackMeta { | |||||
| s := &SlackMeta{} | |||||
| if err := json.Unmarshal([]byte(w.Meta), s); err != nil { | if err := json.Unmarshal([]byte(w.Meta), s); err != nil { | ||||
| log.Error(4, "webhook.GetSlackHook(%d): %v", w.ID, err) | log.Error(4, "webhook.GetSlackHook(%d): %v", w.ID, err) | ||||
| } | } | ||||
| @@ -114,12 +125,16 @@ func (w *Webhook) UpdateEvent() error { | |||||
| return err | return err | ||||
| } | } | ||||
| // HasCreateEvent returns true if hook enabled create event. | |||||
| func (w *Webhook) HasCreateEvent() bool { | |||||
| return w.SendEverything || | |||||
| (w.ChooseEvents && w.HookEvents.Create) | |||||
| } | |||||
| // HasPushEvent returns true if hook enabled push event. | // HasPushEvent returns true if hook enabled push event. | ||||
| func (w *Webhook) HasPushEvent() bool { | func (w *Webhook) HasPushEvent() bool { | ||||
| if w.PushOnly { | |||||
| return true | |||||
| } | |||||
| return false | |||||
| return w.PushOnly || w.SendEverything || | |||||
| (w.ChooseEvents && w.HookEvents.Push) | |||||
| } | } | ||||
| // CreateWebhook creates a new web hook. | // CreateWebhook creates a new web hook. | ||||
| @@ -140,9 +155,9 @@ func GetWebhookByID(id int64) (*Webhook, error) { | |||||
| return w, nil | return w, nil | ||||
| } | } | ||||
| // GetActiveWebhooksByRepoId returns all active webhooks of repository. | |||||
| func GetActiveWebhooksByRepoId(repoId int64) (ws []*Webhook, err error) { | |||||
| err = x.Where("repo_id=?", repoId).And("is_active=?", true).Find(&ws) | |||||
| // GetActiveWebhooksByRepoID returns all active webhooks of repository. | |||||
| func GetActiveWebhooksByRepoID(repoID int64) (ws []*Webhook, err error) { | |||||
| err = x.Where("repo_id=?", repoID).And("is_active=?", true).Find(&ws) | |||||
| return ws, err | return ws, err | ||||
| } | } | ||||
| @@ -181,9 +196,9 @@ func GetWebhooksByOrgId(orgID int64) (ws []*Webhook, err error) { | |||||
| return ws, err | return ws, err | ||||
| } | } | ||||
| // GetActiveWebhooksByOrgId returns all active webhooks for an organization. | |||||
| func GetActiveWebhooksByOrgId(orgId int64) (ws []*Webhook, err error) { | |||||
| err = x.Where("org_id=?", orgId).And("is_active=?", true).Find(&ws) | |||||
| // GetActiveWebhooksByOrgID returns all active webhooks for an organization. | |||||
| func GetActiveWebhooksByOrgID(orgID int64) (ws []*Webhook, err error) { | |||||
| err = x.Where("org_id=?", orgID).And("is_active=?", true).Find(&ws) | |||||
| return ws, err | return ws, err | ||||
| } | } | ||||
| @@ -230,58 +245,10 @@ func IsValidHookTaskType(name string) bool { | |||||
| type HookEventType string | type HookEventType string | ||||
| const ( | const ( | ||||
| HOOK_EVENT_PUSH HookEventType = "push" | |||||
| HOOK_EVENT_CREATE HookEventType = "create" | |||||
| HOOK_EVENT_PUSH HookEventType = "push" | |||||
| ) | ) | ||||
| // FIXME: just use go-gogs-client structs maybe? | |||||
| type PayloadAuthor struct { | |||||
| Name string `json:"name"` | |||||
| Email string `json:"email"` | |||||
| UserName string `json:"username"` | |||||
| } | |||||
| type PayloadCommit struct { | |||||
| Id string `json:"id"` | |||||
| Message string `json:"message"` | |||||
| Url string `json:"url"` | |||||
| Author *PayloadAuthor `json:"author"` | |||||
| } | |||||
| type PayloadRepo struct { | |||||
| Id int64 `json:"id"` | |||||
| Name string `json:"name"` | |||||
| Url string `json:"url"` | |||||
| Description string `json:"description"` | |||||
| Website string `json:"website"` | |||||
| Watchers int `json:"watchers"` | |||||
| Owner *PayloadAuthor `json:"owner"` | |||||
| Private bool `json:"private"` | |||||
| } | |||||
| type BasePayload interface { | |||||
| GetJSONPayload() ([]byte, error) | |||||
| } | |||||
| // Payload represents a payload information of hook. | |||||
| type Payload struct { | |||||
| Secret string `json:"secret"` | |||||
| Ref string `json:"ref"` | |||||
| Commits []*PayloadCommit `json:"commits"` | |||||
| Repo *PayloadRepo `json:"repository"` | |||||
| Pusher *PayloadAuthor `json:"pusher"` | |||||
| Before string `json:"before"` | |||||
| After string `json:"after"` | |||||
| CompareUrl string `json:"compare_url"` | |||||
| } | |||||
| func (p Payload) GetJSONPayload() ([]byte, error) { | |||||
| data, err := json.MarshalIndent(p, "", " ") | |||||
| if err != nil { | |||||
| return []byte{}, err | |||||
| } | |||||
| return data, nil | |||||
| } | |||||
| // HookRequest represents hook task request information. | // HookRequest represents hook task request information. | ||||
| type HookRequest struct { | type HookRequest struct { | ||||
| Headers map[string]string `json:"headers"` | Headers map[string]string `json:"headers"` | ||||
| @@ -302,7 +269,7 @@ type HookTask struct { | |||||
| UUID string | UUID string | ||||
| Type HookTaskType | Type HookTaskType | ||||
| URL string | URL string | ||||
| BasePayload `xorm:"-"` | |||||
| api.Payloader `xorm:"-"` | |||||
| PayloadContent string `xorm:"TEXT"` | PayloadContent string `xorm:"TEXT"` | ||||
| ContentType HookContentType | ContentType HookContentType | ||||
| EventType HookEventType | EventType HookEventType | ||||
| @@ -367,13 +334,13 @@ func (t *HookTask) MarshalJSON(v interface{}) string { | |||||
| // HookTasks returns a list of hook tasks by given conditions. | // HookTasks returns a list of hook tasks by given conditions. | ||||
| func HookTasks(hookID int64, page int) ([]*HookTask, error) { | func HookTasks(hookID int64, page int) ([]*HookTask, error) { | ||||
| tasks := make([]*HookTask, 0, setting.Webhook.PagingNum) | tasks := make([]*HookTask, 0, setting.Webhook.PagingNum) | ||||
| return tasks, x.Limit(setting.Webhook.PagingNum, (page-1)*setting.Webhook.PagingNum).Desc("id").Find(&tasks) | |||||
| return tasks, x.Limit(setting.Webhook.PagingNum, (page-1)*setting.Webhook.PagingNum).Where("hook_id=?", hookID).Desc("id").Find(&tasks) | |||||
| } | } | ||||
| // CreateHookTask creates a new hook task, | // CreateHookTask creates a new hook task, | ||||
| // it handles conversion from Payload to PayloadContent. | // it handles conversion from Payload to PayloadContent. | ||||
| func CreateHookTask(t *HookTask) error { | func CreateHookTask(t *HookTask) error { | ||||
| data, err := t.BasePayload.GetJSONPayload() | |||||
| data, err := t.Payloader.JSONPayload() | |||||
| if err != nil { | if err != nil { | ||||
| return err | return err | ||||
| } | } | ||||
| @@ -389,6 +356,71 @@ func UpdateHookTask(t *HookTask) error { | |||||
| return err | return err | ||||
| } | } | ||||
| // PrepareWebhooks adds new webhooks to task queue for given payload. | |||||
| func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) error { | |||||
| if err := repo.GetOwner(); err != nil { | |||||
| return fmt.Errorf("GetOwner: %v", err) | |||||
| } | |||||
| ws, err := GetActiveWebhooksByRepoID(repo.ID) | |||||
| if err != nil { | |||||
| return fmt.Errorf("GetActiveWebhooksByRepoID: %v", err) | |||||
| } | |||||
| // check if repo belongs to org and append additional webhooks | |||||
| if repo.Owner.IsOrganization() { | |||||
| // get hooks for org | |||||
| orgws, err := GetActiveWebhooksByOrgID(repo.OwnerID) | |||||
| if err != nil { | |||||
| return fmt.Errorf("GetActiveWebhooksByOrgID: %v", err) | |||||
| } | |||||
| ws = append(ws, orgws...) | |||||
| } | |||||
| if len(ws) == 0 { | |||||
| return nil | |||||
| } | |||||
| for _, w := range ws { | |||||
| w.GetEvent() | |||||
| switch event { | |||||
| case HOOK_EVENT_CREATE: | |||||
| if !w.HasCreateEvent() { | |||||
| continue | |||||
| } | |||||
| case HOOK_EVENT_PUSH: | |||||
| if !w.HasPushEvent() { | |||||
| continue | |||||
| } | |||||
| } | |||||
| switch w.HookTaskType { | |||||
| case SLACK: | |||||
| p, err = GetSlackPayload(p, event, w.Meta) | |||||
| if err != nil { | |||||
| return fmt.Errorf("GetSlackPayload: %v", err) | |||||
| } | |||||
| default: | |||||
| p.SetSecret(w.Secret) | |||||
| } | |||||
| if err = CreateHookTask(&HookTask{ | |||||
| RepoID: repo.ID, | |||||
| HookID: w.ID, | |||||
| Type: w.HookTaskType, | |||||
| URL: w.URL, | |||||
| Payloader: p, | |||||
| ContentType: w.ContentType, | |||||
| EventType: HOOK_EVENT_PUSH, | |||||
| IsSSL: w.IsSSL, | |||||
| }); err != nil { | |||||
| return fmt.Errorf("CreateHookTask: %v", err) | |||||
| } | |||||
| } | |||||
| return nil | |||||
| } | |||||
| type hookQueue struct { | type hookQueue struct { | ||||
| // Make sure one repository only occur once in the queue. | // Make sure one repository only occur once in the queue. | ||||
| lock sync.Mutex | lock sync.Mutex | ||||
| @@ -9,13 +9,18 @@ import ( | |||||
| "errors" | "errors" | ||||
| "fmt" | "fmt" | ||||
| "strings" | "strings" | ||||
| api "github.com/gogits/go-gogs-client" | |||||
| "github.com/gogits/gogs/modules/git" | |||||
| "github.com/gogits/gogs/modules/setting" | |||||
| ) | ) | ||||
| const ( | const ( | ||||
| SLACK_COLOR string = "#dd4b39" | SLACK_COLOR string = "#dd4b39" | ||||
| ) | ) | ||||
| type Slack struct { | |||||
| type SlackMeta struct { | |||||
| Channel string `json:"channel"` | Channel string `json:"channel"` | ||||
| } | } | ||||
| @@ -34,7 +39,9 @@ type SlackAttachment struct { | |||||
| Text string `json:"text"` | Text string `json:"text"` | ||||
| } | } | ||||
| func (p SlackPayload) GetJSONPayload() ([]byte, error) { | |||||
| func (p *SlackPayload) SetSecret(_ string) {} | |||||
| func (p *SlackPayload) JSONPayload() ([]byte, error) { | |||||
| data, err := json.Marshal(p) | data, err := json.Marshal(p) | ||||
| if err != nil { | if err != nil { | ||||
| return []byte{}, err | return []byte{}, err | ||||
| @@ -42,27 +49,47 @@ func (p SlackPayload) GetJSONPayload() ([]byte, error) { | |||||
| return data, nil | return data, nil | ||||
| } | } | ||||
| func GetSlackPayload(p *Payload, meta string) (*SlackPayload, error) { | |||||
| slack := &Slack{} | |||||
| slackPayload := &SlackPayload{} | |||||
| if err := json.Unmarshal([]byte(meta), &slack); err != nil { | |||||
| return slackPayload, errors.New("GetSlackPayload meta json:" + err.Error()) | |||||
| } | |||||
| // see: https://api.slack.com/docs/formatting | |||||
| func SlackTextFormatter(s string) string { | |||||
| // take only first line of commit | |||||
| first := strings.Split(s, "\n")[0] | |||||
| // replace & < > | |||||
| first = strings.Replace(first, "&", "&", -1) | |||||
| first = strings.Replace(first, "<", "<", -1) | |||||
| first = strings.Replace(first, ">", ">", -1) | |||||
| return first | |||||
| } | |||||
| // TODO: handle different payload types: push, new branch, delete branch etc. | |||||
| // when they are added to gogs. Only handles push now | |||||
| return getSlackPushPayload(p, slack) | |||||
| func SlackLinkFormatter(url string, text string) string { | |||||
| return fmt.Sprintf("<%s|%s>", url, SlackTextFormatter(text)) | |||||
| } | } | ||||
| func getSlackPushPayload(p *Payload, slack *Slack) (*SlackPayload, error) { | |||||
| func getSlackCreatePayload(p *api.CreatePayload, slack *SlackMeta) (*SlackPayload, error) { | |||||
| // created tag/branch | |||||
| refName := git.RefEndName(p.Ref) | |||||
| repoLink := SlackLinkFormatter(p.Repo.URL, p.Repo.Name) | |||||
| refLink := SlackLinkFormatter(p.Repo.URL+"/src/"+refName, refName) | |||||
| text := fmt.Sprintf("[%s:%s] %s created by %s", repoLink, refLink, p.RefType, p.Sender.UserName) | |||||
| return &SlackPayload{ | |||||
| Channel: slack.Channel, | |||||
| Text: text, | |||||
| Username: setting.AppName, | |||||
| IconUrl: setting.AppUrl + "/img/favicon.png", | |||||
| }, nil | |||||
| } | |||||
| func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, error) { | |||||
| // n new commits | // n new commits | ||||
| refSplit := strings.Split(p.Ref, "/") | |||||
| branchName := refSplit[len(refSplit)-1] | |||||
| var commitString string | |||||
| var ( | |||||
| branchName = git.RefEndName(p.Ref) | |||||
| commitString string | |||||
| ) | |||||
| if len(p.Commits) == 1 { | if len(p.Commits) == 1 { | ||||
| commitString = "1 new commit" | commitString = "1 new commit" | ||||
| if p.CompareUrl != "" { | |||||
| if len(p.CompareUrl) > 0 { | |||||
| commitString = SlackLinkFormatter(p.CompareUrl, commitString) | commitString = SlackLinkFormatter(p.CompareUrl, commitString) | ||||
| } | } | ||||
| } else { | } else { | ||||
| @@ -72,14 +99,14 @@ func getSlackPushPayload(p *Payload, slack *Slack) (*SlackPayload, error) { | |||||
| } | } | ||||
| } | } | ||||
| repoLink := SlackLinkFormatter(p.Repo.Url, p.Repo.Name) | |||||
| branchLink := SlackLinkFormatter(p.Repo.Url+"/src/"+branchName, branchName) | |||||
| repoLink := SlackLinkFormatter(p.Repo.URL, p.Repo.Name) | |||||
| branchLink := SlackLinkFormatter(p.Repo.URL+"/src/"+branchName, branchName) | |||||
| text := fmt.Sprintf("[%s:%s] %s pushed by %s", repoLink, branchLink, commitString, p.Pusher.Name) | text := fmt.Sprintf("[%s:%s] %s pushed by %s", repoLink, branchLink, commitString, p.Pusher.Name) | ||||
| var attachmentText string | |||||
| var attachmentText string | |||||
| // for each commit, generate attachment text | // for each commit, generate attachment text | ||||
| for i, commit := range p.Commits { | for i, commit := range p.Commits { | ||||
| attachmentText += fmt.Sprintf("%s: %s - %s", SlackLinkFormatter(commit.Url, commit.Id[:7]), SlackTextFormatter(commit.Message), SlackTextFormatter(commit.Author.Name)) | |||||
| attachmentText += fmt.Sprintf("%s: %s - %s", SlackLinkFormatter(commit.URL, commit.ID[:7]), SlackTextFormatter(commit.Message), SlackTextFormatter(commit.Author.Name)) | |||||
| // add linebreak to each commit but the last | // add linebreak to each commit but the last | ||||
| if i < len(p.Commits)-1 { | if i < len(p.Commits)-1 { | ||||
| attachmentText += "\n" | attachmentText += "\n" | ||||
| @@ -91,25 +118,26 @@ func getSlackPushPayload(p *Payload, slack *Slack) (*SlackPayload, error) { | |||||
| return &SlackPayload{ | return &SlackPayload{ | ||||
| Channel: slack.Channel, | Channel: slack.Channel, | ||||
| Text: text, | Text: text, | ||||
| Username: "gogs", | |||||
| IconUrl: "https://raw.githubusercontent.com/gogits/gogs/master/public/img/favicon.png", | |||||
| UnfurlLinks: 0, | |||||
| LinkNames: 0, | |||||
| Username: setting.AppName, | |||||
| IconUrl: setting.AppUrl + "/img/favicon.png", | |||||
| Attachments: slackAttachments, | Attachments: slackAttachments, | ||||
| }, nil | }, nil | ||||
| } | } | ||||
| // see: https://api.slack.com/docs/formatting | |||||
| func SlackTextFormatter(s string) string { | |||||
| // take only first line of commit | |||||
| first := strings.Split(s, "\n")[0] | |||||
| // replace & < > | |||||
| first = strings.Replace(first, "&", "&", -1) | |||||
| first = strings.Replace(first, "<", "<", -1) | |||||
| first = strings.Replace(first, ">", ">", -1) | |||||
| return first | |||||
| } | |||||
| func GetSlackPayload(p api.Payloader, event HookEventType, meta string) (*SlackPayload, error) { | |||||
| s := new(SlackPayload) | |||||
| func SlackLinkFormatter(url string, text string) string { | |||||
| return fmt.Sprintf("<%s|%s>", url, SlackTextFormatter(text)) | |||||
| slack := &SlackMeta{} | |||||
| if err := json.Unmarshal([]byte(meta), &slack); err != nil { | |||||
| return s, errors.New("GetSlackPayload meta json:" + err.Error()) | |||||
| } | |||||
| switch event { | |||||
| case HOOK_EVENT_CREATE: | |||||
| return getSlackCreatePayload(p.(*api.CreatePayload), slack) | |||||
| case HOOK_EVENT_PUSH: | |||||
| return getSlackPushPayload(p.(*api.PushPayload), slack) | |||||
| } | |||||
| return s, nil | |||||
| } | } | ||||
| @@ -67,8 +67,22 @@ func (f *RepoSettingForm) Validate(ctx *macaron.Context, errs binding.Errors) bi | |||||
| // \/ \/ \/ \/ \/ \/ | // \/ \/ \/ \/ \/ \/ | ||||
| type WebhookForm struct { | type WebhookForm struct { | ||||
| PushOnly bool | |||||
| Active bool | |||||
| Events string | |||||
| Create bool | |||||
| Push bool | |||||
| Active bool | |||||
| } | |||||
| func (f WebhookForm) PushOnly() bool { | |||||
| return f.Events == "push_only" | |||||
| } | |||||
| func (f WebhookForm) SendEverything() bool { | |||||
| return f.Events == "send_everything" | |||||
| } | |||||
| func (f WebhookForm) ChooseEvents() bool { | |||||
| return f.Events == "choose_events" | |||||
| } | } | ||||
| type NewWebhookForm struct { | type NewWebhookForm struct { | ||||
| @@ -367,6 +367,23 @@ function initRepository() { | |||||
| } | } | ||||
| }; | }; | ||||
| function initWebhook() { | |||||
| if ($('.new.webhook').length == 0) { | |||||
| return; | |||||
| } | |||||
| $('.events.checkbox input').change(function () { | |||||
| if ($(this).is(':checked')) { | |||||
| $('.events.fields').show(); | |||||
| } | |||||
| }); | |||||
| $('.non-events.checkbox input').change(function () { | |||||
| if ($(this).is(':checked')) { | |||||
| $('.events.fields').hide(); | |||||
| } | |||||
| }); | |||||
| } | |||||
| $(document).ready(function () { | $(document).ready(function () { | ||||
| csrf = $('meta[name=_csrf]').attr("content"); | csrf = $('meta[name=_csrf]').attr("content"); | ||||
| @@ -473,4 +490,5 @@ $(document).ready(function () { | |||||
| initCommentForm(); | initCommentForm(); | ||||
| initInstall(); | initInstall(); | ||||
| initRepository(); | initRepository(); | ||||
| initWebhook(); | |||||
| }); | }); | ||||
| @@ -76,4 +76,12 @@ | |||||
| margin-left: 25px; | margin-left: 25px; | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| .new.webhook { | |||||
| .events.fields { | |||||
| .column { | |||||
| padding-left: 40px; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -83,7 +83,7 @@ func CreateRepoHook(ctx *middleware.Context, form api.CreateHookOption) { | |||||
| ctx.JSON(422, &base.ApiJsonErr{"missing config option: channel", base.DOC_URL}) | ctx.JSON(422, &base.ApiJsonErr{"missing config option: channel", base.DOC_URL}) | ||||
| return | return | ||||
| } | } | ||||
| meta, err := json.Marshal(&models.Slack{ | |||||
| meta, err := json.Marshal(&models.SlackMeta{ | |||||
| Channel: channel, | Channel: channel, | ||||
| }) | }) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -141,7 +141,7 @@ func EditRepoHook(ctx *middleware.Context, form api.EditHookOption) { | |||||
| if w.HookTaskType == models.SLACK { | if w.HookTaskType == models.SLACK { | ||||
| if channel, ok := form.Config["channel"]; ok { | if channel, ok := form.Config["channel"]; ok { | ||||
| meta, err := json.Marshal(&models.Slack{ | |||||
| meta, err := json.Marshal(&models.SlackMeta{ | |||||
| Channel: channel, | Channel: channel, | ||||
| }) | }) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -320,6 +320,18 @@ func WebhooksNew(ctx *middleware.Context) { | |||||
| ctx.HTML(200, orCtx.NewTemplate) | 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) { | func WebHooksNewPost(ctx *middleware.Context, form auth.NewWebhookForm) { | ||||
| ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook") | ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook") | ||||
| ctx.Data["PageIsSettingsHooks"] = true | ctx.Data["PageIsSettingsHooks"] = true | ||||
| @@ -345,13 +357,11 @@ func WebHooksNewPost(ctx *middleware.Context, form auth.NewWebhookForm) { | |||||
| } | } | ||||
| w := &models.Webhook{ | w := &models.Webhook{ | ||||
| RepoID: orCtx.RepoID, | |||||
| URL: form.PayloadURL, | |||||
| ContentType: contentType, | |||||
| Secret: form.Secret, | |||||
| HookEvent: &models.HookEvent{ | |||||
| PushOnly: form.PushOnly, | |||||
| }, | |||||
| RepoID: orCtx.RepoID, | |||||
| URL: form.PayloadURL, | |||||
| ContentType: contentType, | |||||
| Secret: form.Secret, | |||||
| HookEvent: ParseHookEvent(form.WebhookForm), | |||||
| IsActive: form.Active, | IsActive: form.Active, | ||||
| HookTaskType: models.GOGS, | HookTaskType: models.GOGS, | ||||
| OrgID: orCtx.OrgID, | OrgID: orCtx.OrgID, | ||||
| @@ -385,7 +395,7 @@ func SlackHooksNewPost(ctx *middleware.Context, form auth.NewSlackHookForm) { | |||||
| return | return | ||||
| } | } | ||||
| meta, err := json.Marshal(&models.Slack{ | |||||
| meta, err := json.Marshal(&models.SlackMeta{ | |||||
| Channel: form.Channel, | Channel: form.Channel, | ||||
| }) | }) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -394,12 +404,10 @@ func SlackHooksNewPost(ctx *middleware.Context, form auth.NewSlackHookForm) { | |||||
| } | } | ||||
| w := &models.Webhook{ | w := &models.Webhook{ | ||||
| RepoID: orCtx.RepoID, | |||||
| URL: form.PayloadURL, | |||||
| ContentType: models.JSON, | |||||
| HookEvent: &models.HookEvent{ | |||||
| PushOnly: form.PushOnly, | |||||
| }, | |||||
| RepoID: orCtx.RepoID, | |||||
| URL: form.PayloadURL, | |||||
| ContentType: models.JSON, | |||||
| HookEvent: ParseHookEvent(form.WebhookForm), | |||||
| IsActive: form.Active, | IsActive: form.Active, | ||||
| HookTaskType: models.SLACK, | HookTaskType: models.SLACK, | ||||
| Meta: string(meta), | Meta: string(meta), | ||||
| @@ -491,9 +499,7 @@ func WebHooksEditPost(ctx *middleware.Context, form auth.NewWebhookForm) { | |||||
| w.URL = form.PayloadURL | w.URL = form.PayloadURL | ||||
| w.ContentType = contentType | w.ContentType = contentType | ||||
| w.Secret = form.Secret | w.Secret = form.Secret | ||||
| w.HookEvent = &models.HookEvent{ | |||||
| PushOnly: form.PushOnly, | |||||
| } | |||||
| w.HookEvent = ParseHookEvent(form.WebhookForm) | |||||
| w.IsActive = form.Active | w.IsActive = form.Active | ||||
| if err := w.UpdateEvent(); err != nil { | if err := w.UpdateEvent(); err != nil { | ||||
| ctx.Handle(500, "UpdateEvent", err) | ctx.Handle(500, "UpdateEvent", err) | ||||
| @@ -523,7 +529,7 @@ func SlackHooksEditPost(ctx *middleware.Context, form auth.NewSlackHookForm) { | |||||
| return | return | ||||
| } | } | ||||
| meta, err := json.Marshal(&models.Slack{ | |||||
| meta, err := json.Marshal(&models.SlackMeta{ | |||||
| Channel: form.Channel, | Channel: form.Channel, | ||||
| }) | }) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -533,9 +539,7 @@ func SlackHooksEditPost(ctx *middleware.Context, form auth.NewSlackHookForm) { | |||||
| w.URL = form.PayloadURL | w.URL = form.PayloadURL | ||||
| w.Meta = string(meta) | w.Meta = string(meta) | ||||
| w.HookEvent = &models.HookEvent{ | |||||
| PushOnly: form.PushOnly, | |||||
| } | |||||
| w.HookEvent = ParseHookEvent(form.WebhookForm) | |||||
| w.IsActive = form.Active | w.IsActive = form.Active | ||||
| if err := w.UpdateEvent(); err != nil { | if err := w.UpdateEvent(); err != nil { | ||||
| ctx.Handle(500, "UpdateEvent", err) | ctx.Handle(500, "UpdateEvent", err) | ||||
| @@ -26,4 +26,22 @@ | |||||
| </div> | </div> | ||||
| </footer> | </footer> | ||||
| </body> | </body> | ||||
| <!-- Third-party libraries --> | |||||
| {{if .RequireHighlightJS}} | |||||
| <link rel="stylesheet" href="{{AppSubUrl}}/css/highlight-8.7/default.css"> | |||||
| <script src="{{AppSubUrl}}/js/libs/highlight-8.7.pack.js"></script> | |||||
| {{end}} | |||||
| {{if .RequireMinicolors}} | |||||
| <link rel="stylesheet" href="{{AppSubUrl}}/css/jquery.minicolors-2.1.12.css"> | |||||
| <script src="{{AppSubUrl}}/js/libs/jquery.minicolors-2.1.12.min.js"></script> | |||||
| {{end}} | |||||
| {{if .RequireDatetimepicker}} | |||||
| <link rel="stylesheet" href="{{AppSubUrl}}/css/jquery.datetimepicker-2.4.5.css"> | |||||
| <script src="{{AppSubUrl}}/js/libs/jquery.datetimepicker-2.4.5.js"></script> | |||||
| {{end}} | |||||
| {{if .RequireDropzone}} | |||||
| <link rel="stylesheet" href="{{AppSubUrl}}/css/dropzone-4.0.1.css"> | |||||
| <script src="{{AppSubUrl}}/js/libs/dropzone-4.0.1.js"></script> | |||||
| {{end}} | |||||
| </html> | </html> | ||||
| @@ -25,24 +25,6 @@ | |||||
| <script src="{{AppSubUrl}}/js/semantic-2.0.8.min.js"></script> | <script src="{{AppSubUrl}}/js/semantic-2.0.8.min.js"></script> | ||||
| <script src="{{AppSubUrl}}/js/gogs.js?v={{AppVer}}"></script> | <script src="{{AppSubUrl}}/js/gogs.js?v={{AppVer}}"></script> | ||||
| <!-- Third-party libraries --> | |||||
| {{if .RequireHighlightJS}} | |||||
| <link rel="stylesheet" href="{{AppSubUrl}}/css/highlight-8.7/default.css"> | |||||
| <script src="{{AppSubUrl}}/js/libs/highlight-8.7.pack.js"></script> | |||||
| {{end}} | |||||
| {{if .RequireMinicolors}} | |||||
| <link rel="stylesheet" href="{{AppSubUrl}}/css/jquery.minicolors-2.1.12.css"> | |||||
| <script src="{{AppSubUrl}}/js/libs/jquery.minicolors-2.1.12.min.js"></script> | |||||
| {{end}} | |||||
| {{if .RequireDatetimepicker}} | |||||
| <link rel="stylesheet" href="{{AppSubUrl}}/css/jquery.datetimepicker-2.4.5.css"> | |||||
| <script src="{{AppSubUrl}}/js/libs/jquery.datetimepicker-2.4.5.js"></script> | |||||
| {{end}} | |||||
| {{if .RequireDropzone}} | |||||
| <link rel="stylesheet" href="{{AppSubUrl}}/css/dropzone-4.0.1.css"> | |||||
| <script src="{{AppSubUrl}}/js/libs/dropzone-4.0.1.js"></script> | |||||
| {{end}} | |||||
| <title>{{if .Title}}{{.Title}} - {{end}}{{AppName}}</title> | <title>{{if .Title}}{{.Title}} - {{end}}{{AppName}}</title> | ||||
| </head> | </head> | ||||
| <body> | <body> | ||||
| @@ -28,7 +28,7 @@ | |||||
| {{if .IsSucceed}} | {{if .IsSucceed}} | ||||
| <span class="ui green label">{{.ResponseInfo.Status}}</span> | <span class="ui green label">{{.ResponseInfo.Status}}</span> | ||||
| {{else}} | {{else}} | ||||
| <span class="ui red label">500</span> | |||||
| <span class="ui red label">{{.ResponseInfo.Status}}</span> | |||||
| {{end}} | {{end}} | ||||
| {{else}} | {{else}} | ||||
| <span class="ui label">N/A</span> | <span class="ui label">N/A</span> | ||||
| @@ -1,14 +1,52 @@ | |||||
| <div class="field"> | <div class="field"> | ||||
| <h4>{{.i18n.Tr "repo.settings.event_desc"}}</h4> | <h4>{{.i18n.Tr "repo.settings.event_desc"}}</h4> | ||||
| <div class="grouped fields"> | |||||
| <div class="field"> | |||||
| <div class="ui radio checkbox checked"> | |||||
| <input class="hidden" name="push_only" type="radio" {{if or .PageIsSettingsHooksNew .Webhook.PushOnly}}checked{{end}}> | |||||
| <label>{{.i18n.Tr "repo.settings.event_push_only" | Str2html}}</label> | |||||
| </div> | |||||
| </div> | |||||
| <div class="grouped event type fields"> | |||||
| <div class="field"> | |||||
| <div class="ui radio non-events checkbox"> | |||||
| <input class="hidden" name="events" type="radio" value="push_only" {{if or .PageIsSettingsHooksNew .Webhook.PushOnly}}checked{{end}}> | |||||
| <label>{{.i18n.Tr "repo.settings.event_push_only" | Str2html}}</label> | |||||
| </div> | |||||
| </div> | |||||
| <div class="field"> | |||||
| <div class="ui radio non-events checkbox"> | |||||
| <input class="hidden" name="events" type="radio" value="send_everything" {{if .Webhook.SendEverything}}checked{{end}}> | |||||
| <label>{{.i18n.Tr "repo.settings.event_send_everything" | Str2html}}</label> | |||||
| </div> | |||||
| </div> | |||||
| <div class="field"> | |||||
| <div class="ui radio events checkbox"> | |||||
| <input class="hidden" name="events" type="radio" value="choose_events" {{if .Webhook.ChooseEvents}}checked{{end}}> | |||||
| <label>{{.i18n.Tr "repo.settings.event_choose" | Str2html}}</label> | |||||
| </div> | |||||
| </div> | |||||
| </div> | </div> | ||||
| <div class="events fields ui grid" {{if not .Webhook.ChooseEvents}}style="display:none"{{end}}> | |||||
| <!-- Create --> | |||||
| <div class="seven wide column"> | |||||
| <div class="field"> | |||||
| <div class="ui checkbox"> | |||||
| <input class="hidden" name="create" type="checkbox" tabindex="0" {{if .Webhook.Create}}checked{{end}}> | |||||
| <label>{{.i18n.Tr "repo.settings.event_create"}}</label> | |||||
| <span class="help">{{.i18n.Tr "repo.settings.event_create_desc"}}</span> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <!-- Push --> | |||||
| <div class="seven wide column"> | |||||
| <div class="field"> | |||||
| <div class="ui checkbox"> | |||||
| <input class="hidden" name="push" type="checkbox" tabindex="0" {{if .Webhook.Push}}checked{{end}}> | |||||
| <label>{{.i18n.Tr "repo.settings.event_push"}}</label> | |||||
| <span class="help">{{.i18n.Tr "repo.settings.event_push_desc"}}</span> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | </div> | ||||
| <div class="ui divider"></div> | |||||
| <div class="inline field"> | <div class="inline field"> | ||||
| <div class="ui checkbox"> | <div class="ui checkbox"> | ||||
| <input class="hidden" name="active" type="checkbox" tabindex="0" {{if or .PageIsSettingsHooksNew .Webhook.IsActive}}checked{{end}}> | <input class="hidden" name="active" type="checkbox" tabindex="0" {{if or .PageIsSettingsHooksNew .Webhook.IsActive}}checked{{end}}> | ||||