Unknwon лет назад: 8
Родитель
Сommit
404867f206

+ 1 - 1
README.md

@@ -5,7 +5,7 @@ Gogs - Go Git Service [![Build Status](https://travis-ci.org/gogits/gogs.svg?bra
 
 ![](public/img/gogs-large-resize.png)
 
-##### Current version: 0.7.31 Beta
+##### Current version: 0.7.32 Beta
 
 <table>
     <tr>

+ 1 - 0
cmd/web.go

@@ -397,6 +397,7 @@ func runWeb(ctx *cli.Context) {
 				m.Post("/gogs/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost)
 				m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost)
 				m.Get("/:id", repo.WebHooksEdit)
+				m.Post("/:id/test", repo.TestWebhook)
 				m.Post("/gogs/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost)
 				m.Post("/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost)
 

+ 3 - 0
conf/locale/locale_en-US.ini

@@ -597,6 +597,9 @@ settings.hooks_desc = Webhooks are much like basic HTTP POST event triggers. Whe
 settings.webhook_deletion = Delete Webhook
 settings.webhook_deletion_desc = Delete this webhook will remove its information and all delivery history. Do you want to continue?
 settings.webhook_deletion_success = Webhook has been deleted successfully!
+settings.webhook.test_delivery = Test Delivery
+settings.webhook.test_delivery_desc = Send a fake push event delivery to test your webhook settings
+settings.webhook.test_delivery_success = Test webhook has been added to delivery queue. It may taks few seconds before it shows up in the delivery history.
 settings.webhook.request = Request
 settings.webhook.response = Response
 settings.webhook.headers = Headers

+ 1 - 1
gogs.go

@@ -17,7 +17,7 @@ import (
 	"github.com/gogits/gogs/modules/setting"
 )
 
-const APP_VER = "0.7.31.1205 Beta"
+const APP_VER = "0.7.32.1205 Beta"
 
 func init() {
 	runtime.GOMAXPROCS(runtime.NumCPU())

+ 2 - 17
models/action.go

@@ -451,24 +451,9 @@ func CommitRepoAction(
 		IsPrivate:    repo.IsPrivate,
 	}); err != nil {
 		return fmt.Errorf("NotifyWatchers: %v", err)
-
 	}
 
-	repoLink := fmt.Sprintf("%s%s/%s", setting.AppUrl, repoUserName, repoName)
-	payloadRepo := &api.PayloadRepo{
-		ID:          repo.ID,
-		Name:        repo.LowerName,
-		URL:         repoLink,
-		Description: repo.Description,
-		Website:     repo.Website,
-		Watchers:    repo.NumWatches,
-		Owner: &api.PayloadAuthor{
-			Name:     repo.Owner.DisplayName(),
-			Email:    repo.Owner.Email,
-			UserName: repo.Owner.Name,
-		},
-		Private: repo.IsPrivate,
-	}
+	payloadRepo := repo.ComposePayload()
 
 	pusher_email, pusher_name := "", ""
 	pusher, err := GetUserByName(userName)
@@ -494,7 +479,7 @@ func CommitRepoAction(
 			commits[i] = &api.PayloadCommit{
 				ID:      cmt.Sha1,
 				Message: cmt.Message,
-				URL:     fmt.Sprintf("%s/commit/%s", repoLink, cmt.Sha1),
+				URL:     fmt.Sprintf("%s/commit/%s", repo.RepoLink(), cmt.Sha1),
 				Author: &api.PayloadAuthor{
 					Name:     cmt.AuthorName,
 					Email:    cmt.AuthorEmail,

+ 22 - 0
models/repo.go

@@ -28,6 +28,7 @@ import (
 	"gopkg.in/ini.v1"
 
 	"github.com/gogits/git-shell"
+	api "github.com/gogits/go-gogs-client"
 
 	"github.com/gogits/gogs/modules/base"
 	"github.com/gogits/gogs/modules/bindata"
@@ -380,6 +381,27 @@ func (repo *Repository) SavePatch(index int64, patch []byte) error {
 	return nil
 }
 
+// ComposePayload composes and returns *api.PayloadRepo corresponding to the repository.
+func (repo *Repository) ComposePayload() *api.PayloadRepo {
+	cl := repo.CloneLink()
+	return &api.PayloadRepo{
+		ID:          repo.ID,
+		Name:        repo.LowerName,
+		URL:         repo.RepoLink(),
+		SSHURL:      cl.SSH,
+		CloneURL:    cl.HTTPS,
+		Description: repo.Description,
+		Website:     repo.Website,
+		Watchers:    repo.NumWatches,
+		Owner: &api.PayloadAuthor{
+			Name:     repo.MustOwner().DisplayName(),
+			Email:    repo.MustOwner().Email,
+			UserName: repo.MustOwner().Name,
+		},
+		Private: repo.IsPrivate,
+	}
+}
+
 func isRepositoryExist(e Engine, u *User, repoName string) (bool, error) {
 	has, err := e.Get(&Repository{
 		OwnerID:   u.Id,

+ 6 - 6
models/webhook.go

@@ -335,7 +335,7 @@ func (t *HookTask) AfterSet(colName string, _ xorm.Cell) {
 
 		t.ResponseInfo = &HookResponse{}
 		if err = json.Unmarshal([]byte(t.ResponseContent), t.ResponseInfo); err != nil {
-			log.Error(3, "Unmarshal[%d]: %v", t.ID, err)
+			log.Error(3, "Unmarshal [%d]: %v", t.ID, err)
 		}
 	}
 }
@@ -343,7 +343,7 @@ func (t *HookTask) AfterSet(colName string, _ xorm.Cell) {
 func (t *HookTask) MarshalJSON(v interface{}) string {
 	p, err := json.Marshal(v)
 	if err != nil {
-		log.Error(3, "Marshal[%d]: %v", t.ID, err)
+		log.Error(3, "Marshal [%d]: %v", t.ID, err)
 	}
 	return string(p)
 }
@@ -590,24 +590,24 @@ func DeliverHooks() {
 	// Update hook task status.
 	for _, t := range tasks {
 		if err := UpdateHookTask(t); err != nil {
-			log.Error(4, "UpdateHookTask(%d): %v", t.ID, err)
+			log.Error(4, "UpdateHookTask [%d]: %v", t.ID, err)
 		}
 	}
 
 	// Start listening on new hook requests.
 	for repoID := range HookQueue.Queue() {
-		log.Trace("DeliverHooks[%v]: processing delivery hooks", repoID)
+		log.Trace("DeliverHooks [%v]: processing delivery hooks", repoID)
 		HookQueue.Remove(repoID)
 
 		tasks = make([]*HookTask, 0, 5)
 		if err := x.Where("repo_id=? AND is_delivered=?", repoID, false).Find(&tasks); err != nil {
-			log.Error(4, "Get repository(%d) hook tasks: %v", repoID, err)
+			log.Error(4, "Get repository [%d] hook tasks: %v", repoID, err)
 			continue
 		}
 		for _, t := range tasks {
 			t.deliver()
 			if err := UpdateHookTask(t); err != nil {
-				log.Error(4, "UpdateHookTask[%d]: %v", t.ID, err)
+				log.Error(4, "UpdateHookTask [%d]: %v", t.ID, err)
 				continue
 			}
 		}

Разница между файлами не показана из-за своего большого размера
+ 2 - 2
modules/bindata/bindata.go


+ 13 - 0
public/js/gogs.js

@@ -548,6 +548,19 @@ function initWebhook() {
             $('.events.fields').hide();
         }
     });
+
+    // Test delivery
+    $('#test-delivery').click(function () {
+        var $this = $(this);
+        $this.addClass('loading disabled');
+        $.post($this.data('link'), {
+            "_csrf": csrf
+        }).done(
+            setTimeout(function () {
+                window.location.href = $this.data('redirect');
+            }, 5000)
+        )
+    });
 }
 
 

+ 0 - 332
routers/repo/setting.go

@@ -5,14 +5,9 @@
 package repo
 
 import (
-	"encoding/json"
-	"errors"
-	"fmt"
 	"strings"
 	"time"
 
-	"github.com/Unknwon/com"
-
 	"github.com/gogits/gogs/models"
 	"github.com/gogits/gogs/modules/auth"
 	"github.com/gogits/gogs/modules/base"
@@ -26,9 +21,6 @@ import (
 const (
 	SETTINGS_OPTIONS base.TplName = "repo/settings/options"
 	COLLABORATION    base.TplName = "repo/settings/collaboration"
-	HOOKS            base.TplName = "repo/settings/hooks"
-	HOOK_NEW         base.TplName = "repo/settings/hook_new"
-	ORG_HOOK_NEW     base.TplName = "org/settings/hook_new"
 	GITHOOKS         base.TplName = "repo/settings/githooks"
 	GITHOOK_EDIT     base.TplName = "repo/settings/githook_edit"
 	DEPLOY_KEYS      base.TplName = "repo/settings/deploy_keys"
@@ -270,330 +262,6 @@ func Collaboration(ctx *middleware.Context) {
 	ctx.HTML(200, COLLABORATION)
 }
 
-func Webhooks(ctx *middleware.Context) {
-	ctx.Data["Title"] = ctx.Tr("repo.settings.hooks")
-	ctx.Data["PageIsSettingsHooks"] = true
-	ctx.Data["BaseLink"] = ctx.Repo.RepoLink
-	ctx.Data["Description"] = ctx.Tr("repo.settings.hooks_desc", "https://github.com/gogits/go-gogs-client/wiki/Repositories-Webhooks")
-
-	ws, err := models.GetWebhooksByRepoID(ctx.Repo.Repository.ID)
-	if err != nil {
-		ctx.Handle(500, "GetWebhooksByRepoID", err)
-		return
-	}
-	ctx.Data["Webhooks"] = ws
-
-	ctx.HTML(200, HOOKS)
-}
-
-type OrgRepoCtx struct {
-	OrgID       int64
-	RepoID      int64
-	Link        string
-	NewTemplate base.TplName
-}
-
-// getOrgRepoCtx determines whether this is a repo context or organization context.
-func getOrgRepoCtx(ctx *middleware.Context) (*OrgRepoCtx, error) {
-	if len(ctx.Repo.RepoLink) > 0 {
-		return &OrgRepoCtx{
-			RepoID:      ctx.Repo.Repository.ID,
-			Link:        ctx.Repo.RepoLink,
-			NewTemplate: HOOK_NEW,
-		}, nil
-	}
-
-	if len(ctx.Org.OrgLink) > 0 {
-		return &OrgRepoCtx{
-			OrgID:       ctx.Org.Organization.Id,
-			Link:        ctx.Org.OrgLink,
-			NewTemplate: ORG_HOOK_NEW,
-		}, nil
-	}
-
-	return nil, errors.New("Unable to set OrgRepo context")
-}
-
-func checkHookType(ctx *middleware.Context) string {
-	hookType := strings.ToLower(ctx.Params(":type"))
-	if !com.IsSliceContainsStr(setting.Webhook.Types, hookType) {
-		ctx.Handle(404, "checkHookType", nil)
-		return ""
-	}
-	return hookType
-}
-
-func WebhooksNew(ctx *middleware.Context) {
-	ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
-	ctx.Data["PageIsSettingsHooks"] = true
-	ctx.Data["PageIsSettingsHooksNew"] = true
-	ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
-
-	orCtx, err := getOrgRepoCtx(ctx)
-	if err != nil {
-		ctx.Handle(500, "getOrgRepoCtx", err)
-		return
-	}
-
-	ctx.Data["HookType"] = checkHookType(ctx)
-	if ctx.Written() {
-		return
-	}
-	ctx.Data["BaseLink"] = orCtx.Link
-
-	ctx.HTML(200, orCtx.NewTemplate)
-}
-
-func ParseHookEvent(form auth.WebhookForm) *models.HookEvent {
-	return &models.HookEvent{
-		PushOnly:       form.PushOnly(),
-		SendEverything: form.SendEverything(),
-		ChooseEvents:   form.ChooseEvents(),
-		HookEvents: models.HookEvents{
-			Create: form.Create,
-			Push:   form.Push,
-		},
-	}
-}
-
-func WebHooksNewPost(ctx *middleware.Context, form auth.NewWebhookForm) {
-	ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
-	ctx.Data["PageIsSettingsHooks"] = true
-	ctx.Data["PageIsSettingsHooksNew"] = true
-	ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
-	ctx.Data["HookType"] = "gogs"
-
-	orCtx, err := getOrgRepoCtx(ctx)
-	if err != nil {
-		ctx.Handle(500, "getOrgRepoCtx", err)
-		return
-	}
-	ctx.Data["BaseLink"] = orCtx.Link
-
-	if ctx.HasError() {
-		ctx.HTML(200, orCtx.NewTemplate)
-		return
-	}
-
-	contentType := models.JSON
-	if models.HookContentType(form.ContentType) == models.FORM {
-		contentType = models.FORM
-	}
-
-	w := &models.Webhook{
-		RepoID:       orCtx.RepoID,
-		URL:          form.PayloadURL,
-		ContentType:  contentType,
-		Secret:       form.Secret,
-		HookEvent:    ParseHookEvent(form.WebhookForm),
-		IsActive:     form.Active,
-		HookTaskType: models.GOGS,
-		OrgID:        orCtx.OrgID,
-	}
-	if err := w.UpdateEvent(); err != nil {
-		ctx.Handle(500, "UpdateEvent", err)
-		return
-	} else if err := models.CreateWebhook(w); err != nil {
-		ctx.Handle(500, "CreateWebhook", err)
-		return
-	}
-
-	ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
-	ctx.Redirect(orCtx.Link + "/settings/hooks")
-}
-
-func SlackHooksNewPost(ctx *middleware.Context, form auth.NewSlackHookForm) {
-	ctx.Data["Title"] = ctx.Tr("repo.settings")
-	ctx.Data["PageIsSettingsHooks"] = true
-	ctx.Data["PageIsSettingsHooksNew"] = true
-	ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
-
-	orCtx, err := getOrgRepoCtx(ctx)
-	if err != nil {
-		ctx.Handle(500, "getOrgRepoCtx", err)
-		return
-	}
-
-	if ctx.HasError() {
-		ctx.HTML(200, orCtx.NewTemplate)
-		return
-	}
-
-	meta, err := json.Marshal(&models.SlackMeta{
-		Channel:  form.Channel,
-		Username: form.Username,
-		IconURL:  form.IconURL,
-		Color:    form.Color,
-	})
-	if err != nil {
-		ctx.Handle(500, "Marshal", err)
-		return
-	}
-
-	w := &models.Webhook{
-		RepoID:       orCtx.RepoID,
-		URL:          form.PayloadURL,
-		ContentType:  models.JSON,
-		HookEvent:    ParseHookEvent(form.WebhookForm),
-		IsActive:     form.Active,
-		HookTaskType: models.SLACK,
-		Meta:         string(meta),
-		OrgID:        orCtx.OrgID,
-	}
-	if err := w.UpdateEvent(); err != nil {
-		ctx.Handle(500, "UpdateEvent", err)
-		return
-	} else if err := models.CreateWebhook(w); err != nil {
-		ctx.Handle(500, "CreateWebhook", err)
-		return
-	}
-
-	ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
-	ctx.Redirect(orCtx.Link + "/settings/hooks")
-}
-
-func checkWebhook(ctx *middleware.Context) (*OrgRepoCtx, *models.Webhook) {
-	ctx.Data["RequireHighlightJS"] = true
-
-	orCtx, err := getOrgRepoCtx(ctx)
-	if err != nil {
-		ctx.Handle(500, "getOrgRepoCtx", err)
-		return nil, nil
-	}
-	ctx.Data["BaseLink"] = orCtx.Link
-
-	w, err := models.GetWebhookByID(ctx.ParamsInt64(":id"))
-	if err != nil {
-		if models.IsErrWebhookNotExist(err) {
-			ctx.Handle(404, "GetWebhookByID", nil)
-		} else {
-			ctx.Handle(500, "GetWebhookByID", err)
-		}
-		return nil, nil
-	}
-
-	switch w.HookTaskType {
-	case models.SLACK:
-		ctx.Data["SlackHook"] = w.GetSlackHook()
-		ctx.Data["HookType"] = "slack"
-	default:
-		ctx.Data["HookType"] = "gogs"
-	}
-
-	ctx.Data["History"], err = w.History(1)
-	if err != nil {
-		ctx.Handle(500, "History", err)
-	}
-	return orCtx, w
-}
-
-func WebHooksEdit(ctx *middleware.Context) {
-	ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
-	ctx.Data["PageIsSettingsHooks"] = true
-	ctx.Data["PageIsSettingsHooksEdit"] = true
-
-	orCtx, w := checkWebhook(ctx)
-	if ctx.Written() {
-		return
-	}
-	ctx.Data["Webhook"] = w
-
-	ctx.HTML(200, orCtx.NewTemplate)
-}
-
-func WebHooksEditPost(ctx *middleware.Context, form auth.NewWebhookForm) {
-	ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
-	ctx.Data["PageIsSettingsHooks"] = true
-	ctx.Data["PageIsSettingsHooksEdit"] = true
-
-	orCtx, w := checkWebhook(ctx)
-	if ctx.Written() {
-		return
-	}
-	ctx.Data["Webhook"] = w
-
-	if ctx.HasError() {
-		ctx.HTML(200, orCtx.NewTemplate)
-		return
-	}
-
-	contentType := models.JSON
-	if models.HookContentType(form.ContentType) == models.FORM {
-		contentType = models.FORM
-	}
-
-	w.URL = form.PayloadURL
-	w.ContentType = contentType
-	w.Secret = form.Secret
-	w.HookEvent = ParseHookEvent(form.WebhookForm)
-	w.IsActive = form.Active
-	if err := w.UpdateEvent(); err != nil {
-		ctx.Handle(500, "UpdateEvent", err)
-		return
-	} else if err := models.UpdateWebhook(w); err != nil {
-		ctx.Handle(500, "WebHooksEditPost", err)
-		return
-	}
-
-	ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
-	ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
-}
-
-func SlackHooksEditPost(ctx *middleware.Context, form auth.NewSlackHookForm) {
-	ctx.Data["Title"] = ctx.Tr("repo.settings")
-	ctx.Data["PageIsSettingsHooks"] = true
-	ctx.Data["PageIsSettingsHooksEdit"] = true
-
-	orCtx, w := checkWebhook(ctx)
-	if ctx.Written() {
-		return
-	}
-	ctx.Data["Webhook"] = w
-
-	if ctx.HasError() {
-		ctx.HTML(200, orCtx.NewTemplate)
-		return
-	}
-
-	meta, err := json.Marshal(&models.SlackMeta{
-		Channel:  form.Channel,
-		Username: form.Username,
-		IconURL:  form.IconURL,
-		Color:    form.Color,
-	})
-	if err != nil {
-		ctx.Handle(500, "Marshal", err)
-		return
-	}
-
-	w.URL = form.PayloadURL
-	w.Meta = string(meta)
-	w.HookEvent = ParseHookEvent(form.WebhookForm)
-	w.IsActive = form.Active
-	if err := w.UpdateEvent(); err != nil {
-		ctx.Handle(500, "UpdateEvent", err)
-		return
-	} else if err := models.UpdateWebhook(w); err != nil {
-		ctx.Handle(500, "UpdateWebhook", err)
-		return
-	}
-
-	ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
-	ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
-}
-
-func DeleteWebhook(ctx *middleware.Context) {
-	if err := models.DeleteWebhook(ctx.QueryInt64("id")); err != nil {
-		ctx.Flash.Error("DeleteWebhook: " + err.Error())
-	} else {
-		ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
-	}
-
-	ctx.JSON(200, map[string]interface{}{
-		"redirect": ctx.Repo.RepoLink + "/settings/hooks",
-	})
-}
-
 func parseOwnerAndRepo(ctx *middleware.Context) (*models.User, *models.Repository) {
 	owner, err := models.GetUserByName(ctx.Params(":username"))
 	if err != nil {

+ 385 - 0
routers/repo/webhook.go

@@ -0,0 +1,385 @@
+// Copyright 2015 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package repo
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"strings"
+
+	"github.com/Unknwon/com"
+
+	api "github.com/gogits/go-gogs-client"
+
+	"github.com/gogits/gogs/models"
+	"github.com/gogits/gogs/modules/auth"
+	"github.com/gogits/gogs/modules/base"
+	"github.com/gogits/gogs/modules/middleware"
+	"github.com/gogits/gogs/modules/setting"
+)
+
+const (
+	HOOKS        base.TplName = "repo/settings/hooks"
+	HOOK_NEW     base.TplName = "repo/settings/hook_new"
+	ORG_HOOK_NEW base.TplName = "org/settings/hook_new"
+)
+
+func Webhooks(ctx *middleware.Context) {
+	ctx.Data["Title"] = ctx.Tr("repo.settings.hooks")
+	ctx.Data["PageIsSettingsHooks"] = true
+	ctx.Data["BaseLink"] = ctx.Repo.RepoLink
+	ctx.Data["Description"] = ctx.Tr("repo.settings.hooks_desc", "https://github.com/gogits/go-gogs-client/wiki/Repositories-Webhooks")
+
+	ws, err := models.GetWebhooksByRepoID(ctx.Repo.Repository.ID)
+	if err != nil {
+		ctx.Handle(500, "GetWebhooksByRepoID", err)
+		return
+	}
+	ctx.Data["Webhooks"] = ws
+
+	ctx.HTML(200, HOOKS)
+}
+
+type OrgRepoCtx struct {
+	OrgID       int64
+	RepoID      int64
+	Link        string
+	NewTemplate base.TplName
+}
+
+// getOrgRepoCtx determines whether this is a repo context or organization context.
+func getOrgRepoCtx(ctx *middleware.Context) (*OrgRepoCtx, error) {
+	if len(ctx.Repo.RepoLink) > 0 {
+		return &OrgRepoCtx{
+			RepoID:      ctx.Repo.Repository.ID,
+			Link:        ctx.Repo.RepoLink,
+			NewTemplate: HOOK_NEW,
+		}, nil
+	}
+
+	if len(ctx.Org.OrgLink) > 0 {
+		return &OrgRepoCtx{
+			OrgID:       ctx.Org.Organization.Id,
+			Link:        ctx.Org.OrgLink,
+			NewTemplate: ORG_HOOK_NEW,
+		}, nil
+	}
+
+	return nil, errors.New("Unable to set OrgRepo context")
+}
+
+func checkHookType(ctx *middleware.Context) string {
+	hookType := strings.ToLower(ctx.Params(":type"))
+	if !com.IsSliceContainsStr(setting.Webhook.Types, hookType) {
+		ctx.Handle(404, "checkHookType", nil)
+		return ""
+	}
+	return hookType
+}
+
+func WebhooksNew(ctx *middleware.Context) {
+	ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
+	ctx.Data["PageIsSettingsHooks"] = true
+	ctx.Data["PageIsSettingsHooksNew"] = true
+	ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
+
+	orCtx, err := getOrgRepoCtx(ctx)
+	if err != nil {
+		ctx.Handle(500, "getOrgRepoCtx", err)
+		return
+	}
+
+	ctx.Data["HookType"] = checkHookType(ctx)
+	if ctx.Written() {
+		return
+	}
+	ctx.Data["BaseLink"] = orCtx.Link
+
+	ctx.HTML(200, orCtx.NewTemplate)
+}
+
+func ParseHookEvent(form auth.WebhookForm) *models.HookEvent {
+	return &models.HookEvent{
+		PushOnly:       form.PushOnly(),
+		SendEverything: form.SendEverything(),
+		ChooseEvents:   form.ChooseEvents(),
+		HookEvents: models.HookEvents{
+			Create: form.Create,
+			Push:   form.Push,
+		},
+	}
+}
+
+func WebHooksNewPost(ctx *middleware.Context, form auth.NewWebhookForm) {
+	ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
+	ctx.Data["PageIsSettingsHooks"] = true
+	ctx.Data["PageIsSettingsHooksNew"] = true
+	ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
+	ctx.Data["HookType"] = "gogs"
+
+	orCtx, err := getOrgRepoCtx(ctx)
+	if err != nil {
+		ctx.Handle(500, "getOrgRepoCtx", err)
+		return
+	}
+	ctx.Data["BaseLink"] = orCtx.Link
+
+	if ctx.HasError() {
+		ctx.HTML(200, orCtx.NewTemplate)
+		return
+	}
+
+	contentType := models.JSON
+	if models.HookContentType(form.ContentType) == models.FORM {
+		contentType = models.FORM
+	}
+
+	w := &models.Webhook{
+		RepoID:       orCtx.RepoID,
+		URL:          form.PayloadURL,
+		ContentType:  contentType,
+		Secret:       form.Secret,
+		HookEvent:    ParseHookEvent(form.WebhookForm),
+		IsActive:     form.Active,
+		HookTaskType: models.GOGS,
+		OrgID:        orCtx.OrgID,
+	}
+	if err := w.UpdateEvent(); err != nil {
+		ctx.Handle(500, "UpdateEvent", err)
+		return
+	} else if err := models.CreateWebhook(w); err != nil {
+		ctx.Handle(500, "CreateWebhook", err)
+		return
+	}
+
+	ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
+	ctx.Redirect(orCtx.Link + "/settings/hooks")
+}
+
+func SlackHooksNewPost(ctx *middleware.Context, form auth.NewSlackHookForm) {
+	ctx.Data["Title"] = ctx.Tr("repo.settings")
+	ctx.Data["PageIsSettingsHooks"] = true
+	ctx.Data["PageIsSettingsHooksNew"] = true
+	ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
+
+	orCtx, err := getOrgRepoCtx(ctx)
+	if err != nil {
+		ctx.Handle(500, "getOrgRepoCtx", err)
+		return
+	}
+
+	if ctx.HasError() {
+		ctx.HTML(200, orCtx.NewTemplate)
+		return
+	}
+
+	meta, err := json.Marshal(&models.SlackMeta{
+		Channel:  form.Channel,
+		Username: form.Username,
+		IconURL:  form.IconURL,
+		Color:    form.Color,
+	})
+	if err != nil {
+		ctx.Handle(500, "Marshal", err)
+		return
+	}
+
+	w := &models.Webhook{
+		RepoID:       orCtx.RepoID,
+		URL:          form.PayloadURL,
+		ContentType:  models.JSON,
+		HookEvent:    ParseHookEvent(form.WebhookForm),
+		IsActive:     form.Active,
+		HookTaskType: models.SLACK,
+		Meta:         string(meta),
+		OrgID:        orCtx.OrgID,
+	}
+	if err := w.UpdateEvent(); err != nil {
+		ctx.Handle(500, "UpdateEvent", err)
+		return
+	} else if err := models.CreateWebhook(w); err != nil {
+		ctx.Handle(500, "CreateWebhook", err)
+		return
+	}
+
+	ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
+	ctx.Redirect(orCtx.Link + "/settings/hooks")
+}
+
+func checkWebhook(ctx *middleware.Context) (*OrgRepoCtx, *models.Webhook) {
+	ctx.Data["RequireHighlightJS"] = true
+
+	orCtx, err := getOrgRepoCtx(ctx)
+	if err != nil {
+		ctx.Handle(500, "getOrgRepoCtx", err)
+		return nil, nil
+	}
+	ctx.Data["BaseLink"] = orCtx.Link
+
+	w, err := models.GetWebhookByID(ctx.ParamsInt64(":id"))
+	if err != nil {
+		if models.IsErrWebhookNotExist(err) {
+			ctx.Handle(404, "GetWebhookByID", nil)
+		} else {
+			ctx.Handle(500, "GetWebhookByID", err)
+		}
+		return nil, nil
+	}
+
+	switch w.HookTaskType {
+	case models.SLACK:
+		ctx.Data["SlackHook"] = w.GetSlackHook()
+		ctx.Data["HookType"] = "slack"
+	default:
+		ctx.Data["HookType"] = "gogs"
+	}
+
+	ctx.Data["History"], err = w.History(1)
+	if err != nil {
+		ctx.Handle(500, "History", err)
+	}
+	return orCtx, w
+}
+
+func WebHooksEdit(ctx *middleware.Context) {
+	ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
+	ctx.Data["PageIsSettingsHooks"] = true
+	ctx.Data["PageIsSettingsHooksEdit"] = true
+
+	orCtx, w := checkWebhook(ctx)
+	if ctx.Written() {
+		return
+	}
+	ctx.Data["Webhook"] = w
+
+	ctx.HTML(200, orCtx.NewTemplate)
+}
+
+func WebHooksEditPost(ctx *middleware.Context, form auth.NewWebhookForm) {
+	ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
+	ctx.Data["PageIsSettingsHooks"] = true
+	ctx.Data["PageIsSettingsHooksEdit"] = true
+
+	orCtx, w := checkWebhook(ctx)
+	if ctx.Written() {
+		return
+	}
+	ctx.Data["Webhook"] = w
+
+	if ctx.HasError() {
+		ctx.HTML(200, orCtx.NewTemplate)
+		return
+	}
+
+	contentType := models.JSON
+	if models.HookContentType(form.ContentType) == models.FORM {
+		contentType = models.FORM
+	}
+
+	w.URL = form.PayloadURL
+	w.ContentType = contentType
+	w.Secret = form.Secret
+	w.HookEvent = ParseHookEvent(form.WebhookForm)
+	w.IsActive = form.Active
+	if err := w.UpdateEvent(); err != nil {
+		ctx.Handle(500, "UpdateEvent", err)
+		return
+	} else if err := models.UpdateWebhook(w); err != nil {
+		ctx.Handle(500, "WebHooksEditPost", err)
+		return
+	}
+
+	ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
+	ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
+}
+
+func SlackHooksEditPost(ctx *middleware.Context, form auth.NewSlackHookForm) {
+	ctx.Data["Title"] = ctx.Tr("repo.settings")
+	ctx.Data["PageIsSettingsHooks"] = true
+	ctx.Data["PageIsSettingsHooksEdit"] = true
+
+	orCtx, w := checkWebhook(ctx)
+	if ctx.Written() {
+		return
+	}
+	ctx.Data["Webhook"] = w
+
+	if ctx.HasError() {
+		ctx.HTML(200, orCtx.NewTemplate)
+		return
+	}
+
+	meta, err := json.Marshal(&models.SlackMeta{
+		Channel:  form.Channel,
+		Username: form.Username,
+		IconURL:  form.IconURL,
+		Color:    form.Color,
+	})
+	if err != nil {
+		ctx.Handle(500, "Marshal", err)
+		return
+	}
+
+	w.URL = form.PayloadURL
+	w.Meta = string(meta)
+	w.HookEvent = ParseHookEvent(form.WebhookForm)
+	w.IsActive = form.Active
+	if err := w.UpdateEvent(); err != nil {
+		ctx.Handle(500, "UpdateEvent", err)
+		return
+	} else if err := models.UpdateWebhook(w); err != nil {
+		ctx.Handle(500, "UpdateWebhook", err)
+		return
+	}
+
+	ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
+	ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
+}
+
+func TestWebhook(ctx *middleware.Context) {
+	p := &api.PushPayload{
+		Ref:    ctx.Repo.Repository.DefaultBranch,
+		Before: ctx.Repo.CommitID,
+		After:  ctx.Repo.CommitID,
+		Commits: []*api.PayloadCommit{
+			{
+				ID:      ctx.Repo.CommitID,
+				Message: ctx.Repo.Commit.Message(),
+				URL:     ctx.Repo.RepoLink + "/commit/" + ctx.Repo.CommitID,
+				Author: &api.PayloadAuthor{
+					Name:  ctx.Repo.Commit.Author.Name,
+					Email: ctx.Repo.Commit.Author.Email,
+				},
+			},
+		},
+		Repo: ctx.Repo.Repository.ComposePayload(),
+		Sender: &api.PayloadUser{
+			UserName:  ctx.User.Name,
+			ID:        ctx.User.Id,
+			AvatarUrl: setting.AppUrl + ctx.User.RelAvatarLink(),
+		},
+	}
+	if err := models.PrepareWebhooks(ctx.Repo.Repository, models.HOOK_EVENT_PUSH, p); err != nil {
+		ctx.Flash.Error("PrepareWebhooks: " + err.Error())
+		ctx.Status(500)
+	} else {
+		go models.HookQueue.Add(ctx.Repo.Repository.ID)
+		ctx.Flash.Info(ctx.Tr("repo.settings.webhook.test_delivery_success"))
+		ctx.Status(200)
+	}
+}
+
+func DeleteWebhook(ctx *middleware.Context) {
+	if err := models.DeleteWebhook(ctx.QueryInt64("id")); err != nil {
+		ctx.Flash.Error("DeleteWebhook: " + err.Error())
+	} else {
+		ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
+	}
+
+	ctx.JSON(200, map[string]interface{}{
+		"redirect": ctx.Repo.RepoLink + "/settings/hooks",
+	})
+}

+ 1 - 1
templates/.VERSION

@@ -1 +1 @@
-0.7.31.1205 Beta
+0.7.32.1205 Beta

+ 6 - 0
templates/repo/settings/hook_history.tmpl

@@ -1,6 +1,12 @@
 {{if .PageIsSettingsHooksEdit}}
 <h4 class="ui top attached header">
   {{.i18n.Tr "repo.settings.recent_deliveries"}}
+  {{if .IsRepositoryAdmin}}
+  <div class="ui right">
+  	<button class="ui teal tiny button poping up" id="test-delivery" data-content=
+  	"{{.i18n.Tr "repo.settings.webhook.test_delivery_desc"}}" data-variation="inverted tiny" data-link="{{.Link}}/test" data-redirect="{{.Link}}">{{.i18n.Tr "repo.settings.webhook.test_delivery"}}</button>
+  </div>
+  {{end}}
 </h4>
 <div class="ui attached table segment">
 	<div class="ui hook history list">