Browse Source

Webhooks for repo creation/deletion (#1663)

* Webhooks for repo creation/deletion

* add createHookTask

* Add handles for GetSlackPayload and GetDiscordPayload
Ethan Koenig 2 years ago
parent
commit
b689bb6180

+ 31 - 11
models/repo.go

@@ -835,8 +835,8 @@ func wikiRemoteURL(remote string) string {
835 835
 }
836 836
 
837 837
 // MigrateRepository migrates a existing repository from other project hosting.
838
-func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) {
839
-	repo, err := CreateRepository(u, CreateRepoOptions{
838
+func MigrateRepository(doer, u *User, opts MigrateRepoOptions) (*Repository, error) {
839
+	repo, err := CreateRepository(doer, u, CreateRepoOptions{
840 840
 		Name:        opts.Name,
841 841
 		Description: opts.Description,
842 842
 		IsPrivate:   opts.IsPrivate,
@@ -1202,7 +1202,7 @@ func IsUsableRepoName(name string) error {
1202 1202
 	return isUsableName(reservedRepoNames, reservedRepoPatterns, name)
1203 1203
 }
1204 1204
 
1205
-func createRepository(e *xorm.Session, u *User, repo *Repository) (err error) {
1205
+func createRepository(e *xorm.Session, doer, u *User, repo *Repository) (err error) {
1206 1206
 	if err = IsUsableRepoName(repo.Name); err != nil {
1207 1207
 		return err
1208 1208
 	}
@@ -1249,7 +1249,15 @@ func createRepository(e *xorm.Session, u *User, repo *Repository) (err error) {
1249 1249
 			return fmt.Errorf("getOwnerTeam: %v", err)
1250 1250
 		} else if err = t.addRepository(e, repo); err != nil {
1251 1251
 			return fmt.Errorf("addRepository: %v", err)
1252
+		} else if err = prepareWebhooks(e, repo, HookEventRepository, &api.RepositoryPayload{
1253
+			Action:       api.HookRepoCreated,
1254
+			Repository:   repo.APIFormat(AccessModeOwner),
1255
+			Organization: u.APIFormat(),
1256
+			Sender:       doer.APIFormat(),
1257
+		}); err != nil {
1258
+			return fmt.Errorf("prepareWebhooks: %v", err)
1252 1259
 		}
1260
+		go HookQueue.Add(repo.ID)
1253 1261
 	} else {
1254 1262
 		// Organization automatically called this in addRepository method.
1255 1263
 		if err = repo.recalculateAccesses(e); err != nil {
@@ -1266,8 +1274,8 @@ func createRepository(e *xorm.Session, u *User, repo *Repository) (err error) {
1266 1274
 	return nil
1267 1275
 }
1268 1276
 
1269
-// CreateRepository creates a repository for given user or organization.
1270
-func CreateRepository(u *User, opts CreateRepoOptions) (_ *Repository, err error) {
1277
+// CreateRepository creates a repository for the user/organization u.
1278
+func CreateRepository(doer, u *User, opts CreateRepoOptions) (_ *Repository, err error) {
1271 1279
 	if !u.CanCreateRepo() {
1272 1280
 		return nil, ErrReachLimitOfRepo{u.MaxRepoCreation}
1273 1281
 	}
@@ -1287,7 +1295,7 @@ func CreateRepository(u *User, opts CreateRepoOptions) (_ *Repository, err error
1287 1295
 		return nil, err
1288 1296
 	}
1289 1297
 
1290
-	if err = createRepository(sess, u, repo); err != nil {
1298
+	if err = createRepository(sess, doer, u, repo); err != nil {
1291 1299
 		return nil, err
1292 1300
 	}
1293 1301
 
@@ -1623,7 +1631,7 @@ func UpdateRepositoryUnits(repo *Repository, units []RepoUnit) (err error) {
1623 1631
 }
1624 1632
 
1625 1633
 // DeleteRepository deletes a repository for a user or organization.
1626
-func DeleteRepository(uid, repoID int64) error {
1634
+func DeleteRepository(doer *User, uid, repoID int64) error {
1627 1635
 	// In case is a organization.
1628 1636
 	org, err := GetUserByID(uid)
1629 1637
 	if err != nil {
@@ -1781,6 +1789,18 @@ func DeleteRepository(uid, repoID int64) error {
1781 1789
 		return fmt.Errorf("Commit: %v", err)
1782 1790
 	}
1783 1791
 
1792
+	if org.IsOrganization() {
1793
+		if err = PrepareWebhooks(repo, HookEventRepository, &api.RepositoryPayload{
1794
+			Action:       api.HookRepoDeleted,
1795
+			Repository:   repo.APIFormat(AccessModeOwner),
1796
+			Organization: org.APIFormat(),
1797
+			Sender:       doer.APIFormat(),
1798
+		}); err != nil {
1799
+			return err
1800
+		}
1801
+		go HookQueue.Add(repo.ID)
1802
+	}
1803
+
1784 1804
 	return nil
1785 1805
 }
1786 1806
 
@@ -1974,7 +1994,7 @@ func gatherMissingRepoRecords() ([]*Repository, error) {
1974 1994
 }
1975 1995
 
1976 1996
 // DeleteMissingRepositories deletes all repository records that lost Git files.
1977
-func DeleteMissingRepositories() error {
1997
+func DeleteMissingRepositories(doer *User) error {
1978 1998
 	repos, err := gatherMissingRepoRecords()
1979 1999
 	if err != nil {
1980 2000
 		return fmt.Errorf("gatherMissingRepoRecords: %v", err)
@@ -1986,7 +2006,7 @@ func DeleteMissingRepositories() error {
1986 2006
 
1987 2007
 	for _, repo := range repos {
1988 2008
 		log.Trace("Deleting %d/%d...", repo.OwnerID, repo.ID)
1989
-		if err := DeleteRepository(repo.OwnerID, repo.ID); err != nil {
2009
+		if err := DeleteRepository(doer, repo.OwnerID, repo.ID); err != nil {
1990 2010
 			if err2 := CreateRepositoryNotice(fmt.Sprintf("DeleteRepository [%d]: %v", repo.ID, err)); err2 != nil {
1991 2011
 				return fmt.Errorf("CreateRepositoryNotice: %v", err)
1992 2012
 			}
@@ -2226,7 +2246,7 @@ func HasForkedRepo(ownerID, repoID int64) (*Repository, bool) {
2226 2246
 }
2227 2247
 
2228 2248
 // ForkRepository forks a repository
2229
-func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Repository, err error) {
2249
+func ForkRepository(doer, u *User, oldRepo *Repository, name, desc string) (_ *Repository, err error) {
2230 2250
 	forkedRepo, err := oldRepo.GetUserFork(u.ID)
2231 2251
 	if err != nil {
2232 2252
 		return nil, err
@@ -2256,7 +2276,7 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Reposit
2256 2276
 		return nil, err
2257 2277
 	}
2258 2278
 
2259
-	if err = createRepository(sess, u, repo); err != nil {
2279
+	if err = createRepository(sess, doer, u, repo); err != nil {
2260 2280
 		return nil, err
2261 2281
 	}
2262 2282
 

+ 5 - 6
models/repo_test.go

@@ -118,13 +118,12 @@ func TestGetUserFork(t *testing.T) {
118 118
 func TestForkRepository(t *testing.T) {
119 119
 	assert.NoError(t, PrepareTestDatabase())
120 120
 
121
-	// User13 has repo 11 forked from repo10
122
-	repo, err := GetRepositoryByID(10)
123
-	assert.NoError(t, err)
124
-	assert.NotNil(t, repo)
121
+	// user 13 has already forked repo10
122
+	user := AssertExistsAndLoadBean(t, &User{ID: 13}).(*User)
123
+	repo := AssertExistsAndLoadBean(t, &Repository{ID: 10}).(*Repository)
125 124
 
126
-	repo, err = ForkRepository(&User{ID: 13}, repo, "test", "test")
127
-	assert.Nil(t, repo)
125
+	fork, err := ForkRepository(user, user, repo, "test", "test")
126
+	assert.Nil(t, fork)
128 127
 	assert.Error(t, err)
129 128
 	assert.True(t, IsErrRepoAlreadyExist(err))
130 129
 }

+ 40 - 8
models/webhook.go

@@ -68,6 +68,7 @@ type HookEvents struct {
68 68
 	Create      bool `json:"create"`
69 69
 	Push        bool `json:"push"`
70 70
 	PullRequest bool `json:"pull_request"`
71
+	Repository  bool `json:"repository"`
71 72
 }
72 73
 
73 74
 // HookEvent represents events that will delivery hook.
@@ -188,6 +189,12 @@ func (w *Webhook) HasPullRequestEvent() bool {
188 189
 		(w.ChooseEvents && w.HookEvents.PullRequest)
189 190
 }
190 191
 
192
+// HasRepositoryEvent returns if hook enabled repository event.
193
+func (w *Webhook) HasRepositoryEvent() bool {
194
+	return w.SendEverything ||
195
+		(w.ChooseEvents && w.HookEvents.Repository)
196
+}
197
+
191 198
 // EventsArray returns an array of hook events
192 199
 func (w *Webhook) EventsArray() []string {
193 200
 	events := make([]string, 0, 3)
@@ -246,8 +253,12 @@ func GetWebhookByOrgID(orgID, id int64) (*Webhook, error) {
246 253
 
247 254
 // GetActiveWebhooksByRepoID returns all active webhooks of repository.
248 255
 func GetActiveWebhooksByRepoID(repoID int64) ([]*Webhook, error) {
256
+	return getActiveWebhooksByRepoID(x, repoID)
257
+}
258
+
259
+func getActiveWebhooksByRepoID(e Engine, repoID int64) ([]*Webhook, error) {
249 260
 	webhooks := make([]*Webhook, 0, 5)
250
-	return webhooks, x.Where("is_active=?", true).
261
+	return webhooks, e.Where("is_active=?", true).
251 262
 		Find(&webhooks, &Webhook{RepoID: repoID})
252 263
 }
253 264
 
@@ -259,7 +270,11 @@ func GetWebhooksByRepoID(repoID int64) ([]*Webhook, error) {
259 270
 
260 271
 // GetActiveWebhooksByOrgID returns all active webhooks for an organization.
261 272
 func GetActiveWebhooksByOrgID(orgID int64) (ws []*Webhook, err error) {
262
-	err = x.
273
+	return getActiveWebhooksByOrgID(x, orgID)
274
+}
275
+
276
+func getActiveWebhooksByOrgID(e Engine, orgID int64) (ws []*Webhook, err error) {
277
+	err = e.
263 278
 		Where("org_id=?", orgID).
264 279
 		And("is_active=?", true).
265 280
 		Find(&ws)
@@ -379,6 +394,7 @@ const (
379 394
 	HookEventCreate      HookEventType = "create"
380 395
 	HookEventPush        HookEventType = "push"
381 396
 	HookEventPullRequest HookEventType = "pull_request"
397
+	HookEventRepository  HookEventType = "repository"
382 398
 )
383 399
 
384 400
 // HookRequest represents hook task request information.
@@ -479,13 +495,17 @@ func HookTasks(hookID int64, page int) ([]*HookTask, error) {
479 495
 // CreateHookTask creates a new hook task,
480 496
 // it handles conversion from Payload to PayloadContent.
481 497
 func CreateHookTask(t *HookTask) error {
498
+	return createHookTask(x, t)
499
+}
500
+
501
+func createHookTask(e Engine, t *HookTask) error {
482 502
 	data, err := t.Payloader.JSONPayload()
483 503
 	if err != nil {
484 504
 		return err
485 505
 	}
486 506
 	t.UUID = gouuid.NewV4().String()
487 507
 	t.PayloadContent = string(data)
488
-	_, err = x.Insert(t)
508
+	_, err = e.Insert(t)
489 509
 	return err
490 510
 }
491 511
 
@@ -497,6 +517,10 @@ func UpdateHookTask(t *HookTask) error {
497 517
 
498 518
 // PrepareWebhook adds special webhook to task queue for given payload.
499 519
 func PrepareWebhook(w *Webhook, repo *Repository, event HookEventType, p api.Payloader) error {
520
+	return prepareWebhook(x, w, repo, event, p)
521
+}
522
+
523
+func prepareWebhook(e Engine, w *Webhook, repo *Repository, event HookEventType, p api.Payloader) error {
500 524
 	switch event {
501 525
 	case HookEventCreate:
502 526
 		if !w.HasCreateEvent() {
@@ -510,6 +534,10 @@ func PrepareWebhook(w *Webhook, repo *Repository, event HookEventType, p api.Pay
510 534
 		if !w.HasPullRequestEvent() {
511 535
 			return nil
512 536
 		}
537
+	case HookEventRepository:
538
+		if !w.HasRepositoryEvent() {
539
+			return nil
540
+		}
513 541
 	}
514 542
 
515 543
 	var payloader api.Payloader
@@ -531,7 +559,7 @@ func PrepareWebhook(w *Webhook, repo *Repository, event HookEventType, p api.Pay
531 559
 		payloader = p
532 560
 	}
533 561
 
534
-	if err = CreateHookTask(&HookTask{
562
+	if err = createHookTask(e, &HookTask{
535 563
 		RepoID:      repo.ID,
536 564
 		HookID:      w.ID,
537 565
 		Type:        w.HookTaskType,
@@ -548,15 +576,19 @@ func PrepareWebhook(w *Webhook, repo *Repository, event HookEventType, p api.Pay
548 576
 
549 577
 // PrepareWebhooks adds new webhooks to task queue for given payload.
550 578
 func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) error {
551
-	ws, err := GetActiveWebhooksByRepoID(repo.ID)
579
+	return prepareWebhooks(x, repo, event, p)
580
+}
581
+
582
+func prepareWebhooks(e Engine, repo *Repository, event HookEventType, p api.Payloader) error {
583
+	ws, err := getActiveWebhooksByRepoID(e, repo.ID)
552 584
 	if err != nil {
553 585
 		return fmt.Errorf("GetActiveWebhooksByRepoID: %v", err)
554 586
 	}
555 587
 
556 588
 	// check if repo belongs to org and append additional webhooks
557
-	if repo.MustOwner().IsOrganization() {
589
+	if repo.mustOwner(e).IsOrganization() {
558 590
 		// get hooks for org
559
-		orgHooks, err := GetActiveWebhooksByOrgID(repo.OwnerID)
591
+		orgHooks, err := getActiveWebhooksByOrgID(e, repo.OwnerID)
560 592
 		if err != nil {
561 593
 			return fmt.Errorf("GetActiveWebhooksByOrgID: %v", err)
562 594
 		}
@@ -568,7 +600,7 @@ func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) err
568 600
 	}
569 601
 
570 602
 	for _, w := range ws {
571
-		if err = PrepareWebhook(w, repo, event, p); err != nil {
603
+		if err = prepareWebhook(e, w, repo, event, p); err != nil {
572 604
 			return err
573 605
 		}
574 606
 	}

+ 33 - 0
models/webhook_discord.go

@@ -228,6 +228,37 @@ func getDiscordPullRequestPayload(p *api.PullRequestPayload, meta *DiscordMeta)
228 228
 	}, nil
229 229
 }
230 230
 
231
+func getDiscordRepositoryPayload(p *api.RepositoryPayload, meta *DiscordMeta) (*DiscordPayload, error) {
232
+	var title, url string
233
+	var color int
234
+	switch p.Action {
235
+	case api.HookRepoCreated:
236
+		title = fmt.Sprintf("[%s] Repository created", p.Repository.FullName)
237
+		url = p.Repository.HTMLURL
238
+		color = successColor
239
+	case api.HookRepoDeleted:
240
+		title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName)
241
+		color = warnColor
242
+	}
243
+
244
+	return &DiscordPayload{
245
+		Username:  meta.Username,
246
+		AvatarURL: meta.IconURL,
247
+		Embeds: []DiscordEmbed{
248
+			{
249
+				Title: title,
250
+				URL:   url,
251
+				Color: color,
252
+				Author: DiscordEmbedAuthor{
253
+					Name:    p.Sender.UserName,
254
+					URL:     setting.AppURL + p.Sender.UserName,
255
+					IconURL: p.Sender.AvatarURL,
256
+				},
257
+			},
258
+		},
259
+	}, nil
260
+}
261
+
231 262
 // GetDiscordPayload converts a discord webhook into a DiscordPayload
232 263
 func GetDiscordPayload(p api.Payloader, event HookEventType, meta string) (*DiscordPayload, error) {
233 264
 	s := new(DiscordPayload)
@@ -244,6 +275,8 @@ func GetDiscordPayload(p api.Payloader, event HookEventType, meta string) (*Disc
244 275
 		return getDiscordPushPayload(p.(*api.PushPayload), discord)
245 276
 	case HookEventPullRequest:
246 277
 		return getDiscordPullRequestPayload(p.(*api.PullRequestPayload), discord)
278
+	case HookEventRepository:
279
+		return getDiscordRepositoryPayload(p.(*api.RepositoryPayload), discord)
247 280
 	}
248 281
 
249 282
 	return s, nil

+ 26 - 0
models/webhook_slack.go

@@ -189,6 +189,30 @@ func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*S
189 189
 	}, nil
190 190
 }
191 191
 
192
+func getSlackRepositoryPayload(p *api.RepositoryPayload, slack *SlackMeta) (*SlackPayload, error) {
193
+	senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
194
+	var text, title, attachmentText string
195
+	switch p.Action {
196
+	case api.HookRepoCreated:
197
+		text = fmt.Sprintf("[%s] Repository created by %s", p.Repository.FullName, senderLink)
198
+		title = p.Repository.HTMLURL
199
+	case api.HookRepoDeleted:
200
+		text = fmt.Sprintf("[%s] Repository deleted by %s", p.Repository.FullName, senderLink)
201
+	}
202
+
203
+	return &SlackPayload{
204
+		Channel:  slack.Channel,
205
+		Text:     text,
206
+		Username: slack.Username,
207
+		IconURL:  slack.IconURL,
208
+		Attachments: []SlackAttachment{{
209
+			Color: slack.Color,
210
+			Title: title,
211
+			Text:  attachmentText,
212
+		}},
213
+	}, nil
214
+}
215
+
192 216
 // GetSlackPayload converts a slack webhook into a SlackPayload
193 217
 func GetSlackPayload(p api.Payloader, event HookEventType, meta string) (*SlackPayload, error) {
194 218
 	s := new(SlackPayload)
@@ -205,6 +229,8 @@ func GetSlackPayload(p api.Payloader, event HookEventType, meta string) (*SlackP
205 229
 		return getSlackPushPayload(p.(*api.PushPayload), slack)
206 230
 	case HookEventPullRequest:
207 231
 		return getSlackPullRequestPayload(p.(*api.PullRequestPayload), slack)
232
+	case HookEventRepository:
233
+		return getSlackRepositoryPayload(p.(*api.RepositoryPayload), slack)
208 234
 	}
209 235
 
210 236
 	return s, nil

+ 1 - 0
modules/auth/repo_form.go

@@ -124,6 +124,7 @@ type WebhookForm struct {
124 124
 	Create      bool
125 125
 	Push        bool
126 126
 	PullRequest bool
127
+	Repository  bool
127 128
 	Active      bool
128 129
 }
129 130
 

+ 2 - 0
options/locale/locale_en-US.ini

@@ -892,6 +892,8 @@ settings.event_pull_request = Pull Request
892 892
 settings.event_pull_request_desc = Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized.
893 893
 settings.event_push = Push
894 894
 settings.event_push_desc = Git push to a repository
895
+settings.event_repository = Repository
896
+settings.event_repository_desc = Repository created or deleted
895 897
 settings.active = Active
896 898
 settings.active_helper = Information about the event which triggered the hook will be sent as well.
897 899
 settings.add_hook_success = New webhook has been added.

+ 1 - 1
routers/admin/admin.go

@@ -145,7 +145,7 @@ func Dashboard(ctx *context.Context) {
145 145
 			err = models.DeleteRepositoryArchives()
146 146
 		case cleanMissingRepos:
147 147
 			success = ctx.Tr("admin.dashboard.delete_missing_repos_success")
148
-			err = models.DeleteMissingRepositories()
148
+			err = models.DeleteMissingRepositories(ctx.User)
149 149
 		case gitGCRepos:
150 150
 			success = ctx.Tr("admin.dashboard.git_gc_repos_success")
151 151
 			err = models.GitGcRepos()

+ 1 - 1
routers/admin/repos.go

@@ -39,7 +39,7 @@ func DeleteRepo(ctx *context.Context) {
39 39
 		return
40 40
 	}
41 41
 
42
-	if err := models.DeleteRepository(repo.MustOwner().ID, repo.ID); err != nil {
42
+	if err := models.DeleteRepository(ctx.User, repo.MustOwner().ID, repo.ID); err != nil {
43 43
 		ctx.Handle(500, "DeleteRepository", err)
44 44
 		return
45 45
 	}

+ 1 - 1
routers/api/v1/repo/fork.go

@@ -73,7 +73,7 @@ func CreateFork(ctx *context.APIContext, form api.CreateForkOption) {
73 73
 		}
74 74
 		forker = org
75 75
 	}
76
-	fork, err := models.ForkRepository(forker, repo, repo.Name, repo.Description)
76
+	fork, err := models.ForkRepository(ctx.User, forker, repo, repo.Name, repo.Description)
77 77
 	if err != nil {
78 78
 		ctx.Error(500, "ForkRepository", err)
79 79
 		return

+ 5 - 5
routers/api/v1/repo/repo.go

@@ -105,7 +105,7 @@ func Search(ctx *context.APIContext) {
105 105
 
106 106
 // CreateUserRepo create a repository for a user
107 107
 func CreateUserRepo(ctx *context.APIContext, owner *models.User, opt api.CreateRepoOption) {
108
-	repo, err := models.CreateRepository(owner, models.CreateRepoOptions{
108
+	repo, err := models.CreateRepository(ctx.User, owner, models.CreateRepoOptions{
109 109
 		Name:        opt.Name,
110 110
 		Description: opt.Description,
111 111
 		Gitignores:  opt.Gitignores,
@@ -121,7 +121,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *models.User, opt api.CreateR
121 121
 			ctx.Error(422, "", err)
122 122
 		} else {
123 123
 			if repo != nil {
124
-				if err = models.DeleteRepository(ctx.User.ID, repo.ID); err != nil {
124
+				if err = models.DeleteRepository(ctx.User, ctx.User.ID, repo.ID); err != nil {
125 125
 					log.Error(4, "DeleteRepository: %v", err)
126 126
 				}
127 127
 			}
@@ -254,7 +254,7 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) {
254 254
 		return
255 255
 	}
256 256
 
257
-	repo, err := models.MigrateRepository(ctxUser, models.MigrateRepoOptions{
257
+	repo, err := models.MigrateRepository(ctx.User, ctxUser, models.MigrateRepoOptions{
258 258
 		Name:        form.RepoName,
259 259
 		Description: form.Description,
260 260
 		IsPrivate:   form.Private || setting.Repository.ForcePrivate,
@@ -263,7 +263,7 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) {
263 263
 	})
264 264
 	if err != nil {
265 265
 		if repo != nil {
266
-			if errDelete := models.DeleteRepository(ctxUser.ID, repo.ID); errDelete != nil {
266
+			if errDelete := models.DeleteRepository(ctx.User, ctxUser.ID, repo.ID); errDelete != nil {
267 267
 				log.Error(4, "DeleteRepository: %v", errDelete)
268 268
 			}
269 269
 		}
@@ -345,7 +345,7 @@ func Delete(ctx *context.APIContext) {
345 345
 		return
346 346
 	}
347 347
 
348
-	if err := models.DeleteRepository(owner.ID, repo.ID); err != nil {
348
+	if err := models.DeleteRepository(ctx.User, owner.ID, repo.ID); err != nil {
349 349
 		ctx.Error(500, "DeleteRepository", err)
350 350
 		return
351 351
 	}

+ 1 - 1
routers/repo/pull.go

@@ -125,7 +125,7 @@ func ForkPost(ctx *context.Context, form auth.CreateRepoForm) {
125 125
 		}
126 126
 	}
127 127
 
128
-	repo, err := models.ForkRepository(ctxUser, forkRepo, form.RepoName, form.Description)
128
+	repo, err := models.ForkRepository(ctx.User, ctxUser, forkRepo, form.RepoName, form.Description)
129 129
 	if err != nil {
130 130
 		ctx.Data["Err_RepoName"] = true
131 131
 		switch {

+ 4 - 4
routers/repo/repo.go

@@ -127,7 +127,7 @@ func CreatePost(ctx *context.Context, form auth.CreateRepoForm) {
127 127
 		return
128 128
 	}
129 129
 
130
-	repo, err := models.CreateRepository(ctxUser, models.CreateRepoOptions{
130
+	repo, err := models.CreateRepository(ctx.User, ctxUser, models.CreateRepoOptions{
131 131
 		Name:        form.RepoName,
132 132
 		Description: form.Description,
133 133
 		Gitignores:  form.Gitignores,
@@ -143,7 +143,7 @@ func CreatePost(ctx *context.Context, form auth.CreateRepoForm) {
143 143
 	}
144 144
 
145 145
 	if repo != nil {
146
-		if errDelete := models.DeleteRepository(ctxUser.ID, repo.ID); errDelete != nil {
146
+		if errDelete := models.DeleteRepository(ctx.User, ctxUser.ID, repo.ID); errDelete != nil {
147 147
 			log.Error(4, "DeleteRepository: %v", errDelete)
148 148
 		}
149 149
 	}
@@ -204,7 +204,7 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) {
204 204
 		return
205 205
 	}
206 206
 
207
-	repo, err := models.MigrateRepository(ctxUser, models.MigrateRepoOptions{
207
+	repo, err := models.MigrateRepository(ctx.User, ctxUser, models.MigrateRepoOptions{
208 208
 		Name:        form.RepoName,
209 209
 		Description: form.Description,
210 210
 		IsPrivate:   form.Private || setting.Repository.ForcePrivate,
@@ -218,7 +218,7 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) {
218 218
 	}
219 219
 
220 220
 	if repo != nil {
221
-		if errDelete := models.DeleteRepository(ctxUser.ID, repo.ID); errDelete != nil {
221
+		if errDelete := models.DeleteRepository(ctx.User, ctxUser.ID, repo.ID); errDelete != nil {
222 222
 			log.Error(4, "DeleteRepository: %v", errDelete)
223 223
 		}
224 224
 	}

+ 1 - 1
routers/repo/setting.go

@@ -314,7 +314,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
314 314
 			}
315 315
 		}
316 316
 
317
-		if err := models.DeleteRepository(ctx.Repo.Owner.ID, repo.ID); err != nil {
317
+		if err := models.DeleteRepository(ctx.User, ctx.Repo.Owner.ID, repo.ID); err != nil {
318 318
 			ctx.Handle(500, "DeleteRepository", err)
319 319
 			return
320 320
 		}

+ 1 - 0
routers/repo/webhook.go

@@ -121,6 +121,7 @@ func ParseHookEvent(form auth.WebhookForm) *models.HookEvent {
121 121
 			Create:      form.Create,
122 122
 			Push:        form.Push,
123 123
 			PullRequest: form.PullRequest,
124
+			Repository:  form.Repository,
124 125
 		},
125 126
 	}
126 127
 }

+ 10 - 0
templates/repo/settings/hook_settings.tmpl

@@ -52,6 +52,16 @@
52 52
 				</div>
53 53
 			</div>
54 54
 		</div>
55
+		<!-- Repository -->
56
+		<div class="seven wide column">
57
+			<div class="field">
58
+				<div class="ui checkbox">
59
+					<input class="hidden" name="repository" type="checkbox" tabindex="0" {{if .Webhook.Repository}}checked{{end}}>
60
+					<label>{{.i18n.Tr "repo.settings.event_repository"}}</label>
61
+					<span class="help">{{.i18n.Tr "repo.settings.event_repository_desc"}}</span>
62
+				</div>
63
+			</div>
64
+		</div>
55 65
 	</div>
56 66
 </div>
57 67