add organization webhook support #14784
@ -325,6 +325,8 @@ var migrations = []Migration{
|
||||
NewMigration("Create protected tag table", createProtectedTagTable),
|
||||
// v187 -> v188
|
||||
NewMigration("Drop unneeded webhook related columns", dropWebhookColumns),
|
||||
// v188 -> v189
|
||||
NewMigration("Add org_id to hook_task table", addOrgIDHookTaskColumn),
|
||||
}
|
||||
|
||||
// GetCurrentDBVersion returns the current db version
|
||||
|
22
models/migrations/v188.go
Normal file
22
models/migrations/v188.go
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2021 The Gitea 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 migrations
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addOrgIDHookTaskColumn(x *xorm.Engine) error {
|
||||
type HookTask struct {
|
||||
OrgID int64 `xorm:"INDEX NOT NULL DEFAULT 0"`
|
||||
}
|
||||
|
||||
if err := x.Sync2(new(HookTask)); err != nil {
|
||||
return fmt.Errorf("Sync2: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
@ -97,6 +97,9 @@ type HookEvents struct {
|
||||
PullRequestSync bool `json:"pull_request_sync"`
|
||||
Repository bool `json:"repository"`
|
||||
Release bool `json:"release"`
|
||||
Organization bool `json:"organization"`
|
||||
Team bool `json:"team"`
|
||||
TeamMember bool `json:"membership"`
|
||||
}
|
||||
|
||||
// HookEvent represents events that will delivery hook.
|
||||
@ -299,6 +302,33 @@ func (w *Webhook) HasRepositoryEvent() bool {
|
||||
(w.ChooseEvents && w.HookEvents.Repository)
|
||||
}
|
||||
|
||||
// HasOrgEvent returns if hook enabled org event.
|
||||
func (w *Webhook) HasOrgEvent() bool {
|
||||
if w.RepoID != 0 {
|
||||
return false
|
||||
}
|
||||
return w.SendEverything ||
|
||||
(w.ChooseEvents && w.HookEvents.Organization)
|
||||
}
|
||||
|
||||
// HasTeamEvent returns if hook enabled team event.
|
||||
func (w *Webhook) HasTeamEvent() bool {
|
||||
if w.RepoID != 0 {
|
||||
return false
|
||||
}
|
||||
return w.SendEverything ||
|
||||
(w.ChooseEvents && w.HookEvents.Team)
|
||||
}
|
||||
|
||||
// HasTeamMemberEvent returns if hook enabled team member event.
|
||||
func (w *Webhook) HasTeamMemberEvent() bool {
|
||||
if w.RepoID != 0 {
|
||||
return false
|
||||
}
|
||||
return w.SendEverything ||
|
||||
(w.ChooseEvents && w.HookEvents.TeamMember)
|
||||
}
|
||||
|
||||
// EventCheckers returns event checkers
|
||||
func (w *Webhook) EventCheckers() []struct {
|
||||
Has func() bool
|
||||
@ -328,6 +358,9 @@ func (w *Webhook) EventCheckers() []struct {
|
||||
{w.HasPullRequestSyncEvent, HookEventPullRequestSync},
|
||||
{w.HasRepositoryEvent, HookEventRepository},
|
||||
{w.HasReleaseEvent, HookEventRelease},
|
||||
{w.HasOrgEvent, HookEventOrg},
|
||||
{w.HasTeamEvent, HookEventTeam},
|
||||
{w.HasTeamMemberEvent, HookEventTeamMember},
|
||||
}
|
||||
}
|
||||
|
||||
@ -597,6 +630,9 @@ const (
|
||||
HookEventPullRequestSync HookEventType = "pull_request_sync"
|
||||
HookEventRepository HookEventType = "repository"
|
||||
HookEventRelease HookEventType = "release"
|
||||
HookEventOrg HookEventType = "organization"
|
||||
HookEventTeam HookEventType = "team"
|
||||
HookEventTeamMember HookEventType = "membership"
|
||||
)
|
||||
|
||||
// Event returns the HookEventType as an event string
|
||||
@ -627,10 +663,38 @@ func (h HookEventType) Event() string {
|
||||
return "repository"
|
||||
case HookEventRelease:
|
||||
|
||||
return "release"
|
||||
case HookEventOrg:
|
||||
return "organization"
|
||||
case HookEventTeam:
|
||||
return "team"
|
||||
case HookEventTeamMember:
|
||||
return "membership"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// HookEventLevel leve of a hook event
|
||||
type HookEventLevel int64
|
||||
|
||||
const (
|
||||
// HookEventLevelRepo all hook types can be used
|
||||
HookEventLevelRepo HookEventLevel = iota + 1
|
||||
// HookEventLevelOrg org and system hook can be used
|
||||
HookEventLevelOrg
|
||||
// HookEventLevelSys only system hook can be used
|
||||
// HookEventLevelSys
|
||||
)
|
||||
|
||||
// EventLevel got last leve of this event
|
||||
func (h HookEventType) EventLevel() HookEventLevel {
|
||||
if h == HookEventOrg ||
|
||||
h == HookEventTeam ||
|
||||
h == HookEventTeamMember {
|
||||
return HookEventLevelOrg
|
||||
}
|
||||
return HookEventLevelRepo
|
||||
}
|
||||
|
||||
// HookRequest represents hook task request information.
|
||||
type HookRequest struct {
|
||||
URL string `json:"url"`
|
||||
@ -649,6 +713,7 @@ type HookResponse struct {
|
||||
type HookTask struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64 `xorm:"INDEX"`
|
||||
OrgID int64 `xorm:"INDEX NOT NULL DEFAULT 0"`
|
||||
HookID int64
|
||||
UUID string
|
||||
api.Payloader `xorm:"-"`
|
||||
@ -759,6 +824,15 @@ func FindRepoUndeliveredHookTasks(repoID int64) ([]*HookTask, error) {
|
||||
return tasks, nil
|
||||
}
|
||||
|
||||
// FindOrgUndeliveredHookTasks finds the undelivered hook tasks of an organisation
|
||||
func FindOrgUndeliveredHookTasks(orgID int64) ([]*HookTask, error) {
|
||||
tasks := make([]*HookTask, 0, 5)
|
||||
adelowo
commented
```suggestion
// FindOrgUndeliveredHookTasks finds the undelivered hook tasks of an organisation
```
|
||||
if err := x.Where("org_id=? AND is_delivered=?", orgID, false).Find(&tasks); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tasks, nil
|
||||
}
|
||||
|
||||
// CleanupHookTaskTable deletes rows from hook_task as needed.
|
||||
func CleanupHookTaskTable(ctx context.Context, cleanupType HookTaskCleanupType, olderThan time.Duration, numberToKeep int) error {
|
||||
log.Trace("Doing: CleanupHookTaskTable")
|
||||
|
@ -70,8 +70,22 @@ func TestWebhook_EventsArray(t *testing.T) {
|
||||
"pull_request", "pull_request_assign", "pull_request_label", "pull_request_milestone",
|
||||
"pull_request_comment", "pull_request_review_approved", "pull_request_review_rejected",
|
||||
"pull_request_review_comment", "pull_request_sync", "repository", "release",
|
||||
},
|
||||
"organization", "team", "membership"},
|
||||
(&Webhook{
|
||||
RepoID: 0,
|
||||
OrgID: 3,
|
||||
HookEvent: &HookEvent{SendEverything: true},
|
||||
}).EventsArray(),
|
||||
)
|
||||
|
||||
assert.Equal(t, []string{"create", "delete", "fork", "push",
|
||||
"issues", "issue_assign", "issue_label", "issue_milestone", "issue_comment",
|
||||
"pull_request", "pull_request_assign", "pull_request_label", "pull_request_milestone",
|
||||
"pull_request_comment", "pull_request_review_approved", "pull_request_review_rejected",
|
||||
"pull_request_review_comment", "pull_request_sync", "repository", "release"},
|
||||
(&Webhook{
|
||||
RepoID: 3,
|
||||
OrgID: 0,
|
||||
HookEvent: &HookEvent{SendEverything: true},
|
||||
}).EventsArray(),
|
||||
)
|
||||
|
@ -59,4 +59,12 @@ type Notifier interface {
|
||||
NotifySyncDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string)
|
||||
|
||||
NotifyRepoPendingTransfer(doer, newOwner *models.User, repo *models.Repository)
|
||||
|
||||
// org
|
||||
NotifyAddOrgMember(doer, org, member *models.User)
|
||||
NotifyRemoveOrgMember(doer, org, member *models.User)
|
||||
NotifyAddOrgTeam(doer, org *models.User, team *models.Team)
|
||||
NotifyRemoveOrgTeam(doer, org *models.User, team *models.Team)
|
||||
NotifyAddTeamMember(doer, org, member *models.User, team *models.Team)
|
||||
NotifyRemoveTeamMember(doer, org, member *models.User, team *models.Team)
|
||||
}
|
||||
|
@ -167,6 +167,30 @@ func (*NullNotifier) NotifySyncCreateRef(doer *models.User, repo *models.Reposit
|
||||
func (*NullNotifier) NotifySyncDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
|
||||
}
|
||||
|
||||
// NotifyAddOrgMember notify new member added into an org
|
||||
func (*NullNotifier) NotifyAddOrgMember(doer, org, member *models.User) {
|
||||
}
|
||||
|
||||
// NotifyRemoveOrgMember notify member leave or be removed from an org
|
||||
func (*NullNotifier) NotifyRemoveOrgMember(doer, org, member *models.User) {
|
||||
}
|
||||
|
||||
// NotifyAddOrgTeam notify a new team created in an org
|
||||
func (*NullNotifier) NotifyAddOrgTeam(doer, org *models.User, team *models.Team) {
|
||||
}
|
||||
|
||||
// NotifyRemoveOrgTeam notify a team removed from an org
|
||||
func (*NullNotifier) NotifyRemoveOrgTeam(doer, org *models.User, team *models.Team) {
|
||||
}
|
||||
|
||||
// NotifyAddTeamMember notify add new member in a team
|
||||
func (*NullNotifier) NotifyAddTeamMember(doer, org, member *models.User, team *models.Team) {
|
||||
}
|
||||
|
||||
// NotifyRemoveTeamMember notify a member be removed from a team
|
||||
func (*NullNotifier) NotifyRemoveTeamMember(doer, org, member *models.User, team *models.Team) {
|
||||
}
|
||||
|
||||
// NotifyRepoPendingTransfer places a place holder function
|
||||
func (*NullNotifier) NotifyRepoPendingTransfer(doer, newOwner *models.User, repo *models.Repository) {
|
||||
}
|
||||
|
@ -291,6 +291,48 @@ func NotifySyncDeleteRef(pusher *models.User, repo *models.Repository, refType,
|
||||
}
|
||||
}
|
||||
|
||||
// NotifyAddOrgMember notify new member added into an org
|
||||
func NotifyAddOrgMember(doer, org, member *models.User) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.NotifyAddOrgMember(doer, org, member)
|
||||
}
|
||||
}
|
||||
|
||||
// NotifyRemoveOrgMember notify member leave or be removed from an org
|
||||
func NotifyRemoveOrgMember(doer, org, member *models.User) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.NotifyRemoveOrgMember(doer, org, member)
|
||||
}
|
||||
}
|
||||
|
||||
// NotifyAddOrgTeam notify a new team created in an org
|
||||
func NotifyAddOrgTeam(doer, org *models.User, team *models.Team) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.NotifyAddOrgTeam(doer, org, team)
|
||||
}
|
||||
}
|
||||
|
||||
// NotifyRemoveOrgTeam notify a team removed from an org
|
||||
func NotifyRemoveOrgTeam(doer, org *models.User, team *models.Team) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.NotifyRemoveOrgTeam(doer, org, team)
|
||||
}
|
||||
}
|
||||
|
||||
// NotifyAddTeamMember notify add new member in a team
|
||||
func NotifyAddTeamMember(doer, org, member *models.User, team *models.Team) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.NotifyAddTeamMember(doer, org, member, team)
|
||||
}
|
||||
}
|
||||
|
||||
// NotifyRemoveTeamMember notify a member be removed from a team
|
||||
func NotifyRemoveTeamMember(doer, org, member *models.User, team *models.Team) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.NotifyRemoveTeamMember(doer, org, member, team)
|
||||
}
|
||||
}
|
||||
|
||||
// NotifyRepoPendingTransfer notifies creation of pending transfer to notifiers
|
||||
func NotifyRepoPendingTransfer(doer, newOwner *models.User, repo *models.Repository) {
|
||||
for _, notifier := range notifiers {
|
||||
|
@ -819,3 +819,97 @@ func (m *webhookNotifier) NotifySyncCreateRef(pusher *models.User, repo *models.
|
||||
func (m *webhookNotifier) NotifySyncDeleteRef(pusher *models.User, repo *models.Repository, refType, refFullName string) {
|
||||
m.NotifyDeleteRef(pusher, repo, refType, refFullName)
|
||||
}
|
||||
|
||||
func (*webhookNotifier) NotifyAddOrgMember(doer, org, member *models.User) {
|
||||
apiDoer := convert.ToUser(doer, doer)
|
||||
apiMember := convert.ToUser(member, doer)
|
||||
apiOrg := convert.ToOrganization(org)
|
||||
|
||||
if err := webhook_services.PrepareOrgWebhooks(org, models.HookEventOrg, &api.OrganizationPayload{
|
||||
Action: api.OrganizationActionTypeAddMember,
|
||||
Member: apiMember,
|
||||
Organization: apiOrg,
|
||||
Sender: apiDoer,
|
||||
}); err != nil {
|
||||
log.Error("PrepareWebhooks: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (*webhookNotifier) NotifyRemoveOrgMember(doer, org, member *models.User) {
|
||||
apiDoer := convert.ToUser(doer, doer)
|
||||
apiMember := convert.ToUser(member, doer)
|
||||
apiOrg := convert.ToOrganization(org)
|
||||
|
||||
if err := webhook_services.PrepareOrgWebhooks(org, models.HookEventOrg, &api.OrganizationPayload{
|
||||
Action: api.OrganizationActionTypeRemoveMember,
|
||||
Member: apiMember,
|
||||
Organization: apiOrg,
|
||||
Sender: apiDoer,
|
||||
}); err != nil {
|
||||
log.Error("PrepareWebhooks: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (*webhookNotifier) NotifyAddOrgTeam(doer, org *models.User, team *models.Team) {
|
||||
apiDoer := convert.ToUser(doer, doer)
|
||||
apiOrg := convert.ToOrganization(org)
|
||||
apiTeam := convert.ToTeam(team)
|
||||
|
||||
if err := webhook_services.PrepareOrgWebhooks(org, models.HookEventTeam, &api.TeamPayload{
|
||||
Action: api.TeamActionTypeAdd,
|
||||
Team: apiTeam,
|
||||
Organization: apiOrg,
|
||||
Sender: apiDoer,
|
||||
}); err != nil {
|
||||
log.Error("PrepareWebhooks: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (*webhookNotifier) NotifyRemoveOrgTeam(doer, org *models.User, team *models.Team) {
|
||||
apiDoer := convert.ToUser(doer, doer)
|
||||
apiOrg := convert.ToOrganization(org)
|
||||
apiTeam := convert.ToTeam(team)
|
||||
|
||||
if err := webhook_services.PrepareOrgWebhooks(org, models.HookEventTeam, &api.TeamPayload{
|
||||
Action: api.TeamActionTypeRemove,
|
||||
Team: apiTeam,
|
||||
Organization: apiOrg,
|
||||
Sender: apiDoer,
|
||||
}); err != nil {
|
||||
log.Error("PrepareWebhooks: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (*webhookNotifier) NotifyAddTeamMember(doer, org, member *models.User, team *models.Team) {
|
||||
apiDoer := convert.ToUser(doer, doer)
|
||||
apiMember := convert.ToUser(member, doer)
|
||||
apiOrg := convert.ToOrganization(org)
|
||||
apiTeam := convert.ToTeam(team)
|
||||
|
||||
if err := webhook_services.PrepareOrgWebhooks(org, models.HookEventTeamMember, &api.MembershipPayload{
|
||||
Action: api.MembershipActionTypeAdd,
|
||||
Team: apiTeam,
|
||||
Organization: apiOrg,
|
||||
Member: apiMember,
|
||||
Sender: apiDoer,
|
||||
}); err != nil {
|
||||
log.Error("PrepareWebhooks: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (*webhookNotifier) NotifyRemoveTeamMember(doer, org, member *models.User, team *models.Team) {
|
||||
apiDoer := convert.ToUser(doer, doer)
|
||||
apiMember := convert.ToUser(member, doer)
|
||||
apiOrg := convert.ToOrganization(org)
|
||||
apiTeam := convert.ToTeam(team)
|
||||
|
||||
if err := webhook_services.PrepareOrgWebhooks(org, models.HookEventTeamMember, &api.MembershipPayload{
|
||||
Action: api.MembershipActionTypeRemove,
|
||||
Team: apiTeam,
|
||||
Organization: apiOrg,
|
||||
Member: apiMember,
|
||||
Sender: apiDoer,
|
||||
}); err != nil {
|
||||
log.Error("PrepareWebhooks: %v", err)
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
package structs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
@ -438,3 +439,95 @@ func (p *RepositoryPayload) JSONPayload() ([]byte, error) {
|
||||
json := jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
return json.MarshalIndent(p, "", " ")
|
||||
}
|
||||
|
||||
// organization
|
||||
|
||||
// OrganizationActionType org action type
|
||||
type OrganizationActionType string
|
||||
|
||||
const (
|
||||
// OrganizationActionTypeAddMember add member in an org
|
||||
OrganizationActionTypeAddMember OrganizationActionType = "member_added"
|
||||
// OrganizationActionTypeRemoveMember member removed in an org
|
||||
OrganizationActionTypeRemoveMember OrganizationActionType = "member_removed"
|
||||
)
|
||||
|
||||
// OrganizationPayload Activity related to an organization and its members.
|
||||
type OrganizationPayload struct {
|
||||
Secret string `json:"secret"`
|
||||
Action OrganizationActionType `json:"action"`
|
||||
Member *User `json:"member"`
|
||||
Organization *Organization `json:"organization"`
|
||||
Sender *User `json:"sender"`
|
||||
}
|
||||
|
||||
// SetSecret modifies the secret of the RepositoryPayload
|
||||
func (p *OrganizationPayload) SetSecret(secret string) {
|
||||
p.Secret = secret
|
||||
}
|
||||
|
||||
// JSONPayload JSON representation of the payload
|
||||
func (p *OrganizationPayload) JSONPayload() ([]byte, error) {
|
||||
return json.MarshalIndent(p, "", " ")
|
||||
}
|
||||
|
||||
// TeamActionType action types of team
|
||||
type TeamActionType string
|
||||
|
||||
const (
|
||||
// TeamActionTypeAdd created a new team
|
||||
TeamActionTypeAdd TeamActionType = "created"
|
||||
// TeamActionTypeRemove deleted a team
|
||||
TeamActionTypeRemove TeamActionType = "deleted"
|
||||
)
|
||||
|
||||
// TeamPayload Activity related to an organization's team.
|
||||
// The type of activity is specified in the action property of the payload object
|
||||
type TeamPayload struct {
|
||||
Secret string `json:"secret"`
|
||||
Action TeamActionType `json:"action"`
|
||||
Team *Team `json:"team"`
|
||||
Organization *Organization `json:"organization"`
|
||||
Sender *User `json:"sender"`
|
||||
}
|
||||
|
||||
// SetSecret modifies the secret of the RepositoryPayload
|
||||
func (p *TeamPayload) SetSecret(secret string) {
|
||||
p.Secret = secret
|
||||
}
|
||||
|
||||
// JSONPayload JSON representation of the payload
|
||||
func (p *TeamPayload) JSONPayload() ([]byte, error) {
|
||||
return json.MarshalIndent(p, "", " ")
|
||||
}
|
||||
|
||||
// MembershipActionType type of membership
|
||||
type MembershipActionType string
|
||||
|
||||
const (
|
||||
// MembershipActionTypeAdd add new member
|
||||
MembershipActionTypeAdd MembershipActionType = "added"
|
||||
// MembershipActionTypeRemove remove member
|
||||
MembershipActionTypeRemove MembershipActionType = "removed"
|
||||
)
|
||||
|
||||
// MembershipPayload Activity related to team membership.
|
||||
// The type of activity is specified in the action property of the payload object
|
||||
type MembershipPayload struct {
|
||||
Secret string `json:"secret"`
|
||||
Action MembershipActionType `json:"action"`
|
||||
Member *User `json:"member"`
|
||||
Team *Team `json:"team"`
|
||||
Organization *Organization `json:"organization"`
|
||||
Sender *User `json:"sender"`
|
||||
}
|
||||
|
||||
// SetSecret modifies the secret of the RepositoryPayload
|
||||
func (p *MembershipPayload) SetSecret(secret string) {
|
||||
p.Secret = secret
|
||||
}
|
||||
|
||||
// JSONPayload JSON representation of the payload
|
||||
func (p *MembershipPayload) JSONPayload() ([]byte, error) {
|
||||
return json.MarshalIndent(p, "", " ")
|
||||
}
|
||||
|
@ -1812,6 +1812,13 @@ settings.event_pull_request_review = Pull Request Reviewed
|
||||
settings.event_pull_request_review_desc = Pull request approved, rejected, or review comment.
|
||||
settings.event_pull_request_sync = Pull Request Synchronized
|
||||
settings.event_pull_request_sync_desc = Pull request synchronized.
|
||||
settings.event_header_org = Organization
|
||||
settings.event_organization = Organization
|
||||
settings.event_organization_desc = added or removed member in organization
|
||||
settings.event_team = team
|
||||
settings.event_team_desc = added or removed team in organization
|
||||
settings.event_team_member = team member (membership)
|
||||
settings.event_team_member_desc = added or removed member in a team
|
||||
settings.branch_filter = Branch filter
|
||||
settings.branch_filter_desc = Branch whitelist for push, branch creation and branch deletion events, specified as glob pattern. If empty or <code>*</code>, events for all branches are reported. See <a href="https://pkg.go.dev/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a> documentation for syntax. Examples: <code>master</code>, <code>{master,release*}</code>.
|
||||
settings.active = Active
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/convert"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/notification"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
"code.gitea.io/gitea/routers/api/v1/user"
|
||||
@ -187,6 +188,7 @@ func CreateTeam(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
notification.NotifyAddOrgTeam(ctx.User, ctx.Org.Organization, team)
|
||||
ctx.JSON(http.StatusCreated, convert.ToTeam(team))
|
||||
}
|
||||
|
||||
@ -286,11 +288,19 @@ func DeleteTeam(ctx *context.APIContext) {
|
||||
// responses:
|
||||
// "204":
|
||||
// description: team deleted
|
||||
|
||||
if err := models.DeleteTeam(ctx.Org.Team); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "DeleteTeam", err)
|
||||
return
|
||||
}
|
||||
var err error
|
||||
ctx.Org.Organization, err = models.GetUserByID(ctx.Org.Team.OrgID)
|
||||
if err != nil {
|
||||
log.Error("GetUserByID: %v", err)
|
||||
ctx.Status(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
notification.NotifyRemoveOrgTeam(ctx.User, ctx.Org.Organization, ctx.Org.Team)
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
@ -412,10 +422,25 @@ func AddTeamMember(ctx *context.APIContext) {
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
var err error
|
||||
ctx.Org.Organization, err = models.GetUserByID(ctx.Org.Team.OrgID)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "AddMember", err)
|
||||
return
|
||||
}
|
||||
|
||||
isOrgMember, err2 := models.IsOrganizationMember(ctx.Org.Organization.ID, u.ID)
|
||||
if err2 != nil {
|
||||
log.Error("IsOrganizationMember : %v", err2)
|
||||
}
|
||||
if err := ctx.Org.Team.AddMember(u.ID); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "AddMember", err)
|
||||
return
|
||||
}
|
||||
notification.NotifyAddTeamMember(ctx.User, ctx.Org.Organization, u, ctx.Org.Team)
|
||||
if !isOrgMember {
|
||||
notification.NotifyAddOrgMember(ctx.User, ctx.Org.Organization, u)
|
||||
}
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
@ -453,6 +478,21 @@ func RemoveTeamMember(ctx *context.APIContext) {
|
||||
ctx.Error(http.StatusInternalServerError, "RemoveMember", err)
|
||||
return
|
||||
}
|
||||
var err error
|
||||
ctx.Org.Organization, err = models.GetUserByID(ctx.Org.Team.OrgID)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "RemoveMember", err)
|
||||
return
|
||||
}
|
||||
isOrgMember, err2 := models.IsOrganizationMember(ctx.Org.Organization.ID, u.ID)
|
||||
if err2 != nil {
|
||||
log.Error("IsOrganizationMember : %v", err2)
|
||||
}
|
||||
notification.NotifyRemoveTeamMember(ctx.User, ctx.Org.Organization, u, ctx.Org.Team)
|
||||
if !isOrgMember {
|
||||
notification.NotifyRemoveOrgMember(ctx.User, ctx.Org.Organization, u)
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
|
@ -129,6 +129,9 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, orgID, repoID
|
||||
PullRequestSync: pullHook(form.Events, string(models.HookEventPullRequestSync)),
|
||||
Repository: util.IsStringInSlice(string(models.HookEventRepository), form.Events, true),
|
||||
Release: util.IsStringInSlice(string(models.HookEventRelease), form.Events, true),
|
||||
Organization: util.IsStringInSlice(string(models.HookEventOrg), form.Events, true),
|
||||
Team: util.IsStringInSlice(string(models.HookEventTeam), form.Events, true),
|
||||
TeamMember: util.IsStringInSlice(string(models.HookEventTeamMember), form.Events, true),
|
||||
},
|
||||
BranchFilter: form.BranchFilter,
|
||||
},
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/notification"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
"code.gitea.io/gitea/routers/utils"
|
||||
"code.gitea.io/gitea/services/forms"
|
||||
@ -63,9 +64,25 @@ func TeamsAction(ctx *context.Context) {
|
||||
ctx.Error(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
isOrgMember, err2 := models.IsOrganizationMember(ctx.Org.Organization.ID, ctx.User.ID)
|
||||
if err2 != nil {
|
||||
log.Error("IsOrganizationMember: %v", err2)
|
||||
}
|
||||
err = ctx.Org.Team.AddMember(ctx.User.ID)
|
||||
if err == nil {
|
||||
notification.NotifyAddTeamMember(ctx.User, ctx.Org.Organization, ctx.User, ctx.Org.Team)
|
||||
if !isOrgMember {
|
||||
notification.NotifyAddOrgMember(ctx.User, ctx.Org.Organization, ctx.User)
|
||||
}
|
||||
}
|
||||
case "leave":
|
||||
err = ctx.Org.Team.RemoveMember(ctx.User.ID)
|
||||
if err == nil {
|
||||
notification.NotifyRemoveTeamMember(ctx.User, ctx.Org.Organization, ctx.User, ctx.Org.Team)
|
||||
if isOrgMember, err := models.IsOrganizationMember(ctx.Org.Organization.ID, ctx.User.ID); err == nil && !isOrgMember {
|
||||
notification.NotifyRemoveOrgMember(ctx.User, ctx.Org.Organization, ctx.User)
|
||||
}
|
||||
}
|
||||
case "remove":
|
||||
if !ctx.Org.IsOwner {
|
||||
ctx.Error(http.StatusNotFound)
|
||||
@ -73,6 +90,15 @@ func TeamsAction(ctx *context.Context) {
|
||||
}
|
||||
err = ctx.Org.Team.RemoveMember(uid)
|
||||
page = "team"
|
||||
if err == nil {
|
||||
u, err2 := models.GetUserByID(uid)
|
||||
if err2 == nil {
|
||||
notification.NotifyRemoveTeamMember(ctx.User, ctx.Org.Organization, u, ctx.Org.Team)
|
||||
if isOrgMember, err3 := models.IsOrganizationMember(ctx.Org.Organization.ID, u.ID); err3 == nil && !isOrgMember {
|
||||
notification.NotifyRemoveOrgMember(ctx.User, ctx.Org.Organization, u)
|
||||
}
|
||||
}
|
||||
}
|
||||
case "add":
|
||||
if !ctx.Org.IsOwner {
|
||||
ctx.Error(http.StatusNotFound)
|
||||
@ -97,12 +123,24 @@ func TeamsAction(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
isOrgMember, err2 := models.IsOrganizationMember(ctx.Org.Organization.ID, u.ID)
|
||||
if err2 != nil {
|
||||
log.Error("IsOrganizationMember: %v", err2)
|
||||
}
|
||||
|
||||
if ctx.Org.Team.IsMember(u.ID) {
|
||||
ctx.Flash.Error(ctx.Tr("org.teams.add_duplicate_users"))
|
||||
} else {
|
||||
err = ctx.Org.Team.AddMember(u.ID)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
notification.NotifyAddTeamMember(ctx.User, ctx.Org.Organization, u, ctx.Org.Team)
|
||||
if !isOrgMember {
|
||||
notification.NotifyAddOrgMember(ctx.User, ctx.Org.Organization, u)
|
||||
}
|
||||
}
|
||||
|
||||
page = "team"
|
||||
}
|
||||
|
||||
@ -237,6 +275,7 @@ func NewTeamPost(ctx *context.Context) {
|
||||
}
|
||||
return
|
||||
}
|
||||
notification.NotifyAddOrgTeam(ctx.User, ctx.Org.Organization, t)
|
||||
log.Trace("Team created: %s/%s", ctx.Org.Organization.Name, t.Name)
|
||||
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + t.LowerName)
|
||||
}
|
||||
@ -351,6 +390,7 @@ func DeleteTeam(ctx *context.Context) {
|
||||
ctx.Flash.Success(ctx.Tr("org.teams.delete_team_success"))
|
||||
}
|
||||
|
||||
notification.NotifyRemoveOrgTeam(ctx.User, ctx.Org.Organization, ctx.Org.Team)
|
||||
ctx.JSON(http.StatusOK, map[string]interface{}{
|
||||
"redirect": ctx.Org.OrgLink + "/teams",
|
||||
})
|
||||
|
@ -136,6 +136,10 @@ func WebhooksNew(ctx *context.Context) {
|
||||
ctx.Data["PageIsSettingsHooksNew"] = true
|
||||
}
|
||||
|
||||
if orCtx.OrgID > 0 {
|
||||
ctx.Data["PageIsOrgHooks"] = true
|
||||
}
|
||||
|
||||
hookType := checkHookType(ctx)
|
||||
ctx.Data["HookType"] = hookType
|
||||
if ctx.Written() {
|
||||
@ -177,6 +181,9 @@ func ParseHookEvent(form forms.WebhookForm) *models.HookEvent {
|
||||
PullRequestReview: form.PullRequestReview,
|
||||
PullRequestSync: form.PullRequestSync,
|
||||
Repository: form.Repository,
|
||||
Organization: form.Organization,
|
||||
Team: form.Team,
|
||||
TeamMember: form.TeamMember,
|
||||
},
|
||||
BranchFilter: form.BranchFilter,
|
||||
}
|
||||
@ -696,6 +703,10 @@ func WebHooksEdit(ctx *context.Context) {
|
||||
}
|
||||
ctx.Data["Webhook"] = w
|
||||
|
||||
if orCtx.OrgID > 0 {
|
||||
ctx.Data["PageIsOrgHooks"] = true
|
||||
}
|
||||
|
||||
ctx.HTML(http.StatusOK, orCtx.NewTemplate)
|
||||
}
|
||||
|
||||
|
@ -235,6 +235,9 @@ type WebhookForm struct {
|
||||
PullRequestSync bool
|
||||
Repository bool
|
||||
Active bool
|
||||
Organization bool
|
||||
Team bool
|
||||
TeamMember bool
|
||||
BranchFilter string `binding:"GlobPattern"`
|
||||
}
|
||||
|
||||
|
@ -226,15 +226,26 @@ func DeliverHooks(ctx context.Context) {
|
||||
log.Trace("DeliverHooks [repo_id: %v]", repoIDStr)
|
||||
hookQueue.Remove(repoIDStr)
|
||||
|
||||
repoID, err := strconv.ParseInt(repoIDStr, 10, 64)
|
||||
keyID, err := strconv.ParseInt(repoIDStr, 10, 64)
|
||||
if err != nil {
|
||||
log.Error("Invalid repo ID: %s", repoIDStr)
|
||||
continue
|
||||
}
|
||||
|
||||
tasks, err := models.FindRepoUndeliveredHookTasks(repoID)
|
||||
// keyID > 0 : repo id
|
||||
// keyID < 0 : org id
|
||||
// keyID = 0 : system only (not used now)
|
||||
var tasks []*models.HookTask
|
||||
|
||||
if keyID < 0 {
|
||||
orgID := -keyID
|
||||
tasks, err = models.FindOrgUndeliveredHookTasks(orgID)
|
||||
} else {
|
||||
repoID := keyID
|
||||
tasks, err = models.FindRepoUndeliveredHookTasks(repoID)
|
||||
}
|
||||
if err != nil {
|
||||
log.Error("Get repository [%d] hook tasks: %v", repoID, err)
|
||||
log.Error("Get [%d] hook tasks: %v", keyID, err)
|
||||
continue
|
||||
}
|
||||
for _, t := range tasks {
|
||||
|
@ -91,12 +91,21 @@ func getPayloadBranch(p api.Payloader) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// PrepareWebhook adds special webhook to task queue for given payload.
|
||||
// PrepareWebhook adds special repository webhook to task queue for given payload.
|
||||
zeripath
commented
It probably would be simpler to have:
Then you would need most of the changes in modules/notification/webhook/webhook.go and elsewhere. It probably would be simpler to have:
```go
func PrepareWebhook(w *models.Webhook, repo *models.Repository, event models.HookEventType, p api.Payloader) error {
return PrepareRepoOrgWebhook(w, repo, nil, event, p)
}
func PrepareOrgWebhook(w *models.Webhook, org *models.User, event models.HookEventType, p api.Payloader) error {
return PrepareRepoOrgWebhook(w, nil, org, event, p)
}
func PrepareRepoOrgWebhook(w *models.Webhook, repo *models.Repository, org *models.User, event models.HookEventType, p api.Payloader) error {
...
}
```
Then you would need most of the changes in modules/notification/webhook/webhook.go and elsewhere.
|
||||
func PrepareWebhook(w *models.Webhook, repo *models.Repository, event models.HookEventType, p api.Payloader) error {
|
||||
if err := prepareWebhook(w, repo, event, p); err != nil {
|
||||
return prepareRepoOrgWebhook(w, repo, nil, event, p)
|
||||
}
|
||||
|
||||
func prepareRepoOrgWebhook(w *models.Webhook, repo *models.Repository, org *models.User, event models.HookEventType, p api.Payloader) error {
|
||||
if err := prepareWebhook(w, repo, org, event, p); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if org != nil {
|
||||
go hookQueue.Add(-org.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
go hookQueue.Add(repo.ID)
|
||||
return nil
|
||||
}
|
||||
@ -116,7 +125,7 @@ func checkBranch(w *models.Webhook, branch string) bool {
|
||||
return g.Match(branch)
|
||||
}
|
||||
|
||||
func prepareWebhook(w *models.Webhook, repo *models.Repository, event models.HookEventType, p api.Payloader) error {
|
||||
func prepareWebhook(w *models.Webhook, repo *models.Repository, org *models.User, event models.HookEventType, p api.Payloader) error {
|
||||
// Skip sending if webhooks are disabled.
|
||||
if setting.DisableWebhooks {
|
||||
return nil
|
||||
@ -161,56 +170,90 @@ func prepareWebhook(w *models.Webhook, repo *models.Repository, event models.Hoo
|
||||
payloader = p
|
||||
}
|
||||
|
||||
if err = models.CreateHookTask(&models.HookTask{
|
||||
RepoID: repo.ID,
|
||||
task := &models.HookTask{
|
||||
HookID: w.ID,
|
||||
Payloader: payloader,
|
||||
EventType: event,
|
||||
}); err != nil {
|
||||
}
|
||||
|
||||
if repo != nil {
|
||||
task.RepoID = repo.ID
|
||||
}
|
||||
|
||||
if org != nil {
|
||||
task.OrgID = org.ID
|
||||
}
|
||||
|
||||
if err = models.CreateHookTask(task); err != nil {
|
||||
return fmt.Errorf("CreateHookTask: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PrepareWebhooks adds new webhooks to task queue for given payload.
|
||||
// PrepareWebhooks adds new repository webhooks to task queue for given payload.
|
||||
func PrepareWebhooks(repo *models.Repository, event models.HookEventType, p api.Payloader) error {
|
||||
if err := prepareWebhooks(repo, event, p); err != nil {
|
||||
return prepareRepoOrgWebhooks(repo, nil, event, p)
|
||||
}
|
||||
|
||||
// PrepareOrgWebhooks adds new orgnization webhooks to task queue for given payload.
|
||||
func PrepareOrgWebhooks(org *models.User, event models.HookEventType, p api.Payloader) error {
|
||||
return prepareRepoOrgWebhooks(nil, org, event, p)
|
||||
}
|
||||
|
||||
func prepareRepoOrgWebhooks(repo *models.Repository, org *models.User, event models.HookEventType, p api.Payloader) error {
|
||||
if err := prepareWebhooks(repo, org, event, p); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if org != nil {
|
||||
go hookQueue.Add(-org.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
go hookQueue.Add(repo.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func prepareWebhooks(repo *models.Repository, event models.HookEventType, p api.Payloader) error {
|
||||
ws, err := models.GetActiveWebhooksByRepoID(repo.ID)
|
||||
func prepareWebhooks(repo *models.Repository, org *models.User, event models.HookEventType, p api.Payloader) error {
|
||||
level := event.EventLevel()
|
||||
|
||||
// 1. Add any admin-defined system webhooks
|
||||
ws, err := models.GetSystemWebhooks()
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetActiveWebhooksByRepoID: %v", err)
|
||||
return fmt.Errorf("GetSystemWebhooks: %v", err)
|
||||
}
|
||||
|
||||
// check if repo belongs to org and append additional webhooks
|
||||
if repo.MustOwner().IsOrganization() {
|
||||
// 2. check if repo belongs to org and append additional webhooks
|
||||
if org != nil || repo.MustOwner().IsOrganization() {
|
||||
// get hooks for org
|
||||
orgHooks, err := models.GetActiveWebhooksByOrgID(repo.OwnerID)
|
||||
orgHooks, err := models.GetActiveWebhooksByOrgID(func() int64 {
|
||||
if org != nil {
|
||||
return org.ID
|
||||
}
|
||||
return repo.OwnerID
|
||||
}())
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetActiveWebhooksByOrgID: %v", err)
|
||||
}
|
||||
ws = append(ws, orgHooks...)
|
||||
}
|
||||
|
||||
// Add any admin-defined system webhooks
|
||||
systemHooks, err := models.GetSystemWebhooks()
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetSystemWebhooks: %v", err)
|
||||
// 3. add repo hooks if needed
|
||||
if level == models.HookEventLevelRepo && repo != nil {
|
||||
repoHooks, err := models.GetActiveWebhooksByRepoID(repo.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetActiveWebhooksByRepoID: %v", err)
|
||||
}
|
||||
ws = append(ws, repoHooks...)
|
||||
}
|
||||
ws = append(ws, systemHooks...)
|
||||
|
||||
if len(ws) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, w := range ws {
|
||||
if err = prepareWebhook(w, repo, event, p); err != nil {
|
||||
if err = prepareWebhook(w, repo, org, event, p); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -217,6 +217,42 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{if or .PageIsOrgHooks .PageIsAdminSystemHooks}}
|
||||
<!-- org hooks -->
|
||||
<div class="fourteen wide column">
|
||||
<label>{{.i18n.Tr "repo.settings.event_header_org"}}</label>
|
||||
</div>
|
||||
<!-- organization hook -->
|
||||
<div class="seven wide column">
|
||||
<div class="field">
|
||||
<div class="ui checkbox">
|
||||
<input class="hidden" name="organization" type="checkbox" tabindex="0" {{if .Webhook.Organization}}checked{{end}}>
|
||||
<label>{{.i18n.Tr "repo.settings.event_organization"}}</label>
|
||||
<span class="help">{{.i18n.Tr "repo.settings.event_organization_desc"}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- team hook -->
|
||||
<div class="seven wide column">
|
||||
<div class="field">
|
||||
<div class="ui checkbox">
|
||||
<input class="hidden" name="team" type="checkbox" tabindex="0" {{if .Webhook.Team}}checked{{end}}>
|
||||
<label>{{.i18n.Tr "repo.settings.event_team"}}</label>
|
||||
<span class="help">{{.i18n.Tr "repo.settings.event_team_desc"}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- team member -->
|
||||
<div class="seven wide column">
|
||||
<div class="field">
|
||||
<div class="ui checkbox">
|
||||
<input class="hidden" name="team_member" type="checkbox" tabindex="0" {{if .Webhook.TeamMember}}checked{{end}}>
|
||||
<label>{{.i18n.Tr "repo.settings.event_team_member"}}</label>
|
||||
<span class="help">{{.i18n.Tr "repo.settings.event_team_member_desc"}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
Reference in New Issue
Block a user
iota along makes it zero and it feels kind of weird seeing a valid state as 0