new webhooks list UI

This commit is contained in:
Unknwon 2015-08-26 21:45:51 +08:00
parent 1cb03135b7
commit 2881456421
28 changed files with 329 additions and 180 deletions

View File

@ -303,6 +303,7 @@ func runWeb(ctx *cli.Context) {
adminReq := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: true, AdminRequire: true}) adminReq := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: true, AdminRequire: true})
// ***** START: Admin *****
m.Group("/admin", func() { m.Group("/admin", func() {
m.Get("", adminReq, admin.Dashboard) m.Get("", adminReq, admin.Dashboard)
m.Get("/config", admin.Config) m.Get("/config", admin.Config)
@ -339,6 +340,7 @@ func runWeb(ctx *cli.Context) {
m.Get("/:id:int/delete", admin.DeleteNotice) m.Get("/:id:int/delete", admin.DeleteNotice)
}) })
}, adminReq) }, adminReq)
// ***** END: Admin *****
m.Group("", func() { m.Group("", func() {
m.Get("/:username", user.Profile) m.Get("/:username", user.Profile)
@ -377,7 +379,7 @@ func runWeb(ctx *cli.Context) {
reqRepoAdmin := middleware.RequireRepoAdmin() reqRepoAdmin := middleware.RequireRepoAdmin()
// Organization. // ***** START: Organization *****
m.Group("/org", func() { m.Group("/org", func() {
m.Get("/create", org.Create) m.Get("/create", org.Create)
m.Post("/create", bindIgnErr(auth.CreateOrgForm{}), org.CreatePost) m.Post("/create", bindIgnErr(auth.CreateOrgForm{}), org.CreatePost)
@ -403,9 +405,14 @@ func runWeb(ctx *cli.Context) {
m.Post("/teams/:team/delete", org.DeleteTeam) m.Post("/teams/:team/delete", org.DeleteTeam)
m.Group("/settings", func() { m.Group("/settings", func() {
m.Get("", org.Settings) m.Combo("").Get(org.Settings).
m.Post("", bindIgnErr(auth.UpdateOrgSettingForm{}), org.SettingsPost) Post(bindIgnErr(auth.UpdateOrgSettingForm{}), org.SettingsPost)
m.Get("/hooks", org.SettingsHooks)
m.Group("/hooks", func() {
m.Get("", org.Webhooks)
m.Post("/delete", org.DeleteWebhook)
})
m.Get("/hooks/new", repo.WebHooksNew) m.Get("/hooks/new", repo.WebHooksNew)
m.Post("/hooks/gogs/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost) m.Post("/hooks/gogs/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost)
m.Post("/hooks/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost) m.Post("/hooks/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost)
@ -421,8 +428,9 @@ func runWeb(ctx *cli.Context) {
m.Group("/org", func() { m.Group("/org", func() {
m.Get("/:org", org.Home) m.Get("/:org", org.Home)
}, ignSignIn, middleware.OrgAssignment(true)) }, ignSignIn, middleware.OrgAssignment(true))
// ***** END: Organization *****
// Repository. // ***** START: Repository *****
m.Group("/repo", func() { m.Group("/repo", func() {
m.Get("/create", repo.Create) m.Get("/create", repo.Create)
m.Post("/create", bindIgnErr(auth.CreateRepoForm{}), repo.CreatePost) m.Post("/create", bindIgnErr(auth.CreateRepoForm{}), repo.CreatePost)
@ -433,11 +441,15 @@ func runWeb(ctx *cli.Context) {
}, reqSignIn) }, reqSignIn)
m.Group("/:username/:reponame", func() { m.Group("/:username/:reponame", func() {
m.Get("/settings", repo.Settings)
m.Post("/settings", bindIgnErr(auth.RepoSettingForm{}), repo.SettingsPost)
m.Group("/settings", func() { m.Group("/settings", func() {
m.Route("/collaboration", "GET,POST", repo.SettingsCollaboration) m.Combo("").Get(repo.Settings).
m.Get("/hooks", repo.Webhooks) Post(bindIgnErr(auth.RepoSettingForm{}), repo.SettingsPost)
m.Route("/collaboration", "GET,POST", repo.Collaboration)
m.Group("/hooks", func() {
m.Get("", repo.Webhooks)
m.Post("/delete", repo.DeleteWebhook)
})
m.Get("/hooks/new", repo.WebHooksNew) m.Get("/hooks/new", repo.WebHooksNew)
m.Post("/hooks/gogs/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost) m.Post("/hooks/gogs/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost)
m.Post("/hooks/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost) m.Post("/hooks/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost)
@ -446,14 +458,14 @@ func runWeb(ctx *cli.Context) {
m.Post("/hooks/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost) m.Post("/hooks/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost)
m.Group("/hooks/git", func() { m.Group("/hooks/git", func() {
m.Get("", repo.SettingsGitHooks) m.Get("", repo.GitHooks)
m.Combo("/:name").Get(repo.SettingsGitHooksEdit). m.Combo("/:name").Get(repo.GitHooksEdit).
Post(repo.SettingsGitHooksEditPost) Post(repo.GitHooksEditPost)
}, middleware.GitHookService()) }, middleware.GitHookService())
m.Group("/keys", func() { m.Group("/keys", func() {
m.Combo("").Get(repo.SettingsDeployKeys). m.Combo("").Get(repo.DeployKeys).
Post(bindIgnErr(auth.AddSSHKeyForm{}), repo.SettingsDeployKeysPost) Post(bindIgnErr(auth.AddSSHKeyForm{}), repo.DeployKeysPost)
m.Post("/delete", repo.DeleteDeployKey) m.Post("/delete", repo.DeleteDeployKey)
}) })
@ -536,6 +548,7 @@ func runWeb(ctx *cli.Context) {
m.Head("/hooks/trigger", repo.TriggerHook) m.Head("/hooks/trigger", repo.TriggerHook)
}) })
}) })
// ***** END: Repository *****
// robots.txt // robots.txt
m.Get("/robots.txt", func(ctx *middleware.Context) { m.Get("/robots.txt", func(ctx *middleware.Context) {

View File

@ -510,12 +510,14 @@ settings.remove_collaborator_success = Collaborator has been removed.
settings.user_is_org_member = User is organization member who cannot be added as a collaborator. settings.user_is_org_member = User is organization member who cannot be added as a collaborator.
settings.add_webhook = Add Webhook settings.add_webhook = Add Webhook
settings.hooks_desc = Webhooks are much like basic HTTP POST event triggers. Whenever something occurs in Gogs, we will handle the notification to the target host you specify. Learn more in this <a target="_blank" href="%s">Webhooks Guide</a>. settings.hooks_desc = Webhooks are much like basic HTTP POST event triggers. Whenever something occurs in Gogs, we will handle the notification to the target host you specify. Learn more in this <a target="_blank" href="%s">Webhooks Guide</a>.
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.githooks_desc = Git Hooks are powered by Git itself, you can edit files of supported hooks in the list below to perform custom operations. settings.githooks_desc = Git Hooks are powered by Git itself, you can edit files of supported hooks in the list below to perform custom operations.
settings.githook_edit_desc = If the hook is inactive, sample content will be presented. Leaving content to an empty value will disable this hook. settings.githook_edit_desc = If the hook is inactive, sample content will be presented. Leaving content to an empty value will disable this hook.
settings.githook_name = Hook Name settings.githook_name = Hook Name
settings.githook_content = Hook Content settings.githook_content = Hook Content
settings.update_githook = Update Hook settings.update_githook = Update Hook
settings.remove_hook_success = Webhook has been removed.
settings.add_webhook_desc = Gogs will send a <code>POST</code> request to the URL you specify, along with regarding the event that occured. You can also specify what kind of data format you'd like to get upon triggering the hook (JSON, x-www-form-urlencoded, XML, etc). More information can be found in our <a target="_blank" href="%s">Webhooks Guide</a>. settings.add_webhook_desc = Gogs will send a <code>POST</code> request to the URL you specify, along with regarding the event that occured. You can also specify what kind of data format you'd like to get upon triggering the hook (JSON, x-www-form-urlencoded, XML, etc). More information can be found in our <a target="_blank" href="%s">Webhooks Guide</a>.
settings.payload_url = Payload URL settings.payload_url = Payload URL
settings.content_type = Content Type settings.content_type = Content Type

View File

@ -395,6 +395,26 @@
"strictMath": 0, "strictMath": 0,
"strictUnits": 0 "strictUnits": 0
}, },
"\/public\/less\/_organization.less": {
"allowInsecureImports": 0,
"createSourceMap": 0,
"disableJavascript": 0,
"fileType": 1,
"ieCompatibility": 1,
"ignore": 1,
"ignoreWasSetByUser": 0,
"inputAbbreviatedPath": "\/public\/less\/_organization.less",
"outputAbbreviatedPath": "\/public\/css\/_organization.css",
"outputPathIsOutsideProject": 0,
"outputPathIsSetByUser": 0,
"outputStyle": 0,
"relativeURLS": 0,
"shouldRunAutoprefixer": 0,
"shouldRunBless": 0,
"strictImports": 0,
"strictMath": 0,
"strictUnits": 0
},
"\/public\/less\/_repository.less": { "\/public\/less\/_repository.less": {
"allowInsecureImports": 0, "allowInsecureImports": 0,
"createSourceMap": 0, "createSourceMap": 0,

View File

@ -445,13 +445,13 @@ func CommitRepoAction(userID, repoUserID int64, userName, actEmail string,
if err = CreateHookTask(&HookTask{ if err = CreateHookTask(&HookTask{
RepoID: repo.ID, RepoID: repo.ID,
HookID: w.Id, HookID: w.ID,
Type: w.HookTaskType, Type: w.HookTaskType,
Url: w.Url, Url: w.URL,
BasePayload: payload, BasePayload: payload,
ContentType: w.ContentType, ContentType: w.ContentType,
EventType: HOOK_EVENT_PUSH, EventType: HOOK_EVENT_PUSH,
IsSsl: w.IsSsl, IsSsl: w.IsSSL,
}); err != nil { }); err != nil {
return fmt.Errorf("CreateHookTask: %v", err) return fmt.Errorf("CreateHookTask: %v", err)
} }

View File

@ -111,8 +111,11 @@ func (u *User) DashboardLink() string {
return setting.AppSubUrl + "/" return setting.AppSubUrl + "/"
} }
// HomeLink returns the user home page link. // HomeLink returns the user or organization home page link.
func (u *User) HomeLink() string { func (u *User) HomeLink() string {
if u.IsOrganization() {
return setting.AppSubUrl + "/org/" + u.Name
}
return setting.AppSubUrl + "/" + u.Name return setting.AppSubUrl + "/" + u.Name
} }
@ -162,6 +165,15 @@ func (u *User) AvatarLink() string {
return setting.GravatarSource + u.Avatar return setting.GravatarSource + u.Avatar
} }
// DisplayName returns full name if it's not empty,
// returns username otherwise.
func (u *User) DisplayName() string {
if len(u.FullName) > 0 {
return u.FullName
}
return u.Name
}
// 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{

View File

@ -60,20 +60,29 @@ type HookEvent struct {
PushOnly bool `json:"push_only"` PushOnly bool `json:"push_only"`
} }
type HookStatus int
const (
HOOK_STATUS_NONE = iota
HOOK_STATUS_SUCCEED
HOOK_STATUS_FAILED
)
// Webhook represents a web hook object. // Webhook represents a web hook object.
type Webhook struct { type Webhook struct {
Id int64 ID int64 `xorm:"pk autoincr"`
RepoId int64 RepoID int64
Url string `xorm:"TEXT"` OrgID int64
URL string `xorm:"url 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 `xorm:"is_ssl"`
IsActive bool IsActive bool
HookTaskType HookTaskType HookTaskType HookTaskType
Meta string `xorm:"TEXT"` // store hook-specific attributes Meta string `xorm:"TEXT"` // store hook-specific attributes
OrgId int64 LastStatus HookStatus // Last delivery status
Created time.Time `xorm:"CREATED"` Created time.Time `xorm:"CREATED"`
Updated time.Time `xorm:"UPDATED"` Updated time.Time `xorm:"UPDATED"`
} }
@ -82,14 +91,14 @@ type Webhook struct {
func (w *Webhook) GetEvent() { func (w *Webhook) GetEvent() {
w.HookEvent = &HookEvent{} w.HookEvent = &HookEvent{}
if err := json.Unmarshal([]byte(w.Events), w.HookEvent); err != nil { if err := json.Unmarshal([]byte(w.Events), w.HookEvent); err != nil {
log.Error(4, "webhook.GetEvent(%d): %v", w.Id, err) log.Error(4, "webhook.GetEvent(%d): %v", w.ID, err)
} }
} }
func (w *Webhook) GetSlackHook() *Slack { func (w *Webhook) GetSlackHook() *Slack {
s := &Slack{} s := &Slack{}
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)
} }
return s return s
} }
@ -117,7 +126,7 @@ func CreateWebhook(w *Webhook) error {
// GetWebhookById returns webhook by given ID. // GetWebhookById returns webhook by given ID.
func GetWebhookById(hookId int64) (*Webhook, error) { func GetWebhookById(hookId int64) (*Webhook, error) {
w := &Webhook{Id: hookId} w := &Webhook{ID: hookId}
has, err := x.Get(w) has, err := x.Get(w)
if err != nil { if err != nil {
return nil, err return nil, err
@ -134,26 +143,37 @@ func GetActiveWebhooksByRepoId(repoId int64) (ws []*Webhook, err error) {
} }
// GetWebhooksByRepoId returns all webhooks of repository. // GetWebhooksByRepoId returns all webhooks of repository.
func GetWebhooksByRepoId(repoId int64) (ws []*Webhook, err error) { func GetWebhooksByRepoId(repoID int64) (ws []*Webhook, err error) {
err = x.Find(&ws, &Webhook{RepoId: repoId}) err = x.Find(&ws, &Webhook{RepoID: repoID})
return ws, err return ws, err
} }
// UpdateWebhook updates information of webhook. // UpdateWebhook updates information of webhook.
func UpdateWebhook(w *Webhook) error { func UpdateWebhook(w *Webhook) error {
_, err := x.Id(w.Id).AllCols().Update(w) _, err := x.Id(w.ID).AllCols().Update(w)
return err return err
} }
// DeleteWebhook deletes webhook of repository. // DeleteWebhook deletes webhook of repository.
func DeleteWebhook(hookId int64) error { func DeleteWebhook(id int64) (err error) {
_, err := x.Delete(&Webhook{Id: hookId}) sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err return err
} }
if _, err = sess.Delete(&Webhook{ID: id}); err != nil {
return err
} else if _, err = sess.Delete(&HookTask{HookID: id}); err != nil {
return err
}
return sess.Commit()
}
// GetWebhooksByOrgId returns all webhooks for an organization. // GetWebhooksByOrgId returns all webhooks for an organization.
func GetWebhooksByOrgId(orgId int64) (ws []*Webhook, err error) { func GetWebhooksByOrgId(orgID int64) (ws []*Webhook, err error) {
err = x.Find(&ws, &Webhook{OrgId: orgId}) err = x.Find(&ws, &Webhook{OrgID: orgID})
return ws, err return ws, err
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -73,9 +73,21 @@ img {
.text { .text {
&.red { &.red {
color: #d95c5c!important; color: #d95c5c!important;
a {
color: #d95c5c!important;
&:hover {
color: #E67777!important;
}
}
} }
&.blue { &.blue {
color: #428bca!important; color: #428bca!important;
a {
color: #15c!important;
&:hover {
color: #428bca!important;
}
}
} }
&.grey { &.grey {
color: #767676!important; color: #767676!important;
@ -105,6 +117,10 @@ img {
white-space: nowrap; white-space: nowrap;
display: inline-block; display: inline-block;
} }
&.thin {
font-weight: normal;
}
} }
.message { .message {

View File

@ -59,7 +59,7 @@
} }
} }
} }
.repository.edit.hook { .repository.edit.githook {
form { form {
@input-padding: 25%!important; @input-padding: 25%!important;
.inline.field > label { .inline.field > label {
@ -73,7 +73,7 @@
} }
} }
} }
.repository.edit.hook { .repository.edit.githook {
form { form {
textarea { textarea {
width: 70%!important; width: 70%!important;

View File

@ -0,0 +1,17 @@
.organization {
padding-top: 15px;
padding-bottom: @footer-margin * 2;
.head {
.ui.header {
.text {
vertical-align: middle;
font-size: 1.6rem;
margin-left: 15px;
}
.ui.right {
margin-top: 5px;
}
}
}
}

View File

@ -629,8 +629,9 @@
} }
.item { .item {
padding: 10px 20px; padding: 10px 20px;
i { .octicon,
margin-right: 5px; .fa {
width: 20px;
} }
} }
} }

View File

@ -5,6 +5,7 @@
@import "_install"; @import "_install";
@import "_form"; @import "_form";
@import "_repository"; @import "_repository";
@import "_organization";
@import "_user"; @import "_user";
@import "_dashboard"; @import "_dashboard";
@import "_admin"; @import "_admin";

View File

@ -26,7 +26,7 @@ func ListRepoHooks(ctx *middleware.Context) {
apiHooks := make([]*api.Hook, len(hooks)) apiHooks := make([]*api.Hook, len(hooks))
for i := range hooks { for i := range hooks {
h := &api.Hook{ h := &api.Hook{
Id: hooks[i].Id, ID: hooks[i].ID,
Type: hooks[i].HookTaskType.Name(), Type: hooks[i].HookTaskType.Name(),
Active: hooks[i].IsActive, Active: hooks[i].IsActive,
Config: make(map[string]string), Config: make(map[string]string),
@ -35,7 +35,7 @@ func ListRepoHooks(ctx *middleware.Context) {
// Currently, onle have push event. // Currently, onle have push event.
h.Events = []string{"push"} h.Events = []string{"push"}
h.Config["url"] = hooks[i].Url h.Config["url"] = hooks[i].URL
h.Config["content_type"] = hooks[i].ContentType.Name() h.Config["content_type"] = hooks[i].ContentType.Name()
if hooks[i].HookTaskType == models.SLACK { if hooks[i].HookTaskType == models.SLACK {
s := hooks[i].GetSlackHook() s := hooks[i].GetSlackHook()
@ -67,8 +67,8 @@ func CreateRepoHook(ctx *middleware.Context, form api.CreateHookOption) {
} }
w := &models.Webhook{ w := &models.Webhook{
RepoId: ctx.Repo.Repository.ID, RepoID: ctx.Repo.Repository.ID,
Url: form.Config["url"], URL: form.Config["url"],
ContentType: models.ToHookContentType(form.Config["content_type"]), ContentType: models.ToHookContentType(form.Config["content_type"]),
Secret: form.Config["secret"], Secret: form.Config["secret"],
HookEvent: &models.HookEvent{ HookEvent: &models.HookEvent{
@ -102,12 +102,12 @@ func CreateRepoHook(ctx *middleware.Context, form api.CreateHookOption) {
} }
apiHook := &api.Hook{ apiHook := &api.Hook{
Id: w.Id, ID: w.ID,
Type: w.HookTaskType.Name(), Type: w.HookTaskType.Name(),
Events: []string{"push"}, Events: []string{"push"},
Active: w.IsActive, Active: w.IsActive,
Config: map[string]string{ Config: map[string]string{
"url": w.Url, "url": w.URL,
"content_type": w.ContentType.Name(), "content_type": w.ContentType.Name(),
}, },
} }
@ -129,7 +129,7 @@ func EditRepoHook(ctx *middleware.Context, form api.EditHookOption) {
if form.Config != nil { if form.Config != nil {
if url, ok := form.Config["url"]; ok { if url, ok := form.Config["url"]; ok {
w.Url = url w.URL = url
} }
if ct, ok := form.Config["content_type"]; ok { if ct, ok := form.Config["content_type"]; ok {
if !models.IsValidHookContentType(ct) { if !models.IsValidHookContentType(ct) {

View File

@ -105,9 +105,11 @@ func SettingsDelete(ctx *middleware.Context) {
ctx.HTML(200, SETTINGS_DELETE) ctx.HTML(200, SETTINGS_DELETE)
} }
func SettingsHooks(ctx *middleware.Context) { func Webhooks(ctx *middleware.Context) {
ctx.Data["Title"] = ctx.Tr("org.settings") ctx.Data["Title"] = ctx.Tr("org.settings")
ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooks"] = true
ctx.Data["BaseLink"] = ctx.Org.OrgLink
ctx.Data["Description"] = ctx.Tr("org.settings.hooks_desc")
// Delete web hook. // Delete web hook.
remove := com.StrTo(ctx.Query("remove")).MustInt64() remove := com.StrTo(ctx.Query("remove")).MustInt64()
@ -130,3 +132,15 @@ func SettingsHooks(ctx *middleware.Context) {
ctx.Data["Webhooks"] = ws ctx.Data["Webhooks"] = ws
ctx.HTML(200, SETTINGS_HOOKS) ctx.HTML(200, SETTINGS_HOOKS)
} }
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.Org.OrgLink + "/settings/hooks",
})
}

View File

@ -173,7 +173,7 @@ func SettingsPost(ctx *middleware.Context, form auth.RepoSettingForm) {
} }
} }
func SettingsCollaboration(ctx *middleware.Context) { func Collaboration(ctx *middleware.Context) {
ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["Title"] = ctx.Tr("repo.settings")
ctx.Data["PageIsSettingsCollaboration"] = true ctx.Data["PageIsSettingsCollaboration"] = true
@ -249,26 +249,16 @@ func SettingsCollaboration(ctx *middleware.Context) {
func Webhooks(ctx *middleware.Context) { func Webhooks(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["BaseLink"] = ctx.Repo.RepoLink
// Delete web hook. ctx.Data["Description"] = ctx.Tr("repo.settings.hooks_desc", "http://gogs.io/docs/features/webhook.html")
remove := com.StrTo(ctx.Query("remove")).MustInt64()
if remove > 0 {
if err := models.DeleteWebhook(remove); err != nil {
ctx.Handle(500, "DeleteWebhook", err)
return
}
ctx.Flash.Success(ctx.Tr("repo.settings.remove_hook_success"))
ctx.Redirect(ctx.Repo.RepoLink + "/settings/hooks")
return
}
ws, err := models.GetWebhooksByRepoId(ctx.Repo.Repository.ID) ws, err := models.GetWebhooksByRepoId(ctx.Repo.Repository.ID)
if err != nil { if err != nil {
ctx.Handle(500, "GetWebhooksByRepoId", err) ctx.Handle(500, "GetWebhooksByRepoId", err)
return return
} }
ctx.Data["Webhooks"] = ws ctx.Data["Webhooks"] = ws
ctx.HTML(200, HOOKS) ctx.HTML(200, HOOKS)
} }
@ -318,8 +308,8 @@ func WebHooksNewPost(ctx *middleware.Context, form auth.NewWebhookForm) {
} }
w := &models.Webhook{ w := &models.Webhook{
RepoId: orCtx.RepoId, RepoID: orCtx.RepoId,
Url: form.PayloadUrl, URL: form.PayloadUrl,
ContentType: ct, ContentType: ct,
Secret: form.Secret, Secret: form.Secret,
HookEvent: &models.HookEvent{ HookEvent: &models.HookEvent{
@ -328,7 +318,7 @@ func WebHooksNewPost(ctx *middleware.Context, form auth.NewWebhookForm) {
IsActive: form.Active, IsActive: form.Active,
HookTaskType: models.GOGS, HookTaskType: models.GOGS,
Meta: "", Meta: "",
OrgId: orCtx.OrgId, OrgID: orCtx.OrgId,
} }
if err := w.UpdateEvent(); err != nil { if err := w.UpdateEvent(); err != nil {
@ -429,7 +419,7 @@ func WebHooksEditPost(ctx *middleware.Context, form auth.NewWebhookForm) {
ct = models.FORM ct = models.FORM
} }
w.Url = form.PayloadUrl w.URL = form.PayloadUrl
w.ContentType = ct w.ContentType = ct
w.Secret = form.Secret w.Secret = form.Secret
w.HookEvent = &models.HookEvent{ w.HookEvent = &models.HookEvent{
@ -474,8 +464,8 @@ func SlackHooksNewPost(ctx *middleware.Context, form auth.NewSlackHookForm) {
} }
w := &models.Webhook{ w := &models.Webhook{
RepoId: orCtx.RepoId, RepoID: orCtx.RepoId,
Url: form.PayloadUrl, URL: form.PayloadUrl,
ContentType: models.JSON, ContentType: models.JSON,
Secret: "", Secret: "",
HookEvent: &models.HookEvent{ HookEvent: &models.HookEvent{
@ -484,7 +474,7 @@ func SlackHooksNewPost(ctx *middleware.Context, form auth.NewSlackHookForm) {
IsActive: form.Active, IsActive: form.Active,
HookTaskType: models.SLACK, HookTaskType: models.SLACK,
Meta: string(meta), Meta: string(meta),
OrgId: orCtx.OrgId, OrgID: orCtx.OrgId,
} }
if err := w.UpdateEvent(); err != nil { if err := w.UpdateEvent(); err != nil {
ctx.Handle(500, "UpdateEvent", err) ctx.Handle(500, "UpdateEvent", err)
@ -539,7 +529,7 @@ func SlackHooksEditPost(ctx *middleware.Context, form auth.NewSlackHookForm) {
return return
} }
w.Url = form.PayloadUrl w.URL = form.PayloadUrl
w.Meta = string(meta) w.Meta = string(meta)
w.HookEvent = &models.HookEvent{ w.HookEvent = &models.HookEvent{
PushOnly: form.PushOnly, PushOnly: form.PushOnly,
@ -557,6 +547,18 @@ func SlackHooksEditPost(ctx *middleware.Context, form auth.NewSlackHookForm) {
ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, hookId)) ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, hookId))
} }
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",
})
}
type OrgRepoCtx struct { type OrgRepoCtx struct {
OrgId int64 OrgId int64
RepoId int64 RepoId int64
@ -608,7 +610,7 @@ func TriggerHook(ctx *middleware.Context) {
models.HookQueue.AddRepoID(repo.ID) models.HookQueue.AddRepoID(repo.ID)
} }
func SettingsGitHooks(ctx *middleware.Context) { func GitHooks(ctx *middleware.Context) {
ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["Title"] = ctx.Tr("repo.settings")
ctx.Data["PageIsSettingsGitHooks"] = true ctx.Data["PageIsSettingsGitHooks"] = true
@ -622,7 +624,7 @@ func SettingsGitHooks(ctx *middleware.Context) {
ctx.HTML(200, GITHOOKS) ctx.HTML(200, GITHOOKS)
} }
func SettingsGitHooksEdit(ctx *middleware.Context) { func GitHooksEdit(ctx *middleware.Context) {
ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["Title"] = ctx.Tr("repo.settings")
ctx.Data["PageIsSettingsGitHooks"] = true ctx.Data["PageIsSettingsGitHooks"] = true
@ -640,7 +642,7 @@ func SettingsGitHooksEdit(ctx *middleware.Context) {
ctx.HTML(200, GITHOOK_EDIT) ctx.HTML(200, GITHOOK_EDIT)
} }
func SettingsGitHooksEditPost(ctx *middleware.Context) { func GitHooksEditPost(ctx *middleware.Context) {
name := ctx.Params(":name") name := ctx.Params(":name")
hook, err := ctx.Repo.GitRepo.GetHook(name) hook, err := ctx.Repo.GitRepo.GetHook(name)
if err != nil { if err != nil {
@ -659,7 +661,7 @@ func SettingsGitHooksEditPost(ctx *middleware.Context) {
ctx.Redirect(ctx.Repo.RepoLink + "/settings/hooks/git") ctx.Redirect(ctx.Repo.RepoLink + "/settings/hooks/git")
} }
func SettingsDeployKeys(ctx *middleware.Context) { func DeployKeys(ctx *middleware.Context) {
ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["Title"] = ctx.Tr("repo.settings")
ctx.Data["PageIsSettingsKeys"] = true ctx.Data["PageIsSettingsKeys"] = true
@ -673,7 +675,7 @@ func SettingsDeployKeys(ctx *middleware.Context) {
ctx.HTML(200, DEPLOY_KEYS) ctx.HTML(200, DEPLOY_KEYS)
} }
func SettingsDeployKeysPost(ctx *middleware.Context, form auth.AddSSHKeyForm) { func DeployKeysPost(ctx *middleware.Context, form auth.AddSSHKeyForm) {
ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["Title"] = ctx.Tr("repo.settings")
ctx.Data["PageIsSettingsKeys"] = true ctx.Data["PageIsSettingsKeys"] = true

26
templates/org/header.tmpl Normal file
View File

@ -0,0 +1,26 @@
{{with .Org}}
<div class="ui container">
<div class="ui vertically grid head">
<div class="column">
<div class="ui header">
<img class="ui image" src="{{.AvatarLink}}?s=100">
<span class="text thin grey"><a href="{{AppSubUrl}}/org/{{.Name}}">{{.DisplayName}}</a></span>
<div class="ui right">
<div class="ui menu">
<a class="{{if $.PageIsOrgTeams}}active{{end}} item" href="{{$.OrgLink}}/teams">
<i class="octicon octicon-jersey"></i>&nbsp;{{$.i18n.Tr "org.teams"}}
<div class="floating ui black label">{{.NumTeams}}</div>
</a>
<a class="{{if $.PageIsOrgMembers}}active{{end}} item" href="{{$.OrgLink}}/members">
<i class="octicon octicon-organization"></i>&nbsp;{{$.i18n.Tr "org.people"}}
<div class="floating ui black label">{{.NumMembers}}</div>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="ui divider"></div>
{{end}}

View File

@ -1,38 +1,11 @@
{{template "ng/base/head" .}} {{template "base/head" .}}
{{template "ng/base/header" .}} <div class="organization settings webhooks">
{{template "org/base/header" .}} {{template "org/header" .}}
<div id="setting-wrapper" class="main-wrapper"> <div class="ui container">
<div id="org-setting" class="container clear"> <div class="ui grid">
{{template "org/settings/nav" .}} {{template "org/settings/navbar" .}}
<div class="grid-4-5 left"> {{template "repo/settings/hook_list" .}}
<div class="setting-content">
{{template "ng/base/alert" .}}
<div id="setting-content">
<div id="repo-hooks-panel" class="panel panel-radius">
<div class="panel-header">
<a class="btn btn-small btn-black btn-header btn-radius right" href="{{.OrgLink}}/settings/hooks/new">{{.i18n.Tr "repo.settings.add_webhook"}}</a>
<strong>{{.i18n.Tr "repo.settings.hooks"}}</strong>
</div>
<ul class="panel-body setting-list">
<li>{{.i18n.Tr "org.settings.hooks_desc" | Str2html}}</li>
{{range .Webhooks}}
<li>
{{if .IsActive}}
<span class="left text-success"><i class="octicon octicon-check"></i></span>
{{else}}
<span class="left text-grey"><i class="octicon octicon-primitive-dot"></i></span>
{{end}}
<a class="link" href="{{$.OrgLink}}/settings/hooks/{{.Id}}">{{.Url}}</a>
<a href="{{$.OrgLink}}/settings/hooks?remove={{.Id}}" class="text-red right"><i class="fa fa-times"></i></a>
<a href="{{$.OrgLink}}/settings/hooks/{{.Id}}" class="text-blue right"><i class="fa fa-pencil"></i></a>
</li>
{{end}}
</ul>
</div> </div>
</div> </div>
</div> </div>
</div> {{template "base/footer" .}}
</div>
</div>
</div>
{{template "ng/base/footer" .}}

View File

@ -4,9 +4,9 @@
</div> </div>
<div class="panel-body"> <div class="panel-body">
<ul class="menu menu-vertical switching-list grid-1-5 left"> <ul class="menu menu-vertical switching-list grid-1-5 left">
<li {{if .PageIsSettingsOptions}}class="current"{{end}}><a href="{{AppSubUrl}}/org/{{.Org.Name}}/settings">{{.i18n.Tr "org.settings.options"}}</a></li> <li {{if .PageIsSettingsOptions}}class="current"{{end}}><a href="{{.Org.HomeLink}}/settings">{{.i18n.Tr "org.settings.options"}}</a></li>
<li {{if .PageIsSettingsHooks}}class="current"{{end}}><a href="{{AppSubUrl}}/org/{{.Org.Name}}/settings/hooks">{{.i18n.Tr "repo.settings.hooks"}}</a></li> <li {{if .PageIsSettingsHooks}}class="current"{{end}}><a href="{{.Org.HomeLink}}/settings/hooks">{{.i18n.Tr "repo.settings.hooks"}}</a></li>
<li {{if .PageIsSettingsDelete}}class="current"{{end}}><a href="{{AppSubUrl}}/org/{{.Org.Name}}/settings/delete">{{.i18n.Tr "org.settings.delete"}}</a></li> <li {{if .PageIsSettingsDelete}}class="current"{{end}}><a href="{{.Org.HomeLink}}/settings/delete">{{.i18n.Tr "org.settings.delete"}}</a></li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -0,0 +1,14 @@
<div class="four wide column">
<div class="ui vertical menu">
<div class="header item">{{.i18n.Tr "org.settings"}}</div>
<a class="{{if .PageIsSettingsOptions}}active{{end}} item" href="{{.Org.HomeLink}}/settings">
{{.i18n.Tr "org.settings.options"}}
</a>
<a class="{{if .PageIsSettingsHooks}}active{{end}} item" href="{{.Org.HomeLink}}/settings/hooks">
{{.i18n.Tr "repo.settings.hooks"}}
</a>
<a class="{{if .PageIsSettingsDelete}}active{{end}} item" href="{{.Org.HomeLink}}/settings/delete">
{{.i18n.Tr "org.settings.delete"}}
</a>
</div>
</div>

View File

@ -1,6 +1,6 @@
{{with .Repository}} {{with .Repository}}
<div class="ui container"><!-- start container --> <div class="ui container"><!-- start container -->
<div id="repoheader" class="ui vertically padded grid head"><!-- start grid --> <div class="ui vertically padded grid head"><!-- start grid -->
<div class="column"><!-- start column --> <div class="column"><!-- start column -->
<div class="ui black small compact menu floated right count labelled"> <div class="ui black small compact menu floated right count labelled">
<a class="item{{if $.IsRepositoryOwner}} poping up{{end}}"{{if not $.IsRepositoryOwner}} href="{{AppSubUrl}}/repo/fork/{{.ID}}"{{else}} data-content="{{$.i18n.Tr "repo.fork_from_self"}}" data-position="top center" data-variation="tiny"{{end}}> <a class="item{{if $.IsRepositoryOwner}} poping up{{end}}"{{if not $.IsRepositoryOwner}} href="{{AppSubUrl}}/repo/fork/{{.ID}}"{{else}} data-content="{{$.i18n.Tr "repo.fork_from_self"}}" data-position="top center" data-variation="tiny"{{end}}>
@ -36,10 +36,8 @@
{{if .IsFork}}<div class="fork-flag">{{$.i18n.Tr "repo.forked_from"}} <a href="{{.BaseRepo.RepoLink}}">{{SubStr .BaseRepo.RepoLink 1 -1}}</a></div>{{end}} {{if .IsFork}}<div class="fork-flag">{{$.i18n.Tr "repo.forked_from"}} <a href="{{.BaseRepo.RepoLink}}">{{SubStr .BaseRepo.RepoLink 1 -1}}</a></div>{{end}}
</div> </div>
</div> </div>
</div><!-- end column --> </div><!-- end column -->
</div><!-- end grid --> </div><!-- end grid -->
</div><!-- end container --> </div><!-- end container -->
<div class="ui divider"></div> <div class="ui divider"></div>
{{end}} {{end}}

View File

@ -1,5 +1,5 @@
{{template "base/head" .}} {{template "base/head" .}}
<div class="repository settings edit hook"> <div class="repository settings edit githook">
{{template "repo/header" .}} {{template "repo/header" .}}
<div class="ui container"> <div class="ui container">
<div class="ui grid"> <div class="ui grid">

View File

@ -1,5 +1,5 @@
{{template "base/head" .}} {{template "base/head" .}}
<div class="repository settings hooks"> <div class="repository settings githooks">
{{template "repo/header" .}} {{template "repo/header" .}}
<div class="ui container"> <div class="ui container">
<div class="ui grid"> <div class="ui grid">
@ -16,13 +16,9 @@
</div> </div>
{{range .Hooks}} {{range .Hooks}}
<div class="item"> <div class="item">
{{if .IsActive}} <span class="text {{if .IsActive}}green{{else}}grey{{end}}"><i class="octicon octicon-primitive-dot"></i></span>
<span class="text success"><i class="octicon octicon-check"></i></span>
{{else}}
<span class="text grey"><i class="octicon octicon-primitive-dot"></i></span>
{{end}}
<span>{{.Name}}</span> <span>{{.Name}}</span>
<a href="{{$.RepoLink}}/settings/hooks/git/{{.Name}}" class="text blue ui right"><i class="fa fa-pencil"></i></a> <a class="text blue ui right" href="{{$.RepoLink}}/settings/hooks/git/{{.Name}}"><i class="fa fa-pencil"></i></a>
</div> </div>
{{end}} {{end}}
</div> </div>

View File

@ -1,11 +1,11 @@
<div id="gogs" class="{{if (and .PageIsSettingsHooksEdit (not (eq .HookType "Gogs")))}}hidden{{end}}"> <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="{{if .RepoLink}}{{.RepoLink}}{{else if .OrgLink}}{{.OrgLink}}{{end}}/settings/hooks/gogs/{{if .PageIsSettingsHooksNew}}new{{else}}{{.Webhook.Id}}{{end}}" method="post"> <form class="form form-align panel-body repo-setting-form" id="repo-setting-form-gogs" action="{{if .RepoLink}}{{.RepoLink}}{{else if .OrgLink}}{{.OrgLink}}{{end}}/settings/hooks/gogs/{{if .PageIsSettingsHooksNew}}new{{else}}{{.Webhook.ID}}{{end}}" method="post">
{{.CsrfTokenHtml}} {{.CsrfTokenHtml}}
<input type="hidden" name="hook_type" value="gogs"> <input type="hidden" name="hook_type" value="gogs">
<div class="text-center panel-desc">{{.i18n.Tr "repo.settings.add_webhook_desc" "http://gogs.io/docs/features/webhook.html" | Str2html}}</div> <div class="text-center panel-desc">{{.i18n.Tr "repo.settings.add_webhook_desc" "http://gogs.io/docs/features/webhook.html" | Str2html}}</div>
<div class="field"> <div class="field">
<label class="req" for="payload-url">{{.i18n.Tr "repo.settings.payload_url"}}</label> <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 /> <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>
<div class="field"> <div class="field">
<label class="req">{{.i18n.Tr "repo.settings.content_type"}}</label> <label class="req">{{.i18n.Tr "repo.settings.content_type"}}</label>

View File

@ -0,0 +1,52 @@
<div class="twelve wide column content">
{{template "base/alert" .}}
<h4 class="ui top attached header">
{{.i18n.Tr "repo.settings.hooks"}}
<div class="ui right">
<a class="ui blue tiny button" href="{{.BaseLink}}/settings/hooks/new">{{.i18n.Tr "repo.settings.add_webhook"}}</a>
</div>
</h4>
<div class="ui attached table segment">
<div class="ui hook list">
<div class="item">
{{.Description | Str2html}}
</div>
{{range .Webhooks}}
<div class="item">
{{if eq .LastStatus 1}}
<span class="text green"><i class="octicon octicon-check"></i></span>
{{else if eq .LastStatus 2}}
<span class="text red"><i class="octicon octicon-alert"></i></span>
{{else}}
<span class="text grey"><i class="octicon octicon-primitive-dot"></i></span>
{{end}}
<a href="{{$.BaseLink}}/settings/hooks/{{.ID}}">{{.URL}}</a>
<div class="ui right">
<span class="text blue"><a href="{{$.BaseLink}}/settings/hooks/{{.ID}}"><i class="fa fa-pencil"></i></a></span>
<span class="text red"><a class="delete-button" data-url="{{$.Link}}/delete" data-id="{{.ID}}"><i class="fa fa-times"></i></a></span>
</div>
</div>
{{end}}
</div>
</div>
</div>
<div class="ui small basic delete modal">
<div class="ui icon header">
<i class="trash icon"></i>
{{.i18n.Tr "repo.settings.webhook_deletion"}}
</div>
<div class="content">
<p>{{.i18n.Tr "repo.settings.webhook_deletion_desc"}}</p>
</div>
<div class="actions">
<div class="ui red basic inverted cancel button">
<i class="remove icon"></i>
{{.i18n.Tr "modal.no"}}
</div>
<div class="ui green basic inverted ok button">
<i class="checkmark icon"></i>
{{.i18n.Tr "modal.yes"}}
</div>
</div>
</div>

View File

@ -11,5 +11,5 @@
<div class="field"> <div class="field">
<label></label> <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> <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}} {{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> </div>

View File

@ -1,11 +1,11 @@
<div id="slack" class="{{if or .PageIsSettingsHooksNew (and .PageIsSettingsHooksEdit (not (eq .HookType "Slack")))}}hidden{{end}}"> <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="{{if .RepoLink}}{{.RepoLink}}{{else if .OrgLink}}{{.OrgLink}}{{end}}/settings/hooks/slack/{{if .PageIsSettingsHooksNew}}new{{else}}{{.Webhook.Id}}{{end}}" method="post"> <form class="form form-align panel-body repo-setting-form" id="repo-setting-form-slack" action="{{if .RepoLink}}{{.RepoLink}}{{else if .OrgLink}}{{.OrgLink}}{{end}}/settings/hooks/slack/{{if .PageIsSettingsHooksNew}}new{{else}}{{.Webhook.ID}}{{end}}" method="post">
{{.CsrfTokenHtml}} {{.CsrfTokenHtml}}
<input type="hidden" name="hook_type" value="slack"> <input type="hidden" name="hook_type" value="slack">
<div class="text-center panel-desc">{{.i18n.Tr "repo.settings.add_slack_hook_desc" "http://slack.com" | Str2html}}</div> <div class="text-center panel-desc">{{.i18n.Tr "repo.settings.add_slack_hook_desc" "http://slack.com" | Str2html}}</div>
<div class="field"> <div class="field">
<label class="req" for="payload-url">{{.i18n.Tr "repo.settings.payload_url"}}</label> <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 /> <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>
<div class="field"> <div class="field">
<label class="req" for="channel">{{.i18n.Tr "repo.settings.slack_channel"}}</label> <label class="req" for="channel">{{.i18n.Tr "repo.settings.slack_channel"}}</label>

View File

@ -1,39 +1,11 @@
{{template "ng/base/head" .}} {{template "base/head" .}}
{{template "ng/base/header" .}} <div class="repository settings webhooks">
<div id="repo-wrapper"> {{template "repo/header" .}}
{{template "repo/header_old" .}} <div class="ui container">
<div id="setting-wrapper" class="main-wrapper"> <div class="ui grid">
<div id="repo-setting" class="container clear"> {{template "repo/settings/navbar" .}}
{{template "repo/settings/nav" .}} {{template "repo/settings/hook_list" .}}
<div class="grid-4-5 left">
<div class="setting-content">
{{template "ng/base/alert" .}}
<div id="setting-content">
<div id="repo-hooks-panel" class="panel panel-radius">
<div class="panel-header">
<a class="btn btn-small btn-black btn-header btn-radius right" href="{{.RepoLink}}/settings/hooks/new">{{.i18n.Tr "repo.settings.add_webhook"}}</a>
<strong>{{.i18n.Tr "repo.settings.hooks"}}</strong>
</div>
<ul class="panel-body setting-list">
<li>{{.i18n.Tr "repo.settings.hooks_desc" "http://gogs.io/docs/features/webhook.html" | Str2html}}</li>
{{range .Webhooks}}
<li>
{{if .IsActive}}
<span class="left text-success"><i class="octicon octicon-check"></i></span>
{{else}}
<span class="left text-grey"><i class="octicon octicon-primitive-dot"></i></span>
{{end}}
<a class="link" href="{{$.RepoLink}}/settings/hooks/{{.Id}}">{{.Url}}</a>
<a href="{{$.RepoLink}}/settings/hooks?remove={{.Id}}" class="text-red right"><i class="fa fa-times"></i></a>
<a href="{{$.RepoLink}}/settings/hooks/{{.Id}}" class="text-blue right"><i class="fa fa-pencil"></i></a>
</li>
{{end}}
</ul>
</div> </div>
</div> </div>
</div> </div>
</div> {{template "base/footer" .}}
</div>
</div>
</div>
{{template "ng/base/footer" .}}