add Slack API webhook support
This commit is contained in:
		
							parent
							
								
									5e6091a30a
								
							
						
					
					
						commit
						2bce24068d
					
				| 
						 | 
					@ -284,9 +284,11 @@ func runWeb(*cli.Context) {
 | 
				
			||||||
			r.Route("/collaboration", "GET,POST", repo.SettingsCollaboration)
 | 
								r.Route("/collaboration", "GET,POST", repo.SettingsCollaboration)
 | 
				
			||||||
			r.Get("/hooks", repo.Webhooks)
 | 
								r.Get("/hooks", repo.Webhooks)
 | 
				
			||||||
			r.Get("/hooks/new", repo.WebHooksNew)
 | 
								r.Get("/hooks/new", repo.WebHooksNew)
 | 
				
			||||||
			r.Post("/hooks/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost)
 | 
								r.Post("/hooks/gogs/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost)
 | 
				
			||||||
 | 
								r.Post("/hooks/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost)
 | 
				
			||||||
			r.Get("/hooks/:id", repo.WebHooksEdit)
 | 
								r.Get("/hooks/:id", repo.WebHooksEdit)
 | 
				
			||||||
			r.Post("/hooks/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost)
 | 
								r.Post("/hooks/gogs/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost)
 | 
				
			||||||
 | 
								r.Post("/hooks/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost)
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}, reqSignIn, middleware.RepoAssignment(true), reqTrueOwner)
 | 
						}, reqSignIn, middleware.RepoAssignment(true), reqTrueOwner)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -234,6 +234,11 @@ settings.update_webhook = Update Webhook
 | 
				
			||||||
settings.update_hook_success = Webhook has been updated.
 | 
					settings.update_hook_success = Webhook has been updated.
 | 
				
			||||||
settings.delete_webhook = Delete Webhook
 | 
					settings.delete_webhook = Delete Webhook
 | 
				
			||||||
settings.recent_deliveries = Recent Deliveries
 | 
					settings.recent_deliveries = Recent Deliveries
 | 
				
			||||||
 | 
					settings.hook_type = Hook Type
 | 
				
			||||||
 | 
					settings.add_slack_hook_desc = Add <a href="http://slack.com">Slack</a> integration to your repository.
 | 
				
			||||||
 | 
					settings.slack_token = Token
 | 
				
			||||||
 | 
					settings.slack_domain = Domain
 | 
				
			||||||
 | 
					settings.slack_channel = Channel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[org]
 | 
					[org]
 | 
				
			||||||
org_name_holder = Organization Name
 | 
					org_name_holder = Organization Name
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -266,14 +266,33 @@ func CommitRepoAction(userId, repoUserId int64, userName, actEmail string,
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		p.Secret = w.Secret
 | 
							switch w.HookTaskType {
 | 
				
			||||||
		CreateHookTask(&HookTask{
 | 
							case SLACK:
 | 
				
			||||||
			Type:        WEBHOOK,
 | 
								{
 | 
				
			||||||
			Url:         w.Url,
 | 
									s, err := GetSlackPayload(p, w.Meta)
 | 
				
			||||||
			Payload:     p,
 | 
									if err != nil {
 | 
				
			||||||
			ContentType: w.ContentType,
 | 
										return errors.New("action.GetSlackPayload: " + err.Error())
 | 
				
			||||||
			IsSsl:       w.IsSsl,
 | 
									}
 | 
				
			||||||
		})
 | 
									CreateHookTask(&HookTask{
 | 
				
			||||||
 | 
										Type:        w.HookTaskType,
 | 
				
			||||||
 | 
										Url:         w.Url,
 | 
				
			||||||
 | 
										BasePayload: s,
 | 
				
			||||||
 | 
										ContentType: w.ContentType,
 | 
				
			||||||
 | 
										IsSsl:       w.IsSsl,
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									p.Secret = w.Secret
 | 
				
			||||||
 | 
									CreateHookTask(&HookTask{
 | 
				
			||||||
 | 
										Type:        w.HookTaskType,
 | 
				
			||||||
 | 
										Url:         w.Url,
 | 
				
			||||||
 | 
										BasePayload: p,
 | 
				
			||||||
 | 
										ContentType: w.ContentType,
 | 
				
			||||||
 | 
										IsSsl:       w.IsSsl,
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,114 @@
 | 
				
			||||||
 | 
					// 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"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						SLACK_COLOR string = "#dd4b39"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Slack struct {
 | 
				
			||||||
 | 
						Domain  string `json:"domain"`
 | 
				
			||||||
 | 
						Token   string `json:"token"`
 | 
				
			||||||
 | 
						Channel string `json:"channel"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SlackPayload struct {
 | 
				
			||||||
 | 
						Channel     string            `json:"channel"`
 | 
				
			||||||
 | 
						Text        string            `json:"text"`
 | 
				
			||||||
 | 
						Username    string            `json:"username"`
 | 
				
			||||||
 | 
						IconUrl     string            `json:"icon_url"`
 | 
				
			||||||
 | 
						UnfurlLinks int               `json:"unfurl_links"`
 | 
				
			||||||
 | 
						LinkNames   int               `json:"link_names"`
 | 
				
			||||||
 | 
						Attachments []SlackAttachment `json:"attachments"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SlackAttachment struct {
 | 
				
			||||||
 | 
						Color string `json:"color"`
 | 
				
			||||||
 | 
						Text  string `json:"text"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetSlackURL(domain string, token string) string {
 | 
				
			||||||
 | 
						return fmt.Sprintf(
 | 
				
			||||||
 | 
							"https://%s.slack.com/services/hooks/incoming-webhook?token=%s",
 | 
				
			||||||
 | 
							domain,
 | 
				
			||||||
 | 
							token,
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p SlackPayload) GetJSONPayload() ([]byte, error) {
 | 
				
			||||||
 | 
						data, err := json.Marshal(p)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return []byte{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						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())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 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 getSlackPushPayload(p *Payload, slack *Slack) (*SlackPayload, error) {
 | 
				
			||||||
 | 
						// n new commits
 | 
				
			||||||
 | 
						refSplit := strings.Split(p.Ref, "/")
 | 
				
			||||||
 | 
						branchName := refSplit[len(refSplit)-1]
 | 
				
			||||||
 | 
						var commitString string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// TODO: add commit compare before/after link when gogs adds it
 | 
				
			||||||
 | 
						if len(p.Commits) == 1 {
 | 
				
			||||||
 | 
							commitString = "1 new commit"
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							commitString = fmt.Sprintf("%d new commits", len(p.Commits))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						text := fmt.Sprintf("[%s:%s] %s pushed by %s", p.Repo.Name, branchName, commitString, p.Pusher.Name)
 | 
				
			||||||
 | 
						var attachmentText string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// for each commit, generate attachment text
 | 
				
			||||||
 | 
						for i, commit := range p.Commits {
 | 
				
			||||||
 | 
							attachmentText += fmt.Sprintf("<%s|%s>: %s - %s", commit.Url, commit.Id[:7], SlackFormatter(commit.Message), commit.Author.Name)
 | 
				
			||||||
 | 
							// add linebreak to each commit but the last
 | 
				
			||||||
 | 
							if i < len(p.Commits)-1 {
 | 
				
			||||||
 | 
								attachmentText += "\n"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						slackAttachments := []SlackAttachment{{Color: SLACK_COLOR, Text: attachmentText}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &SlackPayload{
 | 
				
			||||||
 | 
							Channel:     slack.Channel,
 | 
				
			||||||
 | 
							Text:        text,
 | 
				
			||||||
 | 
							Username:    "gogs",
 | 
				
			||||||
 | 
							IconUrl:     "https://raw.githubusercontent.com/gogits/gogs/master/public/img/favicon.png",
 | 
				
			||||||
 | 
							UnfurlLinks: 0,
 | 
				
			||||||
 | 
							LinkNames:   0,
 | 
				
			||||||
 | 
							Attachments: slackAttachments,
 | 
				
			||||||
 | 
						}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// see: https://api.slack.com/docs/formatting
 | 
				
			||||||
 | 
					func SlackFormatter(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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@ package models
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/gogits/gogs/modules/httplib"
 | 
						"github.com/gogits/gogs/modules/httplib"
 | 
				
			||||||
| 
						 | 
					@ -33,15 +34,17 @@ type HookEvent struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Webhook represents a web hook object.
 | 
					// Webhook represents a web hook object.
 | 
				
			||||||
type Webhook struct {
 | 
					type Webhook struct {
 | 
				
			||||||
	Id          int64
 | 
						Id           int64
 | 
				
			||||||
	RepoId      int64
 | 
						RepoId       int64
 | 
				
			||||||
	Url         string `xorm:"TEXT"`
 | 
						Url          string `xorm:"TEXT"`
 | 
				
			||||||
	ContentType HookContentType
 | 
						ContentType  HookContentType
 | 
				
			||||||
	Secret      string `xorm:"TEXT"`
 | 
						Secret       string `xorm:"TEXT"`
 | 
				
			||||||
	Events      string `xorm:"TEXT"`
 | 
						Events       string `xorm:"TEXT"`
 | 
				
			||||||
	*HookEvent  `xorm:"-"`
 | 
						*HookEvent   `xorm:"-"`
 | 
				
			||||||
	IsSsl       bool
 | 
						IsSsl        bool
 | 
				
			||||||
	IsActive    bool
 | 
						IsActive     bool
 | 
				
			||||||
 | 
						HookTaskType HookTaskType
 | 
				
			||||||
 | 
						Meta         string `xorm:"TEXT"` // store hook-specific attributes
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetEvent handles conversion from Events to HookEvent.
 | 
					// GetEvent handles conversion from Events to HookEvent.
 | 
				
			||||||
| 
						 | 
					@ -52,6 +55,14 @@ func (w *Webhook) GetEvent() {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (w *Webhook) GetSlackHook() *Slack {
 | 
				
			||||||
 | 
						s := &Slack{}
 | 
				
			||||||
 | 
						if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
 | 
				
			||||||
 | 
							log.Error(4, "webhook.GetSlackHook(%d): %v", w.Id, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return s
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UpdateEvent handles conversion from HookEvent to Events.
 | 
					// UpdateEvent handles conversion from HookEvent to Events.
 | 
				
			||||||
func (w *Webhook) UpdateEvent() error {
 | 
					func (w *Webhook) UpdateEvent() error {
 | 
				
			||||||
	data, err := json.Marshal(w.HookEvent)
 | 
						data, err := json.Marshal(w.HookEvent)
 | 
				
			||||||
| 
						 | 
					@ -119,8 +130,8 @@ func DeleteWebhook(hookId int64) error {
 | 
				
			||||||
type HookTaskType int
 | 
					type HookTaskType int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	WEBHOOK HookTaskType = iota + 1
 | 
						GOGS HookTaskType = iota + 1
 | 
				
			||||||
	SERVICE
 | 
						SLACK
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type HookEventType string
 | 
					type HookEventType string
 | 
				
			||||||
| 
						 | 
					@ -152,6 +163,10 @@ type PayloadRepo struct {
 | 
				
			||||||
	Private     bool           `json:"private"`
 | 
						Private     bool           `json:"private"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type BasePayload interface {
 | 
				
			||||||
 | 
						GetJSONPayload() ([]byte, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Payload represents a payload information of hook.
 | 
					// Payload represents a payload information of hook.
 | 
				
			||||||
type Payload struct {
 | 
					type Payload struct {
 | 
				
			||||||
	Secret  string           `json:"secret"`
 | 
						Secret  string           `json:"secret"`
 | 
				
			||||||
| 
						 | 
					@ -161,25 +176,33 @@ type Payload struct {
 | 
				
			||||||
	Pusher  *PayloadAuthor   `json:"pusher"`
 | 
						Pusher  *PayloadAuthor   `json:"pusher"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p Payload) GetJSONPayload() ([]byte, error) {
 | 
				
			||||||
 | 
						data, err := json.Marshal(p)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return []byte{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return data, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// HookTask represents a hook task.
 | 
					// HookTask represents a hook task.
 | 
				
			||||||
type HookTask struct {
 | 
					type HookTask struct {
 | 
				
			||||||
	Id             int64
 | 
						Id             int64
 | 
				
			||||||
	Uuid           string
 | 
						Uuid           string
 | 
				
			||||||
	Type           HookTaskType
 | 
						Type           HookTaskType
 | 
				
			||||||
	Url            string
 | 
						Url            string
 | 
				
			||||||
	*Payload       `xorm:"-"`
 | 
						BasePayload    `xorm:"-"`
 | 
				
			||||||
	PayloadContent string `xorm:"TEXT"`
 | 
						PayloadContent string `xorm:"TEXT"`
 | 
				
			||||||
	ContentType    HookContentType
 | 
						ContentType    HookContentType
 | 
				
			||||||
	EventType      HookEventType
 | 
						EventType      HookEventType
 | 
				
			||||||
	IsSsl          bool
 | 
						IsSsl          bool
 | 
				
			||||||
	IsDeliveried   bool
 | 
						IsDelivered    bool
 | 
				
			||||||
	IsSucceed      bool
 | 
						IsSucceed      bool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 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 := json.Marshal(t.Payload)
 | 
						data, err := t.BasePayload.GetJSONPayload()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -198,7 +221,7 @@ func UpdateHookTask(t *HookTask) error {
 | 
				
			||||||
// DeliverHooks checks and delivers undelivered hooks.
 | 
					// DeliverHooks checks and delivers undelivered hooks.
 | 
				
			||||||
func DeliverHooks() {
 | 
					func DeliverHooks() {
 | 
				
			||||||
	timeout := time.Duration(setting.WebhookDeliverTimeout) * time.Second
 | 
						timeout := time.Duration(setting.WebhookDeliverTimeout) * time.Second
 | 
				
			||||||
	x.Where("is_deliveried=?", false).Iterate(new(HookTask),
 | 
						x.Where("is_delivered=?", false).Iterate(new(HookTask),
 | 
				
			||||||
		func(idx int, bean interface{}) error {
 | 
							func(idx int, bean interface{}) error {
 | 
				
			||||||
			t := bean.(*HookTask)
 | 
								t := bean.(*HookTask)
 | 
				
			||||||
			req := httplib.Post(t.Url).SetTimeout(timeout, timeout).
 | 
								req := httplib.Post(t.Url).SetTimeout(timeout, timeout).
 | 
				
			||||||
| 
						 | 
					@ -212,13 +235,36 @@ func DeliverHooks() {
 | 
				
			||||||
				req.Param("payload", t.PayloadContent)
 | 
									req.Param("payload", t.PayloadContent)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			t.IsDeliveried = true
 | 
								t.IsDelivered = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// TODO: record response.
 | 
								// TODO: record response.
 | 
				
			||||||
			if _, err := req.Response(); err != nil {
 | 
								switch t.Type {
 | 
				
			||||||
				log.Error(4, "Delivery: %v", err)
 | 
								case GOGS:
 | 
				
			||||||
			} else {
 | 
									{
 | 
				
			||||||
				t.IsSucceed = true
 | 
										if _, err := req.Response(); err != nil {
 | 
				
			||||||
 | 
											log.Error(4, "Delivery: %v", err)
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											t.IsSucceed = true
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								case SLACK:
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										if res, err := req.Response(); err != nil {
 | 
				
			||||||
 | 
											log.Error(4, "Delivery: %v", err)
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											defer res.Body.Close()
 | 
				
			||||||
 | 
											contents, err := ioutil.ReadAll(res.Body)
 | 
				
			||||||
 | 
											if err != nil {
 | 
				
			||||||
 | 
												log.Error(4, "%s", err)
 | 
				
			||||||
 | 
											} else {
 | 
				
			||||||
 | 
												if string(contents) != "ok" {
 | 
				
			||||||
 | 
													log.Error(4, "slack failed with: %s", string(contents))
 | 
				
			||||||
 | 
												} else {
 | 
				
			||||||
 | 
													t.IsSucceed = true
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if err := UpdateHookTask(t); err != nil {
 | 
								if err := UpdateHookTask(t); err != nil {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -69,17 +69,31 @@ func (f *RepoSettingForm) Validate(ctx *macaron.Context, errs *binding.Errors, l
 | 
				
			||||||
//        \/       \/    \/     \/     \/            \/
 | 
					//        \/       \/    \/     \/     \/            \/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type NewWebhookForm struct {
 | 
					type NewWebhookForm struct {
 | 
				
			||||||
	PayloadUrl  string `form:"payload_url" binding:"Required;Url"`
 | 
						HookTaskType string `form:"hook_type" binding:"Required"`
 | 
				
			||||||
	ContentType string `form:"content_type" binding:"Required"`
 | 
						PayloadUrl   string `form:"payload_url" binding:"Required;Url"`
 | 
				
			||||||
	Secret      string `form:"secret"`
 | 
						ContentType  string `form:"content_type" binding:"Required"`
 | 
				
			||||||
	PushOnly    bool   `form:"push_only"`
 | 
						Secret       string `form:"secret"`
 | 
				
			||||||
	Active      bool   `form:"active"`
 | 
						PushOnly     bool   `form:"push_only"`
 | 
				
			||||||
 | 
						Active       bool   `form:"active"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (f *NewWebhookForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
 | 
					func (f *NewWebhookForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
 | 
				
			||||||
	validate(errs, ctx.Data, f, l)
 | 
						validate(errs, ctx.Data, f, l)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type NewSlackHookForm struct {
 | 
				
			||||||
 | 
						HookTaskType string `form:"hook_type" binding:"Required"`
 | 
				
			||||||
 | 
						Domain       string `form:"domain" binding:"Required`
 | 
				
			||||||
 | 
						Token        string `form:"token" binding:"Required"`
 | 
				
			||||||
 | 
						Channel      string `form:"channel" binding:"Required"`
 | 
				
			||||||
 | 
						PushOnly     bool   `form:"push_only"`
 | 
				
			||||||
 | 
						Active       bool   `form:"active"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *NewSlackHookForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
 | 
				
			||||||
 | 
						validate(errs, ctx.Data, f, l)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// .___
 | 
					// .___
 | 
				
			||||||
// |   | ______ ________ __   ____
 | 
					// |   | ______ ________ __   ____
 | 
				
			||||||
// |   |/  ___//  ___/  |  \_/ __ \
 | 
					// |   |/  ___//  ___/  |  \_/ __ \
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1403,14 +1403,16 @@ The register and sign-in page style
 | 
				
			||||||
#auth-setting-form,
 | 
					#auth-setting-form,
 | 
				
			||||||
#org-setting-form,
 | 
					#org-setting-form,
 | 
				
			||||||
#repo-setting-form,
 | 
					#repo-setting-form,
 | 
				
			||||||
#user-profile-form {
 | 
					#user-profile-form,
 | 
				
			||||||
 | 
					.repo-setting-form {
 | 
				
			||||||
  background-color: #FFF;
 | 
					  background-color: #FFF;
 | 
				
			||||||
  padding: 30px 0;
 | 
					  padding: 30px 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#auth-setting-form textarea,
 | 
					#auth-setting-form textarea,
 | 
				
			||||||
#org-setting-form textarea,
 | 
					#org-setting-form textarea,
 | 
				
			||||||
#repo-setting-form textarea,
 | 
					#repo-setting-form textarea,
 | 
				
			||||||
#user-profile-form textarea {
 | 
					#user-profile-form textarea,
 | 
				
			||||||
 | 
					.repo-setting-form textarea {
 | 
				
			||||||
  margin-left: 4px;
 | 
					  margin-left: 4px;
 | 
				
			||||||
  height: 100px;
 | 
					  height: 100px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1418,24 +1420,38 @@ The register and sign-in page style
 | 
				
			||||||
#org-setting-form label,
 | 
					#org-setting-form label,
 | 
				
			||||||
#repo-setting-form label,
 | 
					#repo-setting-form label,
 | 
				
			||||||
#user-profile-form label,
 | 
					#user-profile-form label,
 | 
				
			||||||
 | 
					.repo-setting-form label,
 | 
				
			||||||
#auth-setting-form .form-label,
 | 
					#auth-setting-form .form-label,
 | 
				
			||||||
#org-setting-form .form-label,
 | 
					#org-setting-form .form-label,
 | 
				
			||||||
#repo-setting-form .form-label,
 | 
					#repo-setting-form .form-label,
 | 
				
			||||||
#user-profile-form .form-label {
 | 
					#user-profile-form .form-label,
 | 
				
			||||||
 | 
					.repo-setting-form .form-label {
 | 
				
			||||||
  width: 240px;
 | 
					  width: 240px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#auth-setting-form .ipt,
 | 
					#auth-setting-form .ipt,
 | 
				
			||||||
#org-setting-form .ipt,
 | 
					#org-setting-form .ipt,
 | 
				
			||||||
#repo-setting-form .ipt,
 | 
					#repo-setting-form .ipt,
 | 
				
			||||||
#user-profile-form .ipt {
 | 
					#user-profile-form .ipt,
 | 
				
			||||||
 | 
					.repo-setting-form .ipt {
 | 
				
			||||||
  width: 360px;
 | 
					  width: 360px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#auth-setting-form .field,
 | 
					#auth-setting-form .field,
 | 
				
			||||||
#org-setting-form .field,
 | 
					#org-setting-form .field,
 | 
				
			||||||
#repo-setting-form .field,
 | 
					#repo-setting-form .field,
 | 
				
			||||||
#user-profile-form .field {
 | 
					#user-profile-form .field,
 | 
				
			||||||
 | 
					.repo-setting-form .field {
 | 
				
			||||||
  margin-bottom: 24px;
 | 
					  margin-bottom: 24px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					#hook-type {
 | 
				
			||||||
 | 
					  padding: 10px 0 0 0;
 | 
				
			||||||
 | 
					  background-color: #fff;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#hook-type .field {
 | 
				
			||||||
 | 
					  margin-bottom: 24px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#hook-type label {
 | 
				
			||||||
 | 
					  width: 240px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
#repo-hooks-panel,
 | 
					#repo-hooks-panel,
 | 
				
			||||||
#repo-hooks-history-panel,
 | 
					#repo-hooks-history-panel,
 | 
				
			||||||
#user-social-panel,
 | 
					#user-social-panel,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -359,6 +359,22 @@ function initRepoSetting() {
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // web hook type change
 | 
				
			||||||
 | 
					    $('select#hook-type').on("change", function () {
 | 
				
			||||||
 | 
					      hookTypes = ['Gogs','Slack'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var curHook = $(this).val();
 | 
				
			||||||
 | 
					      hookTypes.forEach(function(hookType) {
 | 
				
			||||||
 | 
					        if (curHook === hookType) {
 | 
				
			||||||
 | 
					          $('div#'+hookType.toLowerCase()).toggleShow();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					          $('div#'+hookType.toLowerCase()).toggleHide();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    $('#transfer-button').click(function () {
 | 
					    $('#transfer-button').click(function () {
 | 
				
			||||||
        $('#transfer-form').show();
 | 
					        $('#transfer-form').show();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
| 
						 | 
					@ -594,4 +610,4 @@ function homepage() {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $('#promo-form').attr('action', '/user/sign_up');
 | 
					        $('#promo-form').attr('action', '/user/sign_up');
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,7 +34,8 @@
 | 
				
			||||||
#auth-setting-form,
 | 
					#auth-setting-form,
 | 
				
			||||||
#org-setting-form,
 | 
					#org-setting-form,
 | 
				
			||||||
#repo-setting-form,
 | 
					#repo-setting-form,
 | 
				
			||||||
#user-profile-form {
 | 
					#user-profile-form,
 | 
				
			||||||
 | 
					.repo-setting-form {
 | 
				
			||||||
    background-color: #FFF;
 | 
					    background-color: #FFF;
 | 
				
			||||||
    padding: 30px 0;
 | 
					    padding: 30px 0;
 | 
				
			||||||
    textarea {
 | 
					    textarea {
 | 
				
			||||||
| 
						 | 
					@ -53,6 +54,17 @@
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#hook-type {
 | 
				
			||||||
 | 
					    padding: 10px 0 0 0;
 | 
				
			||||||
 | 
					    background-color: #fff;
 | 
				
			||||||
 | 
					    .field {
 | 
				
			||||||
 | 
					        margin-bottom: 24px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    label {
 | 
				
			||||||
 | 
					        width: 240px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#repo-hooks-panel,
 | 
					#repo-hooks-panel,
 | 
				
			||||||
#repo-hooks-history-panel,
 | 
					#repo-hooks-history-panel,
 | 
				
			||||||
#user-social-panel,
 | 
					#user-social-panel,
 | 
				
			||||||
| 
						 | 
					@ -109,4 +121,4 @@
 | 
				
			||||||
  .field {
 | 
					  .field {
 | 
				
			||||||
    margin-bottom: 24px;
 | 
					    margin-bottom: 24px;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,7 @@
 | 
				
			||||||
package repo
 | 
					package repo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
| 
						 | 
					@ -272,11 +273,17 @@ func Webhooks(ctx *middleware.Context) {
 | 
				
			||||||
	ctx.HTML(200, HOOKS)
 | 
						ctx.HTML(200, HOOKS)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func renderHookTypes(ctx *middleware.Context) {
 | 
				
			||||||
 | 
						ctx.Data["HookTypes"] = []string{"Gogs", "Slack"}
 | 
				
			||||||
 | 
						ctx.Data["HookType"] = "Gogs"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func WebHooksNew(ctx *middleware.Context) {
 | 
					func WebHooksNew(ctx *middleware.Context) {
 | 
				
			||||||
	ctx.Data["Title"] = ctx.Tr("repo.settings")
 | 
						ctx.Data["Title"] = ctx.Tr("repo.settings")
 | 
				
			||||||
	ctx.Data["PageIsSettingsHooks"] = true
 | 
						ctx.Data["PageIsSettingsHooks"] = true
 | 
				
			||||||
	ctx.Data["PageIsSettingsHooksNew"] = true
 | 
						ctx.Data["PageIsSettingsHooksNew"] = true
 | 
				
			||||||
	ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
 | 
						ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
 | 
				
			||||||
 | 
						renderHookTypes(ctx)
 | 
				
			||||||
	ctx.HTML(200, HOOK_NEW)
 | 
						ctx.HTML(200, HOOK_NEW)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -304,8 +311,11 @@ func WebHooksNewPost(ctx *middleware.Context, form auth.NewWebhookForm) {
 | 
				
			||||||
		HookEvent: &models.HookEvent{
 | 
							HookEvent: &models.HookEvent{
 | 
				
			||||||
			PushOnly: form.PushOnly,
 | 
								PushOnly: form.PushOnly,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		IsActive: form.Active,
 | 
							IsActive:     form.Active,
 | 
				
			||||||
 | 
							HookTaskType: models.GOGS,
 | 
				
			||||||
 | 
							Meta:         "",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := w.UpdateEvent(); err != nil {
 | 
						if err := w.UpdateEvent(); err != nil {
 | 
				
			||||||
		ctx.Handle(500, "UpdateEvent", err)
 | 
							ctx.Handle(500, "UpdateEvent", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
| 
						 | 
					@ -338,6 +348,19 @@ func WebHooksEdit(ctx *middleware.Context) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// set data per HookTaskType
 | 
				
			||||||
 | 
						switch w.HookTaskType {
 | 
				
			||||||
 | 
						case models.SLACK:
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								ctx.Data["SlackHook"] = w.GetSlackHook()
 | 
				
			||||||
 | 
								ctx.Data["HookType"] = "slack"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								ctx.Data["HookType"] = "gogs"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	w.GetEvent()
 | 
						w.GetEvent()
 | 
				
			||||||
	ctx.Data["Webhook"] = w
 | 
						ctx.Data["Webhook"] = w
 | 
				
			||||||
	ctx.HTML(200, HOOK_NEW)
 | 
						ctx.HTML(200, HOOK_NEW)
 | 
				
			||||||
| 
						 | 
					@ -394,3 +417,104 @@ func WebHooksEditPost(ctx *middleware.Context, form auth.NewWebhookForm) {
 | 
				
			||||||
	ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
 | 
						ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
 | 
				
			||||||
	ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", ctx.Repo.RepoLink, hookId))
 | 
						ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", ctx.Repo.RepoLink, hookId))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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{}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ctx.HasError() {
 | 
				
			||||||
 | 
							ctx.HTML(200, HOOK_NEW)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						meta, err := json.Marshal(&models.Slack{
 | 
				
			||||||
 | 
							Domain:  form.Domain,
 | 
				
			||||||
 | 
							Channel: form.Channel,
 | 
				
			||||||
 | 
							Token:   form.Token,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.Handle(500, "SlackHooksNewPost: JSON marshal failed: ", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						w := &models.Webhook{
 | 
				
			||||||
 | 
							RepoId:      ctx.Repo.Repository.Id,
 | 
				
			||||||
 | 
							Url:         models.GetSlackURL(form.Domain, form.Token),
 | 
				
			||||||
 | 
							ContentType: models.JSON,
 | 
				
			||||||
 | 
							Secret:      "",
 | 
				
			||||||
 | 
							HookEvent: &models.HookEvent{
 | 
				
			||||||
 | 
								PushOnly: form.PushOnly,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							IsActive:     form.Active,
 | 
				
			||||||
 | 
							HookTaskType: models.SLACK,
 | 
				
			||||||
 | 
							Meta:         string(meta),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						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(ctx.Repo.RepoLink + "/settings/hooks")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func SlackHooksEditPost(ctx *middleware.Context, form auth.NewSlackHookForm) {
 | 
				
			||||||
 | 
						ctx.Data["Title"] = ctx.Tr("repo.settings")
 | 
				
			||||||
 | 
						ctx.Data["PageIsSettingsHooks"] = true
 | 
				
			||||||
 | 
						ctx.Data["PageIsSettingsHooksEdit"] = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						hookId := com.StrTo(ctx.Params(":id")).MustInt64()
 | 
				
			||||||
 | 
						fmt.Println("hookId slack=%d", hookId)
 | 
				
			||||||
 | 
						if hookId == 0 {
 | 
				
			||||||
 | 
							ctx.Handle(404, "setting.WebHooksEditPost", nil)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						w, err := models.GetWebhookById(hookId)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if err == models.ErrWebhookNotExist {
 | 
				
			||||||
 | 
								ctx.Handle(404, "GetWebhookById", nil)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								ctx.Handle(500, "GetWebhookById", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						w.GetEvent()
 | 
				
			||||||
 | 
						ctx.Data["Webhook"] = w
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ctx.HasError() {
 | 
				
			||||||
 | 
							ctx.HTML(200, HOOK_NEW)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						meta, err := json.Marshal(&models.Slack{
 | 
				
			||||||
 | 
							Domain:  form.Domain,
 | 
				
			||||||
 | 
							Channel: form.Channel,
 | 
				
			||||||
 | 
							Token:   form.Token,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.Handle(500, "SlackHooksNewPost: JSON marshal failed: ", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						w.Url = models.GetSlackURL(form.Domain, form.Token)
 | 
				
			||||||
 | 
						w.Meta = string(meta)
 | 
				
			||||||
 | 
						w.HookEvent = &models.HookEvent{
 | 
				
			||||||
 | 
							PushOnly: form.PushOnly,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						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, "SlackHooksEditPost", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
 | 
				
			||||||
 | 
						ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", ctx.Repo.RepoLink, hookId))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,23 @@
 | 
				
			||||||
 | 
					<div id="gogs" class="{{if (and .PageIsSettingsHooksEdit (not (eq .HookType "gogs")))}}hidden{{end}}">
 | 
				
			||||||
 | 
					  <form class="form form-align panel-body repo-setting-form" id="repo-setting-form-gogs"  action="{{.RepoLink}}/settings/hooks/gogs/{{if .PageIsSettingsHooksNew}}new{{else}}{{.Webhook.Id}}{{end}}" method="post">
 | 
				
			||||||
 | 
					    {{.CsrfTokenHtml}}
 | 
				
			||||||
 | 
					    <input type="hidden" name="hook_type" value="gogs">
 | 
				
			||||||
 | 
					    <div class="text-center panel-desc">{{.i18n.Tr "repo.settings.add_webhook_desc" | Str2html}}</div>
 | 
				
			||||||
 | 
					    <div class="field">
 | 
				
			||||||
 | 
					        <label class="req" for="payload-url">{{.i18n.Tr "repo.settings.payload_url"}}</label>
 | 
				
			||||||
 | 
					        <input class="ipt ipt-large ipt-radius {{if .Err_UserName}}ipt-error{{end}}" id="payload-url" name="payload_url" type="url" value="{{.Webhook.Url}}" required />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div class="field">
 | 
				
			||||||
 | 
					      <label class="req">{{.i18n.Tr "repo.settings.content_type"}}</label>
 | 
				
			||||||
 | 
					      <select name="content_type">
 | 
				
			||||||
 | 
					          <option value="1" {{if or .PageIsSettingsHooksNew (eq .Webhook.ContentType 1)}}selected{{end}}>application/json</option>
 | 
				
			||||||
 | 
					          <option value="2" {{if eq .Webhook.ContentType 2}}selected{{end}}>application/x-www-form-urlencoded</option>
 | 
				
			||||||
 | 
					      </select>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div class="field">
 | 
				
			||||||
 | 
					        <label for="secret">{{.i18n.Tr "repo.settings.secret"}}</label>
 | 
				
			||||||
 | 
					        <input class="ipt ipt-large ipt-radius {{if .Err_UserName}}ipt-error{{end}}" id="secret" name="secret" type="password" value="{{.Webhook.Secret}}" autocomplete="off" />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    {{template "repo/settings/hook_settings" .}}
 | 
				
			||||||
 | 
					  </form>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
| 
						 | 
					@ -13,40 +13,9 @@
 | 
				
			||||||
	                        <div class="panel-header">
 | 
						                        <div class="panel-header">
 | 
				
			||||||
	                        	<strong>{{if .PageIsSettingsHooksNew}}{{.i18n.Tr "repo.settings.add_webhook"}}{{else}}{{.i18n.Tr "repo.settings.update_webhook"}}{{end}}</strong>
 | 
						                        	<strong>{{if .PageIsSettingsHooksNew}}{{.i18n.Tr "repo.settings.add_webhook"}}{{else}}{{.i18n.Tr "repo.settings.update_webhook"}}{{end}}</strong>
 | 
				
			||||||
	                        </div>
 | 
						                        </div>
 | 
				
			||||||
	                        <form class="form form-align panel-body" id="repo-setting-form" action="{{.RepoLink}}/settings/hooks/{{if .PageIsSettingsHooksNew}}new{{else}}{{.Webhook.Id}}{{end}}" method="post">
 | 
					                          {{template "repo/settings/hook_types" .}}
 | 
				
			||||||
	                            {{.CsrfTokenHtml}}
 | 
					                          {{template "repo/settings/gogs_hook" .}}
 | 
				
			||||||
                        		<div class="text-center panel-desc">{{.i18n.Tr "repo.settings.add_webhook_desc" | Str2html}}</div>
 | 
					                          {{template "repo/settings/slack_hook" .}}
 | 
				
			||||||
	                            <div class="field">
 | 
					 | 
				
			||||||
	                                <label class="req" for="payload-url">{{.i18n.Tr "repo.settings.payload_url"}}</label>
 | 
					 | 
				
			||||||
	                                <input class="ipt ipt-large ipt-radius {{if .Err_UserName}}ipt-error{{end}}" id="payload-url" name="payload_url" type="url" value="{{.Webhook.Url}}" required />
 | 
					 | 
				
			||||||
	                            </div>
 | 
					 | 
				
			||||||
					            <div class="field">
 | 
					 | 
				
			||||||
					                <label class="req">{{.i18n.Tr "repo.settings.content_type"}}</label>
 | 
					 | 
				
			||||||
					                <select name="content_type">
 | 
					 | 
				
			||||||
                                		<option value="1" {{if or .PageIsSettingsHooksNew (eq .Webhook.ContentType 1)}}selected{{end}}>application/json</option>
 | 
					 | 
				
			||||||
                                		<option value="2" {{if eq .Webhook.ContentType 2}}selected{{end}}>application/x-www-form-urlencoded</option>
 | 
					 | 
				
			||||||
					                </select>
 | 
					 | 
				
			||||||
					            </div>
 | 
					 | 
				
			||||||
	                            <div class="field">
 | 
					 | 
				
			||||||
	                                <label for="secret">{{.i18n.Tr "repo.settings.secret"}}</label>
 | 
					 | 
				
			||||||
	                                <input class="ipt ipt-large ipt-radius {{if .Err_UserName}}ipt-error{{end}}" id="secret" name="secret" type="password" value="{{.Webhook.Secret}}" autocomplete="off" />
 | 
					 | 
				
			||||||
	                            </div>
 | 
					 | 
				
			||||||
								<div class="field">
 | 
					 | 
				
			||||||
									<h4 class="text-center">{{.i18n.Tr "repo.settings.event_desc"}}</h4>
 | 
					 | 
				
			||||||
									<label></label>
 | 
					 | 
				
			||||||
	                                <input name="push_only" type="radio" {{if or .PageIsSettingsHooksNew .Webhook.PushOnly}}checked{{end}}> {{.i18n.Tr "repo.settings.event_push_only" | Str2html}}
 | 
					 | 
				
			||||||
	                            </div>
 | 
					 | 
				
			||||||
					            <div class="field">
 | 
					 | 
				
			||||||
					                <label for="active">{{.i18n.Tr "repo.settings.active"}}</label>
 | 
					 | 
				
			||||||
					                <input class="ipt-chk" id="active" name="active" type="checkbox" {{if or .PageIsSettingsHooksNew .Webhook.IsActive}}checked{{end}} />
 | 
					 | 
				
			||||||
					                <span>{{.i18n.Tr "repo.settings.active_helper"}}</span>
 | 
					 | 
				
			||||||
					            </div>
 | 
					 | 
				
			||||||
	                            <div class="field">
 | 
					 | 
				
			||||||
	                                <label></label>
 | 
					 | 
				
			||||||
	                                <button class="btn btn-green btn-large btn-radius">{{if .PageIsSettingsHooksNew}}{{.i18n.Tr "repo.settings.add_webhook"}}{{else}}{{.i18n.Tr "repo.settings.update_webhook"}}{{end}}</button>
 | 
					 | 
				
			||||||
	                                {{if .PageIsSettingsHooksEdit}}<a class="btn btn-red btn-large btn-link btn-radius" href="{{.RepoLink}}/settings/hooks?remove={{.Webhook.Id}}"><strong>{{.i18n.Tr "repo.settings.delete_webhook"}}</strong></a>{{end}}
 | 
					 | 
				
			||||||
	                            </div>
 | 
					 | 
				
			||||||
							</form>
 | 
					 | 
				
			||||||
	                    </div>
 | 
						                    </div>
 | 
				
			||||||
	                </div>
 | 
						                </div>
 | 
				
			||||||
	                {{if .PageIsSettingsHooksEdit}}
 | 
						                {{if .PageIsSettingsHooksEdit}}
 | 
				
			||||||
| 
						 | 
					@ -67,4 +36,4 @@
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
{{template "ng/base/footer" .}}
 | 
					{{template "ng/base/footer" .}}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,15 @@
 | 
				
			||||||
 | 
					<div class="field">
 | 
				
			||||||
 | 
					  <h4 class="text-center">{{.i18n.Tr "repo.settings.event_desc"}}</h4>
 | 
				
			||||||
 | 
					  <label></label>
 | 
				
			||||||
 | 
					  <input name="push_only" type="radio" {{if or .PageIsSettingsHooksNew .Webhook.PushOnly}}checked{{end}}> {{.i18n.Tr "repo.settings.event_push_only" | Str2html}}
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<div class="field">
 | 
				
			||||||
 | 
					  <label for="active">{{.i18n.Tr "repo.settings.active"}}</label>
 | 
				
			||||||
 | 
					  <input class="ipt-chk" id="active" name="active" type="checkbox" {{if or .PageIsSettingsHooksNew .Webhook.IsActive}}checked{{end}} />
 | 
				
			||||||
 | 
					<span>{{.i18n.Tr "repo.settings.active_helper"}}</span>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<div class="field">
 | 
				
			||||||
 | 
					    <label></label>
 | 
				
			||||||
 | 
					    <button class="btn btn-green btn-large btn-radius">{{if .PageIsSettingsHooksNew}}{{.i18n.Tr "repo.settings.add_webhook"}}{{else}}{{.i18n.Tr "repo.settings.update_webhook"}}{{end}}</button>
 | 
				
			||||||
 | 
					    {{if .PageIsSettingsHooksEdit}}<a class="btn btn-red btn-large btn-link btn-radius" href="{{.RepoLink}}/settings/hooks?remove={{.Webhook.Id}}"><strong>{{.i18n.Tr "repo.settings.delete_webhook"}}</strong></a>{{end}}
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					{{if .PageIsSettingsHooksNew}}
 | 
				
			||||||
 | 
					<div id="hook-type" class="form-align">
 | 
				
			||||||
 | 
					  <label class="req">{{.i18n.Tr "repo.settings.hook_type"}}</label>
 | 
				
			||||||
 | 
					  <select name="hook_type" id="hook-type" class="form-control">
 | 
				
			||||||
 | 
					    {{if .HookType}}<option value="{{.HookType}}">{{.HookType}}</option>{{end}}
 | 
				
			||||||
 | 
					    {{range .HookTypes}}
 | 
				
			||||||
 | 
					    {{if not (eq $.HookType .)}}<option value="{{.}}" >{{.}}</option>{{end}}
 | 
				
			||||||
 | 
					    {{end}}
 | 
				
			||||||
 | 
					  </select>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{{end}}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,20 @@
 | 
				
			||||||
 | 
					<div id="slack" class="{{if or .PageIsSettingsHooksNew (and .PageIsSettingsHooksEdit (not (eq .HookType "slack")))}}hidden{{end}}">
 | 
				
			||||||
 | 
					  <form class="form form-align panel-body repo-setting-form" id="repo-setting-form-slack" action="{{.RepoLink}}/settings/hooks/slack/{{if .PageIsSettingsHooksNew}}new{{else}}{{.Webhook.Id}}{{end}}" method="post">
 | 
				
			||||||
 | 
					    {{.CsrfTokenHtml}}
 | 
				
			||||||
 | 
					    <input type="hidden" name="hook_type" value="slack">
 | 
				
			||||||
 | 
					    <div class="text-center panel-desc">{{.i18n.Tr "repo.settings.add_slack_hook_desc" | Str2html}}</div>
 | 
				
			||||||
 | 
					    <div class="field">
 | 
				
			||||||
 | 
					        <label class="req" for="domain">{{.i18n.Tr "repo.settings.slack_domain"}}</label>
 | 
				
			||||||
 | 
					        <input class="ipt ipt-large ipt-radius {{if .Err_UserName}}ipt-error{{end}}" id="domain" name="domain" type="text" value="{{.SlackHook.Domain}}" placeholde="myslack" required />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div class="field">
 | 
				
			||||||
 | 
					        <label class="req" for="token">{{.i18n.Tr "repo.settings.slack_token"}}</label>
 | 
				
			||||||
 | 
					        <input class="ipt ipt-large ipt-radius {{if .Err_UserName}}ipt-error{{end}}" id="token" name="token" type="text" value="{{.SlackHook.Token}}" autocomplete="off" required />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div class="field">
 | 
				
			||||||
 | 
					        <label class="req" for="channel">{{.i18n.Tr "repo.settings.slack_channel"}}</label>
 | 
				
			||||||
 | 
					        <input class="ipt ipt-large ipt-radius {{if .Err_UserName}}ipt-error{{end}}" id="channel" name="channel" type="text" value="{{.SlackHook.Channel}}" placeholder="#general" required />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    {{template "repo/settings/hook_settings" .}}
 | 
				
			||||||
 | 
					  </form>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
		Loading…
	
		Reference in New Issue