* Webhooks for repo creation/deletion * add createHookTask * Add handles for GetSlackPayload and GetDiscordPayloadtags/v1.3.0-rc1
@@ -835,8 +835,8 @@ func wikiRemoteURL(remote string) string { | |||
} | |||
// MigrateRepository migrates a existing repository from other project hosting. | |||
func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) { | |||
repo, err := CreateRepository(u, CreateRepoOptions{ | |||
func MigrateRepository(doer, u *User, opts MigrateRepoOptions) (*Repository, error) { | |||
repo, err := CreateRepository(doer, u, CreateRepoOptions{ | |||
Name: opts.Name, | |||
Description: opts.Description, | |||
IsPrivate: opts.IsPrivate, | |||
@@ -1202,7 +1202,7 @@ func IsUsableRepoName(name string) error { | |||
return isUsableName(reservedRepoNames, reservedRepoPatterns, name) | |||
} | |||
func createRepository(e *xorm.Session, u *User, repo *Repository) (err error) { | |||
func createRepository(e *xorm.Session, doer, u *User, repo *Repository) (err error) { | |||
if err = IsUsableRepoName(repo.Name); err != nil { | |||
return err | |||
} | |||
@@ -1249,7 +1249,15 @@ func createRepository(e *xorm.Session, u *User, repo *Repository) (err error) { | |||
return fmt.Errorf("getOwnerTeam: %v", err) | |||
} else if err = t.addRepository(e, repo); err != nil { | |||
return fmt.Errorf("addRepository: %v", err) | |||
} else if err = prepareWebhooks(e, repo, HookEventRepository, &api.RepositoryPayload{ | |||
Action: api.HookRepoCreated, | |||
Repository: repo.APIFormat(AccessModeOwner), | |||
Organization: u.APIFormat(), | |||
Sender: doer.APIFormat(), | |||
}); err != nil { | |||
return fmt.Errorf("prepareWebhooks: %v", err) | |||
} | |||
go HookQueue.Add(repo.ID) | |||
} else { | |||
// Organization automatically called this in addRepository method. | |||
if err = repo.recalculateAccesses(e); err != nil { | |||
@@ -1266,8 +1274,8 @@ func createRepository(e *xorm.Session, u *User, repo *Repository) (err error) { | |||
return nil | |||
} | |||
// CreateRepository creates a repository for given user or organization. | |||
func CreateRepository(u *User, opts CreateRepoOptions) (_ *Repository, err error) { | |||
// CreateRepository creates a repository for the user/organization u. | |||
func CreateRepository(doer, u *User, opts CreateRepoOptions) (_ *Repository, err error) { | |||
if !u.CanCreateRepo() { | |||
return nil, ErrReachLimitOfRepo{u.MaxRepoCreation} | |||
} | |||
@@ -1287,7 +1295,7 @@ func CreateRepository(u *User, opts CreateRepoOptions) (_ *Repository, err error | |||
return nil, err | |||
} | |||
if err = createRepository(sess, u, repo); err != nil { | |||
if err = createRepository(sess, doer, u, repo); err != nil { | |||
return nil, err | |||
} | |||
@@ -1623,7 +1631,7 @@ func UpdateRepositoryUnits(repo *Repository, units []RepoUnit) (err error) { | |||
} | |||
// DeleteRepository deletes a repository for a user or organization. | |||
func DeleteRepository(uid, repoID int64) error { | |||
func DeleteRepository(doer *User, uid, repoID int64) error { | |||
// In case is a organization. | |||
org, err := GetUserByID(uid) | |||
if err != nil { | |||
@@ -1781,6 +1789,18 @@ func DeleteRepository(uid, repoID int64) error { | |||
return fmt.Errorf("Commit: %v", err) | |||
} | |||
if org.IsOrganization() { | |||
if err = PrepareWebhooks(repo, HookEventRepository, &api.RepositoryPayload{ | |||
Action: api.HookRepoDeleted, | |||
Repository: repo.APIFormat(AccessModeOwner), | |||
Organization: org.APIFormat(), | |||
Sender: doer.APIFormat(), | |||
}); err != nil { | |||
return err | |||
} | |||
go HookQueue.Add(repo.ID) | |||
} | |||
return nil | |||
} | |||
@@ -1974,7 +1994,7 @@ func gatherMissingRepoRecords() ([]*Repository, error) { | |||
} | |||
// DeleteMissingRepositories deletes all repository records that lost Git files. | |||
func DeleteMissingRepositories() error { | |||
func DeleteMissingRepositories(doer *User) error { | |||
repos, err := gatherMissingRepoRecords() | |||
if err != nil { | |||
return fmt.Errorf("gatherMissingRepoRecords: %v", err) | |||
@@ -1986,7 +2006,7 @@ func DeleteMissingRepositories() error { | |||
for _, repo := range repos { | |||
log.Trace("Deleting %d/%d...", repo.OwnerID, repo.ID) | |||
if err := DeleteRepository(repo.OwnerID, repo.ID); err != nil { | |||
if err := DeleteRepository(doer, repo.OwnerID, repo.ID); err != nil { | |||
if err2 := CreateRepositoryNotice(fmt.Sprintf("DeleteRepository [%d]: %v", repo.ID, err)); err2 != nil { | |||
return fmt.Errorf("CreateRepositoryNotice: %v", err) | |||
} | |||
@@ -2226,7 +2246,7 @@ func HasForkedRepo(ownerID, repoID int64) (*Repository, bool) { | |||
} | |||
// ForkRepository forks a repository | |||
func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Repository, err error) { | |||
func ForkRepository(doer, u *User, oldRepo *Repository, name, desc string) (_ *Repository, err error) { | |||
forkedRepo, err := oldRepo.GetUserFork(u.ID) | |||
if err != nil { | |||
return nil, err | |||
@@ -2256,7 +2276,7 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Reposit | |||
return nil, err | |||
} | |||
if err = createRepository(sess, u, repo); err != nil { | |||
if err = createRepository(sess, doer, u, repo); err != nil { | |||
return nil, err | |||
} | |||
@@ -118,13 +118,12 @@ func TestGetUserFork(t *testing.T) { | |||
func TestForkRepository(t *testing.T) { | |||
assert.NoError(t, PrepareTestDatabase()) | |||
// User13 has repo 11 forked from repo10 | |||
repo, err := GetRepositoryByID(10) | |||
assert.NoError(t, err) | |||
assert.NotNil(t, repo) | |||
// user 13 has already forked repo10 | |||
user := AssertExistsAndLoadBean(t, &User{ID: 13}).(*User) | |||
repo := AssertExistsAndLoadBean(t, &Repository{ID: 10}).(*Repository) | |||
repo, err = ForkRepository(&User{ID: 13}, repo, "test", "test") | |||
assert.Nil(t, repo) | |||
fork, err := ForkRepository(user, user, repo, "test", "test") | |||
assert.Nil(t, fork) | |||
assert.Error(t, err) | |||
assert.True(t, IsErrRepoAlreadyExist(err)) | |||
} |
@@ -68,6 +68,7 @@ type HookEvents struct { | |||
Create bool `json:"create"` | |||
Push bool `json:"push"` | |||
PullRequest bool `json:"pull_request"` | |||
Repository bool `json:"repository"` | |||
} | |||
// HookEvent represents events that will delivery hook. | |||
@@ -188,6 +189,12 @@ func (w *Webhook) HasPullRequestEvent() bool { | |||
(w.ChooseEvents && w.HookEvents.PullRequest) | |||
} | |||
// HasRepositoryEvent returns if hook enabled repository event. | |||
func (w *Webhook) HasRepositoryEvent() bool { | |||
return w.SendEverything || | |||
(w.ChooseEvents && w.HookEvents.Repository) | |||
} | |||
// EventsArray returns an array of hook events | |||
func (w *Webhook) EventsArray() []string { | |||
events := make([]string, 0, 3) | |||
@@ -246,8 +253,12 @@ func GetWebhookByOrgID(orgID, id int64) (*Webhook, error) { | |||
// GetActiveWebhooksByRepoID returns all active webhooks of repository. | |||
func GetActiveWebhooksByRepoID(repoID int64) ([]*Webhook, error) { | |||
return getActiveWebhooksByRepoID(x, repoID) | |||
} | |||
func getActiveWebhooksByRepoID(e Engine, repoID int64) ([]*Webhook, error) { | |||
webhooks := make([]*Webhook, 0, 5) | |||
return webhooks, x.Where("is_active=?", true). | |||
return webhooks, e.Where("is_active=?", true). | |||
Find(&webhooks, &Webhook{RepoID: repoID}) | |||
} | |||
@@ -259,7 +270,11 @@ func GetWebhooksByRepoID(repoID int64) ([]*Webhook, error) { | |||
// GetActiveWebhooksByOrgID returns all active webhooks for an organization. | |||
func GetActiveWebhooksByOrgID(orgID int64) (ws []*Webhook, err error) { | |||
err = x. | |||
return getActiveWebhooksByOrgID(x, orgID) | |||
} | |||
func getActiveWebhooksByOrgID(e Engine, orgID int64) (ws []*Webhook, err error) { | |||
err = e. | |||
Where("org_id=?", orgID). | |||
And("is_active=?", true). | |||
Find(&ws) | |||
@@ -379,6 +394,7 @@ const ( | |||
HookEventCreate HookEventType = "create" | |||
HookEventPush HookEventType = "push" | |||
HookEventPullRequest HookEventType = "pull_request" | |||
HookEventRepository HookEventType = "repository" | |||
) | |||
// HookRequest represents hook task request information. | |||
@@ -479,13 +495,17 @@ func HookTasks(hookID int64, page int) ([]*HookTask, error) { | |||
// CreateHookTask creates a new hook task, | |||
// it handles conversion from Payload to PayloadContent. | |||
func CreateHookTask(t *HookTask) error { | |||
return createHookTask(x, t) | |||
} | |||
func createHookTask(e Engine, t *HookTask) error { | |||
data, err := t.Payloader.JSONPayload() | |||
if err != nil { | |||
return err | |||
} | |||
t.UUID = gouuid.NewV4().String() | |||
t.PayloadContent = string(data) | |||
_, err = x.Insert(t) | |||
_, err = e.Insert(t) | |||
return err | |||
} | |||
@@ -497,6 +517,10 @@ func UpdateHookTask(t *HookTask) error { | |||
// PrepareWebhook adds special webhook to task queue for given payload. | |||
func PrepareWebhook(w *Webhook, repo *Repository, event HookEventType, p api.Payloader) error { | |||
return prepareWebhook(x, w, repo, event, p) | |||
} | |||
func prepareWebhook(e Engine, w *Webhook, repo *Repository, event HookEventType, p api.Payloader) error { | |||
switch event { | |||
case HookEventCreate: | |||
if !w.HasCreateEvent() { | |||
@@ -510,6 +534,10 @@ func PrepareWebhook(w *Webhook, repo *Repository, event HookEventType, p api.Pay | |||
if !w.HasPullRequestEvent() { | |||
return nil | |||
} | |||
case HookEventRepository: | |||
if !w.HasRepositoryEvent() { | |||
return nil | |||
} | |||
} | |||
var payloader api.Payloader | |||
@@ -531,7 +559,7 @@ func PrepareWebhook(w *Webhook, repo *Repository, event HookEventType, p api.Pay | |||
payloader = p | |||
} | |||
if err = CreateHookTask(&HookTask{ | |||
if err = createHookTask(e, &HookTask{ | |||
RepoID: repo.ID, | |||
HookID: w.ID, | |||
Type: w.HookTaskType, | |||
@@ -548,15 +576,19 @@ func PrepareWebhook(w *Webhook, repo *Repository, event HookEventType, p api.Pay | |||
// PrepareWebhooks adds new webhooks to task queue for given payload. | |||
func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) error { | |||
ws, err := GetActiveWebhooksByRepoID(repo.ID) | |||
return prepareWebhooks(x, repo, event, p) | |||
} | |||
func prepareWebhooks(e Engine, repo *Repository, event HookEventType, p api.Payloader) error { | |||
ws, err := getActiveWebhooksByRepoID(e, repo.ID) | |||
if err != nil { | |||
return fmt.Errorf("GetActiveWebhooksByRepoID: %v", err) | |||
} | |||
// check if repo belongs to org and append additional webhooks | |||
if repo.MustOwner().IsOrganization() { | |||
if repo.mustOwner(e).IsOrganization() { | |||
// get hooks for org | |||
orgHooks, err := GetActiveWebhooksByOrgID(repo.OwnerID) | |||
orgHooks, err := getActiveWebhooksByOrgID(e, repo.OwnerID) | |||
if err != nil { | |||
return fmt.Errorf("GetActiveWebhooksByOrgID: %v", err) | |||
} | |||
@@ -568,7 +600,7 @@ func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) err | |||
} | |||
for _, w := range ws { | |||
if err = PrepareWebhook(w, repo, event, p); err != nil { | |||
if err = prepareWebhook(e, w, repo, event, p); err != nil { | |||
return err | |||
} | |||
} |
@@ -228,6 +228,37 @@ func getDiscordPullRequestPayload(p *api.PullRequestPayload, meta *DiscordMeta) | |||
}, nil | |||
} | |||
func getDiscordRepositoryPayload(p *api.RepositoryPayload, meta *DiscordMeta) (*DiscordPayload, error) { | |||
var title, url string | |||
var color int | |||
switch p.Action { | |||
case api.HookRepoCreated: | |||
title = fmt.Sprintf("[%s] Repository created", p.Repository.FullName) | |||
url = p.Repository.HTMLURL | |||
color = successColor | |||
case api.HookRepoDeleted: | |||
title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName) | |||
color = warnColor | |||
} | |||
return &DiscordPayload{ | |||
Username: meta.Username, | |||
AvatarURL: meta.IconURL, | |||
Embeds: []DiscordEmbed{ | |||
{ | |||
Title: title, | |||
URL: url, | |||
Color: color, | |||
Author: DiscordEmbedAuthor{ | |||
Name: p.Sender.UserName, | |||
URL: setting.AppURL + p.Sender.UserName, | |||
IconURL: p.Sender.AvatarURL, | |||
}, | |||
}, | |||
}, | |||
}, nil | |||
} | |||
// GetDiscordPayload converts a discord webhook into a DiscordPayload | |||
func GetDiscordPayload(p api.Payloader, event HookEventType, meta string) (*DiscordPayload, error) { | |||
s := new(DiscordPayload) | |||
@@ -244,6 +275,8 @@ func GetDiscordPayload(p api.Payloader, event HookEventType, meta string) (*Disc | |||
return getDiscordPushPayload(p.(*api.PushPayload), discord) | |||
case HookEventPullRequest: | |||
return getDiscordPullRequestPayload(p.(*api.PullRequestPayload), discord) | |||
case HookEventRepository: | |||
return getDiscordRepositoryPayload(p.(*api.RepositoryPayload), discord) | |||
} | |||
return s, nil |
@@ -189,6 +189,30 @@ func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*S | |||
}, nil | |||
} | |||
func getSlackRepositoryPayload(p *api.RepositoryPayload, slack *SlackMeta) (*SlackPayload, error) { | |||
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName) | |||
var text, title, attachmentText string | |||
switch p.Action { | |||
case api.HookRepoCreated: | |||
text = fmt.Sprintf("[%s] Repository created by %s", p.Repository.FullName, senderLink) | |||
title = p.Repository.HTMLURL | |||
case api.HookRepoDeleted: | |||
text = fmt.Sprintf("[%s] Repository deleted by %s", p.Repository.FullName, senderLink) | |||
} | |||
return &SlackPayload{ | |||
Channel: slack.Channel, | |||
Text: text, | |||
Username: slack.Username, | |||
IconURL: slack.IconURL, | |||
Attachments: []SlackAttachment{{ | |||
Color: slack.Color, | |||
Title: title, | |||
Text: attachmentText, | |||
}}, | |||
}, nil | |||
} | |||
// GetSlackPayload converts a slack webhook into a SlackPayload | |||
func GetSlackPayload(p api.Payloader, event HookEventType, meta string) (*SlackPayload, error) { | |||
s := new(SlackPayload) | |||
@@ -205,6 +229,8 @@ func GetSlackPayload(p api.Payloader, event HookEventType, meta string) (*SlackP | |||
return getSlackPushPayload(p.(*api.PushPayload), slack) | |||
case HookEventPullRequest: | |||
return getSlackPullRequestPayload(p.(*api.PullRequestPayload), slack) | |||
case HookEventRepository: | |||
return getSlackRepositoryPayload(p.(*api.RepositoryPayload), slack) | |||
} | |||
return s, nil |
@@ -124,6 +124,7 @@ type WebhookForm struct { | |||
Create bool | |||
Push bool | |||
PullRequest bool | |||
Repository bool | |||
Active bool | |||
} | |||
@@ -892,6 +892,8 @@ settings.event_pull_request = Pull Request | |||
settings.event_pull_request_desc = Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized. | |||
settings.event_push = Push | |||
settings.event_push_desc = Git push to a repository | |||
settings.event_repository = Repository | |||
settings.event_repository_desc = Repository created or deleted | |||
settings.active = Active | |||
settings.active_helper = Information about the event which triggered the hook will be sent as well. | |||
settings.add_hook_success = New webhook has been added. |
@@ -145,7 +145,7 @@ func Dashboard(ctx *context.Context) { | |||
err = models.DeleteRepositoryArchives() | |||
case cleanMissingRepos: | |||
success = ctx.Tr("admin.dashboard.delete_missing_repos_success") | |||
err = models.DeleteMissingRepositories() | |||
err = models.DeleteMissingRepositories(ctx.User) | |||
case gitGCRepos: | |||
success = ctx.Tr("admin.dashboard.git_gc_repos_success") | |||
err = models.GitGcRepos() |
@@ -39,7 +39,7 @@ func DeleteRepo(ctx *context.Context) { | |||
return | |||
} | |||
if err := models.DeleteRepository(repo.MustOwner().ID, repo.ID); err != nil { | |||
if err := models.DeleteRepository(ctx.User, repo.MustOwner().ID, repo.ID); err != nil { | |||
ctx.Handle(500, "DeleteRepository", err) | |||
return | |||
} |
@@ -73,7 +73,7 @@ func CreateFork(ctx *context.APIContext, form api.CreateForkOption) { | |||
} | |||
forker = org | |||
} | |||
fork, err := models.ForkRepository(forker, repo, repo.Name, repo.Description) | |||
fork, err := models.ForkRepository(ctx.User, forker, repo, repo.Name, repo.Description) | |||
if err != nil { | |||
ctx.Error(500, "ForkRepository", err) | |||
return |
@@ -105,7 +105,7 @@ func Search(ctx *context.APIContext) { | |||
// CreateUserRepo create a repository for a user | |||
func CreateUserRepo(ctx *context.APIContext, owner *models.User, opt api.CreateRepoOption) { | |||
repo, err := models.CreateRepository(owner, models.CreateRepoOptions{ | |||
repo, err := models.CreateRepository(ctx.User, owner, models.CreateRepoOptions{ | |||
Name: opt.Name, | |||
Description: opt.Description, | |||
Gitignores: opt.Gitignores, | |||
@@ -121,7 +121,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *models.User, opt api.CreateR | |||
ctx.Error(422, "", err) | |||
} else { | |||
if repo != nil { | |||
if err = models.DeleteRepository(ctx.User.ID, repo.ID); err != nil { | |||
if err = models.DeleteRepository(ctx.User, ctx.User.ID, repo.ID); err != nil { | |||
log.Error(4, "DeleteRepository: %v", err) | |||
} | |||
} | |||
@@ -254,7 +254,7 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) { | |||
return | |||
} | |||
repo, err := models.MigrateRepository(ctxUser, models.MigrateRepoOptions{ | |||
repo, err := models.MigrateRepository(ctx.User, ctxUser, models.MigrateRepoOptions{ | |||
Name: form.RepoName, | |||
Description: form.Description, | |||
IsPrivate: form.Private || setting.Repository.ForcePrivate, | |||
@@ -263,7 +263,7 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) { | |||
}) | |||
if err != nil { | |||
if repo != nil { | |||
if errDelete := models.DeleteRepository(ctxUser.ID, repo.ID); errDelete != nil { | |||
if errDelete := models.DeleteRepository(ctx.User, ctxUser.ID, repo.ID); errDelete != nil { | |||
log.Error(4, "DeleteRepository: %v", errDelete) | |||
} | |||
} | |||
@@ -345,7 +345,7 @@ func Delete(ctx *context.APIContext) { | |||
return | |||
} | |||
if err := models.DeleteRepository(owner.ID, repo.ID); err != nil { | |||
if err := models.DeleteRepository(ctx.User, owner.ID, repo.ID); err != nil { | |||
ctx.Error(500, "DeleteRepository", err) | |||
return | |||
} |
@@ -125,7 +125,7 @@ func ForkPost(ctx *context.Context, form auth.CreateRepoForm) { | |||
} | |||
} | |||
repo, err := models.ForkRepository(ctxUser, forkRepo, form.RepoName, form.Description) | |||
repo, err := models.ForkRepository(ctx.User, ctxUser, forkRepo, form.RepoName, form.Description) | |||
if err != nil { | |||
ctx.Data["Err_RepoName"] = true | |||
switch { |
@@ -127,7 +127,7 @@ func CreatePost(ctx *context.Context, form auth.CreateRepoForm) { | |||
return | |||
} | |||
repo, err := models.CreateRepository(ctxUser, models.CreateRepoOptions{ | |||
repo, err := models.CreateRepository(ctx.User, ctxUser, models.CreateRepoOptions{ | |||
Name: form.RepoName, | |||
Description: form.Description, | |||
Gitignores: form.Gitignores, | |||
@@ -143,7 +143,7 @@ func CreatePost(ctx *context.Context, form auth.CreateRepoForm) { | |||
} | |||
if repo != nil { | |||
if errDelete := models.DeleteRepository(ctxUser.ID, repo.ID); errDelete != nil { | |||
if errDelete := models.DeleteRepository(ctx.User, ctxUser.ID, repo.ID); errDelete != nil { | |||
log.Error(4, "DeleteRepository: %v", errDelete) | |||
} | |||
} | |||
@@ -204,7 +204,7 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) { | |||
return | |||
} | |||
repo, err := models.MigrateRepository(ctxUser, models.MigrateRepoOptions{ | |||
repo, err := models.MigrateRepository(ctx.User, ctxUser, models.MigrateRepoOptions{ | |||
Name: form.RepoName, | |||
Description: form.Description, | |||
IsPrivate: form.Private || setting.Repository.ForcePrivate, | |||
@@ -218,7 +218,7 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) { | |||
} | |||
if repo != nil { | |||
if errDelete := models.DeleteRepository(ctxUser.ID, repo.ID); errDelete != nil { | |||
if errDelete := models.DeleteRepository(ctx.User, ctxUser.ID, repo.ID); errDelete != nil { | |||
log.Error(4, "DeleteRepository: %v", errDelete) | |||
} | |||
} |
@@ -314,7 +314,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { | |||
} | |||
} | |||
if err := models.DeleteRepository(ctx.Repo.Owner.ID, repo.ID); err != nil { | |||
if err := models.DeleteRepository(ctx.User, ctx.Repo.Owner.ID, repo.ID); err != nil { | |||
ctx.Handle(500, "DeleteRepository", err) | |||
return | |||
} |
@@ -121,6 +121,7 @@ func ParseHookEvent(form auth.WebhookForm) *models.HookEvent { | |||
Create: form.Create, | |||
Push: form.Push, | |||
PullRequest: form.PullRequest, | |||
Repository: form.Repository, | |||
}, | |||
} | |||
} |
@@ -52,6 +52,16 @@ | |||
</div> | |||
</div> | |||
</div> | |||
<!-- Repository --> | |||
<div class="seven wide column"> | |||
<div class="field"> | |||
<div class="ui checkbox"> | |||
<input class="hidden" name="repository" type="checkbox" tabindex="0" {{if .Webhook.Repository}}checked{{end}}> | |||
<label>{{.i18n.Tr "repo.settings.event_repository"}}</label> | |||
<span class="help">{{.i18n.Tr "repo.settings.event_repository_desc"}}</span> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||