mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-26 12:27:06 +00:00 
			
		
		
		
	Add support for MS Teams webhooks (#6632)
This commit is contained in:
		 Daniel Grier
					Daniel Grier
				
			
				
					committed by
					
						 techknowlogick
						techknowlogick
					
				
			
			
				
	
			
			
			 techknowlogick
						techknowlogick
					
				
			
						parent
						
							2af67f6044
						
					
				
				
					commit
					b9d1fb6de3
				
			| @@ -122,4 +122,5 @@ _Symbols used in table:_ | |||||||
| | Two factor authentication (2FA) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | | | Two factor authentication (2FA) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | | ||||||
| | Mattermost/Slack integration | ✓ | ✓ | ⁄ | ✓ | ✓ | ⁄ | ✓ | | | Mattermost/Slack integration | ✓ | ✓ | ⁄ | ✓ | ✓ | ⁄ | ✓ | | ||||||
| | Discord integration | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ | | | Discord integration | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ | | ||||||
|  | | Microsoft Teams integration | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ | | ||||||
| | External CI/CD status display | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ | | | External CI/CD status display | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ | | ||||||
|   | |||||||
| @@ -466,6 +466,7 @@ const ( | |||||||
| 	DISCORD | 	DISCORD | ||||||
| 	DINGTALK | 	DINGTALK | ||||||
| 	TELEGRAM | 	TELEGRAM | ||||||
|  | 	MSTEAMS | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var hookTaskTypes = map[string]HookTaskType{ | var hookTaskTypes = map[string]HookTaskType{ | ||||||
| @@ -475,6 +476,7 @@ var hookTaskTypes = map[string]HookTaskType{ | |||||||
| 	"discord":  DISCORD, | 	"discord":  DISCORD, | ||||||
| 	"dingtalk": DINGTALK, | 	"dingtalk": DINGTALK, | ||||||
| 	"telegram": TELEGRAM, | 	"telegram": TELEGRAM, | ||||||
|  | 	"msteams":  MSTEAMS, | ||||||
| } | } | ||||||
|  |  | ||||||
| // ToHookTaskType returns HookTaskType by given name. | // ToHookTaskType returns HookTaskType by given name. | ||||||
| @@ -497,6 +499,8 @@ func (t HookTaskType) Name() string { | |||||||
| 		return "dingtalk" | 		return "dingtalk" | ||||||
| 	case TELEGRAM: | 	case TELEGRAM: | ||||||
| 		return "telegram" | 		return "telegram" | ||||||
|  | 	case MSTEAMS: | ||||||
|  | 		return "msteams" | ||||||
| 	} | 	} | ||||||
| 	return "" | 	return "" | ||||||
| } | } | ||||||
| @@ -675,6 +679,11 @@ func prepareWebhook(e Engine, w *Webhook, repo *Repository, event HookEventType, | |||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return fmt.Errorf("GetTelegramPayload: %v", err) | 			return fmt.Errorf("GetTelegramPayload: %v", err) | ||||||
| 		} | 		} | ||||||
|  | 	case MSTEAMS: | ||||||
|  | 		payloader, err = GetMSTeamsPayload(p, event, w.Meta) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return fmt.Errorf("GetMSTeamsPayload: %v", err) | ||||||
|  | 		} | ||||||
| 	default: | 	default: | ||||||
| 		p.SetSecret(w.Secret) | 		p.SetSecret(w.Secret) | ||||||
| 		payloader = p | 		payloader = p | ||||||
|   | |||||||
							
								
								
									
										703
									
								
								models/webhook_msteams.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										703
									
								
								models/webhook_msteams.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,703 @@ | |||||||
|  | // Copyright 2019 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 models | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/modules/git" | ||||||
|  | 	api "code.gitea.io/sdk/gitea" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type ( | ||||||
|  | 	// MSTeamsFact for Fact Structure | ||||||
|  | 	MSTeamsFact struct { | ||||||
|  | 		Name  string `json:"name"` | ||||||
|  | 		Value string `json:"value"` | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// MSTeamsSection is a MessageCard section | ||||||
|  | 	MSTeamsSection struct { | ||||||
|  | 		ActivityTitle    string        `json:"activityTitle"` | ||||||
|  | 		ActivitySubtitle string        `json:"activitySubtitle"` | ||||||
|  | 		ActivityImage    string        `json:"activityImage"` | ||||||
|  | 		Facts            []MSTeamsFact `json:"facts"` | ||||||
|  | 		Text             string        `json:"text"` | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// MSTeamsAction is an action (creates buttons, links etc) | ||||||
|  | 	MSTeamsAction struct { | ||||||
|  | 		Type    string                `json:"@type"` | ||||||
|  | 		Name    string                `json:"name"` | ||||||
|  | 		Targets []MSTeamsActionTarget `json:"targets,omitempty"` | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// MSTeamsActionTarget is the actual link to follow, etc | ||||||
|  | 	MSTeamsActionTarget struct { | ||||||
|  | 		Os  string `json:"os"` | ||||||
|  | 		URI string `json:"uri"` | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// MSTeamsPayload is the parent object | ||||||
|  | 	MSTeamsPayload struct { | ||||||
|  | 		Type            string           `json:"@type"` | ||||||
|  | 		Context         string           `json:"@context"` | ||||||
|  | 		ThemeColor      string           `json:"themeColor"` | ||||||
|  | 		Title           string           `json:"title"` | ||||||
|  | 		Summary         string           `json:"summary"` | ||||||
|  | 		Sections        []MSTeamsSection `json:"sections"` | ||||||
|  | 		PotentialAction []MSTeamsAction  `json:"potentialAction"` | ||||||
|  | 	} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // SetSecret sets the MSTeams secret | ||||||
|  | func (p *MSTeamsPayload) SetSecret(_ string) {} | ||||||
|  |  | ||||||
|  | // JSONPayload Marshals the MSTeamsPayload to json | ||||||
|  | func (p *MSTeamsPayload) JSONPayload() ([]byte, error) { | ||||||
|  | 	data, err := json.MarshalIndent(p, "", "  ") | ||||||
|  | 	if err != nil { | ||||||
|  | 		return []byte{}, err | ||||||
|  | 	} | ||||||
|  | 	return data, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getMSTeamsCreatePayload(p *api.CreatePayload) (*MSTeamsPayload, error) { | ||||||
|  | 	// created tag/branch | ||||||
|  | 	refName := git.RefEndName(p.Ref) | ||||||
|  | 	title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName) | ||||||
|  |  | ||||||
|  | 	return &MSTeamsPayload{ | ||||||
|  | 		Type:       "MessageCard", | ||||||
|  | 		Context:    "https://schema.org/extensions", | ||||||
|  | 		ThemeColor: fmt.Sprintf("%x", successColor), | ||||||
|  | 		Title:      title, | ||||||
|  | 		Summary:    title, | ||||||
|  | 		Sections: []MSTeamsSection{ | ||||||
|  | 			{ | ||||||
|  | 				ActivityTitle:    p.Sender.FullName, | ||||||
|  | 				ActivitySubtitle: p.Sender.UserName, | ||||||
|  | 				ActivityImage:    p.Sender.AvatarURL, | ||||||
|  | 				Facts: []MSTeamsFact{ | ||||||
|  | 					{ | ||||||
|  | 						Name:  "Repository:", | ||||||
|  | 						Value: p.Repo.FullName, | ||||||
|  | 					}, | ||||||
|  | 					{ | ||||||
|  | 						Name:  fmt.Sprintf("%s:", p.RefType), | ||||||
|  | 						Value: refName, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		PotentialAction: []MSTeamsAction{ | ||||||
|  | 			{ | ||||||
|  | 				Type: "OpenUri", | ||||||
|  | 				Name: "View in Gitea", | ||||||
|  | 				Targets: []MSTeamsActionTarget{ | ||||||
|  | 					{ | ||||||
|  | 						Os:  "default", | ||||||
|  | 						URI: p.Repo.HTMLURL + "/src/" + refName, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getMSTeamsDeletePayload(p *api.DeletePayload) (*MSTeamsPayload, error) { | ||||||
|  | 	// deleted tag/branch | ||||||
|  | 	refName := git.RefEndName(p.Ref) | ||||||
|  | 	title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName) | ||||||
|  |  | ||||||
|  | 	return &MSTeamsPayload{ | ||||||
|  | 		Type:       "MessageCard", | ||||||
|  | 		Context:    "https://schema.org/extensions", | ||||||
|  | 		ThemeColor: fmt.Sprintf("%x", warnColor), | ||||||
|  | 		Title:      title, | ||||||
|  | 		Summary:    title, | ||||||
|  | 		Sections: []MSTeamsSection{ | ||||||
|  | 			{ | ||||||
|  | 				ActivityTitle:    p.Sender.FullName, | ||||||
|  | 				ActivitySubtitle: p.Sender.UserName, | ||||||
|  | 				ActivityImage:    p.Sender.AvatarURL, | ||||||
|  | 				Facts: []MSTeamsFact{ | ||||||
|  | 					{ | ||||||
|  | 						Name:  "Repository:", | ||||||
|  | 						Value: p.Repo.FullName, | ||||||
|  | 					}, | ||||||
|  | 					{ | ||||||
|  | 						Name:  fmt.Sprintf("%s:", p.RefType), | ||||||
|  | 						Value: refName, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		PotentialAction: []MSTeamsAction{ | ||||||
|  | 			{ | ||||||
|  | 				Type: "OpenUri", | ||||||
|  | 				Name: "View in Gitea", | ||||||
|  | 				Targets: []MSTeamsActionTarget{ | ||||||
|  | 					{ | ||||||
|  | 						Os:  "default", | ||||||
|  | 						URI: p.Repo.HTMLURL + "/src/" + refName, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getMSTeamsForkPayload(p *api.ForkPayload) (*MSTeamsPayload, error) { | ||||||
|  | 	// fork | ||||||
|  | 	title := fmt.Sprintf("%s is forked to %s", p.Forkee.FullName, p.Repo.FullName) | ||||||
|  |  | ||||||
|  | 	return &MSTeamsPayload{ | ||||||
|  | 		Type:       "MessageCard", | ||||||
|  | 		Context:    "https://schema.org/extensions", | ||||||
|  | 		ThemeColor: fmt.Sprintf("%x", successColor), | ||||||
|  | 		Title:      title, | ||||||
|  | 		Summary:    title, | ||||||
|  | 		Sections: []MSTeamsSection{ | ||||||
|  | 			{ | ||||||
|  | 				ActivityTitle:    p.Sender.FullName, | ||||||
|  | 				ActivitySubtitle: p.Sender.UserName, | ||||||
|  | 				ActivityImage:    p.Sender.AvatarURL, | ||||||
|  | 				Facts: []MSTeamsFact{ | ||||||
|  | 					{ | ||||||
|  | 						Name:  "Forkee:", | ||||||
|  | 						Value: p.Forkee.FullName, | ||||||
|  | 					}, | ||||||
|  | 					{ | ||||||
|  | 						Name:  "Repository:", | ||||||
|  | 						Value: p.Repo.FullName, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		PotentialAction: []MSTeamsAction{ | ||||||
|  | 			{ | ||||||
|  | 				Type: "OpenUri", | ||||||
|  | 				Name: "View in Gitea", | ||||||
|  | 				Targets: []MSTeamsActionTarget{ | ||||||
|  | 					{ | ||||||
|  | 						Os:  "default", | ||||||
|  | 						URI: p.Repo.HTMLURL, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getMSTeamsPushPayload(p *api.PushPayload) (*MSTeamsPayload, error) { | ||||||
|  | 	var ( | ||||||
|  | 		branchName = git.RefEndName(p.Ref) | ||||||
|  | 		commitDesc string | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	var titleLink string | ||||||
|  | 	if len(p.Commits) == 1 { | ||||||
|  | 		commitDesc = "1 new commit" | ||||||
|  | 		titleLink = p.Commits[0].URL | ||||||
|  | 	} else { | ||||||
|  | 		commitDesc = fmt.Sprintf("%d new commits", len(p.Commits)) | ||||||
|  | 		titleLink = p.CompareURL | ||||||
|  | 	} | ||||||
|  | 	if titleLink == "" { | ||||||
|  | 		titleLink = p.Repo.HTMLURL + "/src/" + branchName | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	title := fmt.Sprintf("[%s:%s] %s", p.Repo.FullName, branchName, commitDesc) | ||||||
|  |  | ||||||
|  | 	var text string | ||||||
|  | 	// for each commit, generate attachment text | ||||||
|  | 	for i, commit := range p.Commits { | ||||||
|  | 		text += fmt.Sprintf("[%s](%s) %s - %s", commit.ID[:7], commit.URL, | ||||||
|  | 			strings.TrimRight(commit.Message, "\r\n"), commit.Author.Name) | ||||||
|  | 		// add linebreak to each commit but the last | ||||||
|  | 		if i < len(p.Commits)-1 { | ||||||
|  | 			text += "\n" | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &MSTeamsPayload{ | ||||||
|  | 		Type:       "MessageCard", | ||||||
|  | 		Context:    "https://schema.org/extensions", | ||||||
|  | 		ThemeColor: fmt.Sprintf("%x", successColor), | ||||||
|  | 		Title:      title, | ||||||
|  | 		Summary:    title, | ||||||
|  | 		Sections: []MSTeamsSection{ | ||||||
|  | 			{ | ||||||
|  | 				ActivityTitle:    p.Sender.FullName, | ||||||
|  | 				ActivitySubtitle: p.Sender.UserName, | ||||||
|  | 				ActivityImage:    p.Sender.AvatarURL, | ||||||
|  | 				Facts: []MSTeamsFact{ | ||||||
|  | 					{ | ||||||
|  | 						Name:  "Repository:", | ||||||
|  | 						Value: p.Repo.FullName, | ||||||
|  | 					}, | ||||||
|  | 					{ | ||||||
|  | 						Name:  "Commit count:", | ||||||
|  | 						Value: fmt.Sprintf("%d", len(p.Commits)), | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		PotentialAction: []MSTeamsAction{ | ||||||
|  | 			{ | ||||||
|  | 				Type: "OpenUri", | ||||||
|  | 				Name: "View in Gitea", | ||||||
|  | 				Targets: []MSTeamsActionTarget{ | ||||||
|  | 					{ | ||||||
|  | 						Os:  "default", | ||||||
|  | 						URI: titleLink, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getMSTeamsIssuesPayload(p *api.IssuePayload) (*MSTeamsPayload, error) { | ||||||
|  | 	var text, title string | ||||||
|  | 	var color int | ||||||
|  | 	url := fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index) | ||||||
|  | 	switch p.Action { | ||||||
|  | 	case api.HookIssueOpened: | ||||||
|  | 		title = fmt.Sprintf("[%s] Issue opened: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title) | ||||||
|  | 		text = p.Issue.Body | ||||||
|  | 		color = warnColor | ||||||
|  | 	case api.HookIssueClosed: | ||||||
|  | 		title = fmt.Sprintf("[%s] Issue closed: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title) | ||||||
|  | 		color = failedColor | ||||||
|  | 		text = p.Issue.Body | ||||||
|  | 	case api.HookIssueReOpened: | ||||||
|  | 		title = fmt.Sprintf("[%s] Issue re-opened: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title) | ||||||
|  | 		text = p.Issue.Body | ||||||
|  | 		color = warnColor | ||||||
|  | 	case api.HookIssueEdited: | ||||||
|  | 		title = fmt.Sprintf("[%s] Issue edited: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title) | ||||||
|  | 		text = p.Issue.Body | ||||||
|  | 		color = warnColor | ||||||
|  | 	case api.HookIssueAssigned: | ||||||
|  | 		title = fmt.Sprintf("[%s] Issue assigned to %s: #%d %s", p.Repository.FullName, | ||||||
|  | 			p.Issue.Assignee.UserName, p.Index, p.Issue.Title) | ||||||
|  | 		text = p.Issue.Body | ||||||
|  | 		color = successColor | ||||||
|  | 	case api.HookIssueUnassigned: | ||||||
|  | 		title = fmt.Sprintf("[%s] Issue unassigned: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title) | ||||||
|  | 		text = p.Issue.Body | ||||||
|  | 		color = warnColor | ||||||
|  | 	case api.HookIssueLabelUpdated: | ||||||
|  | 		title = fmt.Sprintf("[%s] Issue labels updated: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title) | ||||||
|  | 		text = p.Issue.Body | ||||||
|  | 		color = warnColor | ||||||
|  | 	case api.HookIssueLabelCleared: | ||||||
|  | 		title = fmt.Sprintf("[%s] Issue labels cleared: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title) | ||||||
|  | 		text = p.Issue.Body | ||||||
|  | 		color = warnColor | ||||||
|  | 	case api.HookIssueSynchronized: | ||||||
|  | 		title = fmt.Sprintf("[%s] Issue synchronized: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title) | ||||||
|  | 		text = p.Issue.Body | ||||||
|  | 		color = warnColor | ||||||
|  | 	case api.HookIssueMilestoned: | ||||||
|  | 		title = fmt.Sprintf("[%s] Issue milestone: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title) | ||||||
|  | 		text = p.Issue.Body | ||||||
|  | 		color = warnColor | ||||||
|  | 	case api.HookIssueDemilestoned: | ||||||
|  | 		title = fmt.Sprintf("[%s] Issue clear milestone: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title) | ||||||
|  | 		text = p.Issue.Body | ||||||
|  | 		color = warnColor | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &MSTeamsPayload{ | ||||||
|  | 		Type:       "MessageCard", | ||||||
|  | 		Context:    "https://schema.org/extensions", | ||||||
|  | 		ThemeColor: fmt.Sprintf("%x", color), | ||||||
|  | 		Title:      title, | ||||||
|  | 		Summary:    title, | ||||||
|  | 		Sections: []MSTeamsSection{ | ||||||
|  | 			{ | ||||||
|  | 				ActivityTitle:    p.Sender.FullName, | ||||||
|  | 				ActivitySubtitle: p.Sender.UserName, | ||||||
|  | 				ActivityImage:    p.Sender.AvatarURL, | ||||||
|  | 				Text:             text, | ||||||
|  | 				Facts: []MSTeamsFact{ | ||||||
|  | 					{ | ||||||
|  | 						Name:  "Repository:", | ||||||
|  | 						Value: p.Repository.FullName, | ||||||
|  | 					}, | ||||||
|  | 					{ | ||||||
|  | 						Name:  "Issue #:", | ||||||
|  | 						Value: fmt.Sprintf("%d", p.Issue.ID), | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		PotentialAction: []MSTeamsAction{ | ||||||
|  | 			{ | ||||||
|  | 				Type: "OpenUri", | ||||||
|  | 				Name: "View in Gitea", | ||||||
|  | 				Targets: []MSTeamsActionTarget{ | ||||||
|  | 					{ | ||||||
|  | 						Os:  "default", | ||||||
|  | 						URI: url, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getMSTeamsIssueCommentPayload(p *api.IssueCommentPayload) (*MSTeamsPayload, error) { | ||||||
|  | 	title := fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title) | ||||||
|  | 	url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID)) | ||||||
|  | 	content := "" | ||||||
|  | 	var color int | ||||||
|  | 	switch p.Action { | ||||||
|  | 	case api.HookIssueCommentCreated: | ||||||
|  | 		title = "New comment: " + title | ||||||
|  | 		content = p.Comment.Body | ||||||
|  | 		color = successColor | ||||||
|  | 	case api.HookIssueCommentEdited: | ||||||
|  | 		title = "Comment edited: " + title | ||||||
|  | 		content = p.Comment.Body | ||||||
|  | 		color = warnColor | ||||||
|  | 	case api.HookIssueCommentDeleted: | ||||||
|  | 		title = "Comment deleted: " + title | ||||||
|  | 		url = fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index) | ||||||
|  | 		content = p.Comment.Body | ||||||
|  | 		color = warnColor | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &MSTeamsPayload{ | ||||||
|  | 		Type:       "MessageCard", | ||||||
|  | 		Context:    "https://schema.org/extensions", | ||||||
|  | 		ThemeColor: fmt.Sprintf("%x", color), | ||||||
|  | 		Title:      title, | ||||||
|  | 		Summary:    title, | ||||||
|  | 		Sections: []MSTeamsSection{ | ||||||
|  | 			{ | ||||||
|  | 				ActivityTitle:    p.Sender.FullName, | ||||||
|  | 				ActivitySubtitle: p.Sender.UserName, | ||||||
|  | 				ActivityImage:    p.Sender.AvatarURL, | ||||||
|  | 				Text:             content, | ||||||
|  | 				Facts: []MSTeamsFact{ | ||||||
|  | 					{ | ||||||
|  | 						Name:  "Repository:", | ||||||
|  | 						Value: p.Repository.FullName, | ||||||
|  | 					}, | ||||||
|  | 					{ | ||||||
|  | 						Name:  "Issue #:", | ||||||
|  | 						Value: fmt.Sprintf("%d", p.Issue.ID), | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		PotentialAction: []MSTeamsAction{ | ||||||
|  | 			{ | ||||||
|  | 				Type: "OpenUri", | ||||||
|  | 				Name: "View in Gitea", | ||||||
|  | 				Targets: []MSTeamsActionTarget{ | ||||||
|  | 					{ | ||||||
|  | 						Os:  "default", | ||||||
|  | 						URI: url, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getMSTeamsPullRequestPayload(p *api.PullRequestPayload) (*MSTeamsPayload, error) { | ||||||
|  | 	var text, title string | ||||||
|  | 	var color int | ||||||
|  | 	switch p.Action { | ||||||
|  | 	case api.HookIssueOpened: | ||||||
|  | 		title = fmt.Sprintf("[%s] Pull request opened: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title) | ||||||
|  | 		text = p.PullRequest.Body | ||||||
|  | 		color = warnColor | ||||||
|  | 	case api.HookIssueClosed: | ||||||
|  | 		if p.PullRequest.HasMerged { | ||||||
|  | 			title = fmt.Sprintf("[%s] Pull request merged: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title) | ||||||
|  | 			color = successColor | ||||||
|  | 		} else { | ||||||
|  | 			title = fmt.Sprintf("[%s] Pull request closed: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title) | ||||||
|  | 			color = failedColor | ||||||
|  | 		} | ||||||
|  | 		text = p.PullRequest.Body | ||||||
|  | 	case api.HookIssueReOpened: | ||||||
|  | 		title = fmt.Sprintf("[%s] Pull request re-opened: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title) | ||||||
|  | 		text = p.PullRequest.Body | ||||||
|  | 		color = warnColor | ||||||
|  | 	case api.HookIssueEdited: | ||||||
|  | 		title = fmt.Sprintf("[%s] Pull request edited: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title) | ||||||
|  | 		text = p.PullRequest.Body | ||||||
|  | 		color = warnColor | ||||||
|  | 	case api.HookIssueAssigned: | ||||||
|  | 		list := make([]string, len(p.PullRequest.Assignees)) | ||||||
|  | 		for i, user := range p.PullRequest.Assignees { | ||||||
|  | 			list[i] = user.UserName | ||||||
|  | 		} | ||||||
|  | 		title = fmt.Sprintf("[%s] Pull request assigned to %s: #%d by %s", p.Repository.FullName, | ||||||
|  | 			strings.Join(list, ", "), | ||||||
|  | 			p.Index, p.PullRequest.Title) | ||||||
|  | 		text = p.PullRequest.Body | ||||||
|  | 		color = successColor | ||||||
|  | 	case api.HookIssueUnassigned: | ||||||
|  | 		title = fmt.Sprintf("[%s] Pull request unassigned: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title) | ||||||
|  | 		text = p.PullRequest.Body | ||||||
|  | 		color = warnColor | ||||||
|  | 	case api.HookIssueLabelUpdated: | ||||||
|  | 		title = fmt.Sprintf("[%s] Pull request labels updated: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title) | ||||||
|  | 		text = p.PullRequest.Body | ||||||
|  | 		color = warnColor | ||||||
|  | 	case api.HookIssueLabelCleared: | ||||||
|  | 		title = fmt.Sprintf("[%s] Pull request labels cleared: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title) | ||||||
|  | 		text = p.PullRequest.Body | ||||||
|  | 		color = warnColor | ||||||
|  | 	case api.HookIssueSynchronized: | ||||||
|  | 		title = fmt.Sprintf("[%s] Pull request synchronized: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title) | ||||||
|  | 		text = p.PullRequest.Body | ||||||
|  | 		color = warnColor | ||||||
|  | 	case api.HookIssueMilestoned: | ||||||
|  | 		title = fmt.Sprintf("[%s] Pull request milestone: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title) | ||||||
|  | 		text = p.PullRequest.Body | ||||||
|  | 		color = warnColor | ||||||
|  | 	case api.HookIssueDemilestoned: | ||||||
|  | 		title = fmt.Sprintf("[%s] Pull request clear milestone: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title) | ||||||
|  | 		text = p.PullRequest.Body | ||||||
|  | 		color = warnColor | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &MSTeamsPayload{ | ||||||
|  | 		Type:       "MessageCard", | ||||||
|  | 		Context:    "https://schema.org/extensions", | ||||||
|  | 		ThemeColor: fmt.Sprintf("%x", color), | ||||||
|  | 		Title:      title, | ||||||
|  | 		Summary:    title, | ||||||
|  | 		Sections: []MSTeamsSection{ | ||||||
|  | 			{ | ||||||
|  | 				ActivityTitle:    p.Sender.FullName, | ||||||
|  | 				ActivitySubtitle: p.Sender.UserName, | ||||||
|  | 				ActivityImage:    p.Sender.AvatarURL, | ||||||
|  | 				Text:             text, | ||||||
|  | 				Facts: []MSTeamsFact{ | ||||||
|  | 					{ | ||||||
|  | 						Name:  "Repository:", | ||||||
|  | 						Value: p.Repository.FullName, | ||||||
|  | 					}, | ||||||
|  | 					{ | ||||||
|  | 						Name:  "Pull request #:", | ||||||
|  | 						Value: fmt.Sprintf("%d", p.PullRequest.ID), | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		PotentialAction: []MSTeamsAction{ | ||||||
|  | 			{ | ||||||
|  | 				Type: "OpenUri", | ||||||
|  | 				Name: "View in Gitea", | ||||||
|  | 				Targets: []MSTeamsActionTarget{ | ||||||
|  | 					{ | ||||||
|  | 						Os:  "default", | ||||||
|  | 						URI: p.PullRequest.HTMLURL, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getMSTeamsPullRequestApprovalPayload(p *api.PullRequestPayload, event HookEventType) (*MSTeamsPayload, error) { | ||||||
|  | 	var text, title string | ||||||
|  | 	var color int | ||||||
|  | 	switch p.Action { | ||||||
|  | 	case api.HookIssueSynchronized: | ||||||
|  | 		action, err := parseHookPullRequestEventType(event) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		title = fmt.Sprintf("[%s] Pull request review %s: #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title) | ||||||
|  | 		text = p.PullRequest.Body | ||||||
|  | 		color = warnColor | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &MSTeamsPayload{ | ||||||
|  | 		Type:       "MessageCard", | ||||||
|  | 		Context:    "https://schema.org/extensions", | ||||||
|  | 		ThemeColor: fmt.Sprintf("%x", color), | ||||||
|  | 		Title:      title, | ||||||
|  | 		Summary:    title, | ||||||
|  | 		Sections: []MSTeamsSection{ | ||||||
|  | 			{ | ||||||
|  | 				ActivityTitle:    p.Sender.FullName, | ||||||
|  | 				ActivitySubtitle: p.Sender.UserName, | ||||||
|  | 				ActivityImage:    p.Sender.AvatarURL, | ||||||
|  | 				Text:             text, | ||||||
|  | 				Facts: []MSTeamsFact{ | ||||||
|  | 					{ | ||||||
|  | 						Name:  "Repository:", | ||||||
|  | 						Value: p.Repository.FullName, | ||||||
|  | 					}, | ||||||
|  | 					{ | ||||||
|  | 						Name:  "Pull request #:", | ||||||
|  | 						Value: fmt.Sprintf("%d", p.PullRequest.ID), | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		PotentialAction: []MSTeamsAction{ | ||||||
|  | 			{ | ||||||
|  | 				Type: "OpenUri", | ||||||
|  | 				Name: "View in Gitea", | ||||||
|  | 				Targets: []MSTeamsActionTarget{ | ||||||
|  | 					{ | ||||||
|  | 						Os:  "default", | ||||||
|  | 						URI: p.PullRequest.HTMLURL, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getMSTeamsRepositoryPayload(p *api.RepositoryPayload) (*MSTeamsPayload, 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 &MSTeamsPayload{ | ||||||
|  | 		Type:       "MessageCard", | ||||||
|  | 		Context:    "https://schema.org/extensions", | ||||||
|  | 		ThemeColor: fmt.Sprintf("%x", color), | ||||||
|  | 		Title:      title, | ||||||
|  | 		Summary:    title, | ||||||
|  | 		Sections: []MSTeamsSection{ | ||||||
|  | 			{ | ||||||
|  | 				ActivityTitle:    p.Sender.FullName, | ||||||
|  | 				ActivitySubtitle: p.Sender.UserName, | ||||||
|  | 				ActivityImage:    p.Sender.AvatarURL, | ||||||
|  | 				Facts: []MSTeamsFact{ | ||||||
|  | 					{ | ||||||
|  | 						Name:  "Repository:", | ||||||
|  | 						Value: p.Repository.FullName, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		PotentialAction: []MSTeamsAction{ | ||||||
|  | 			{ | ||||||
|  | 				Type: "OpenUri", | ||||||
|  | 				Name: "View in Gitea", | ||||||
|  | 				Targets: []MSTeamsActionTarget{ | ||||||
|  | 					{ | ||||||
|  | 						Os:  "default", | ||||||
|  | 						URI: url, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getMSTeamsReleasePayload(p *api.ReleasePayload) (*MSTeamsPayload, error) { | ||||||
|  | 	var title, url string | ||||||
|  | 	var color int | ||||||
|  | 	switch p.Action { | ||||||
|  | 	case api.HookReleasePublished: | ||||||
|  | 		title = fmt.Sprintf("[%s] Release created", p.Release.TagName) | ||||||
|  | 		url = p.Release.URL | ||||||
|  | 		color = successColor | ||||||
|  | 	case api.HookReleaseUpdated: | ||||||
|  | 		title = fmt.Sprintf("[%s] Release updated", p.Release.TagName) | ||||||
|  | 		url = p.Release.URL | ||||||
|  | 		color = successColor | ||||||
|  | 	case api.HookReleaseDeleted: | ||||||
|  | 		title = fmt.Sprintf("[%s] Release deleted", p.Release.TagName) | ||||||
|  | 		url = p.Release.URL | ||||||
|  | 		color = successColor | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &MSTeamsPayload{ | ||||||
|  | 		Type:       "MessageCard", | ||||||
|  | 		Context:    "https://schema.org/extensions", | ||||||
|  | 		ThemeColor: fmt.Sprintf("%x", color), | ||||||
|  | 		Title:      title, | ||||||
|  | 		Summary:    title, | ||||||
|  | 		Sections: []MSTeamsSection{ | ||||||
|  | 			{ | ||||||
|  | 				ActivityTitle:    p.Sender.FullName, | ||||||
|  | 				ActivitySubtitle: p.Sender.UserName, | ||||||
|  | 				ActivityImage:    p.Sender.AvatarURL, | ||||||
|  | 				Text:             p.Release.Note, | ||||||
|  | 				Facts: []MSTeamsFact{ | ||||||
|  | 					{ | ||||||
|  | 						Name:  "Repository:", | ||||||
|  | 						Value: p.Repository.FullName, | ||||||
|  | 					}, | ||||||
|  | 					{ | ||||||
|  | 						Name:  "Tag:", | ||||||
|  | 						Value: p.Release.TagName, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		PotentialAction: []MSTeamsAction{ | ||||||
|  | 			{ | ||||||
|  | 				Type: "OpenUri", | ||||||
|  | 				Name: "View in Gitea", | ||||||
|  | 				Targets: []MSTeamsActionTarget{ | ||||||
|  | 					{ | ||||||
|  | 						Os:  "default", | ||||||
|  | 						URI: url, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetMSTeamsPayload converts a MSTeams webhook into a MSTeamsPayload | ||||||
|  | func GetMSTeamsPayload(p api.Payloader, event HookEventType, meta string) (*MSTeamsPayload, error) { | ||||||
|  | 	s := new(MSTeamsPayload) | ||||||
|  |  | ||||||
|  | 	switch event { | ||||||
|  | 	case HookEventCreate: | ||||||
|  | 		return getMSTeamsCreatePayload(p.(*api.CreatePayload)) | ||||||
|  | 	case HookEventDelete: | ||||||
|  | 		return getMSTeamsDeletePayload(p.(*api.DeletePayload)) | ||||||
|  | 	case HookEventFork: | ||||||
|  | 		return getMSTeamsForkPayload(p.(*api.ForkPayload)) | ||||||
|  | 	case HookEventIssues: | ||||||
|  | 		return getMSTeamsIssuesPayload(p.(*api.IssuePayload)) | ||||||
|  | 	case HookEventIssueComment: | ||||||
|  | 		return getMSTeamsIssueCommentPayload(p.(*api.IssueCommentPayload)) | ||||||
|  | 	case HookEventPush: | ||||||
|  | 		return getMSTeamsPushPayload(p.(*api.PushPayload)) | ||||||
|  | 	case HookEventPullRequest: | ||||||
|  | 		return getMSTeamsPullRequestPayload(p.(*api.PullRequestPayload)) | ||||||
|  | 	case HookEventPullRequestRejected, HookEventPullRequestApproved, HookEventPullRequestComment: | ||||||
|  | 		return getMSTeamsPullRequestApprovalPayload(p.(*api.PullRequestPayload), event) | ||||||
|  | 	case HookEventRepository: | ||||||
|  | 		return getMSTeamsRepositoryPayload(p.(*api.RepositoryPayload)) | ||||||
|  | 	case HookEventRelease: | ||||||
|  | 		return getMSTeamsReleasePayload(p.(*api.ReleasePayload)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return s, nil | ||||||
|  | } | ||||||
| @@ -275,6 +275,17 @@ func (f *NewTelegramHookForm) Validate(ctx *macaron.Context, errs binding.Errors | |||||||
| 	return validate(errs, ctx.Data, f, ctx.Locale) | 	return validate(errs, ctx.Data, f, ctx.Locale) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // NewMSTeamsHookForm form for creating MS Teams hook | ||||||
|  | type NewMSTeamsHookForm struct { | ||||||
|  | 	PayloadURL string `binding:"Required;ValidUrl"` | ||||||
|  | 	WebhookForm | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Validate validates the fields | ||||||
|  | func (f *NewMSTeamsHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | ||||||
|  | 	return validate(errs, ctx.Data, f, ctx.Locale) | ||||||
|  | } | ||||||
|  |  | ||||||
| // .___ | // .___ | ||||||
| // |   | ______ ________ __   ____ | // |   | ______ ________ __   ____ | ||||||
| // |   |/  ___//  ___/  |  \_/ __ \ | // |   |/  ___//  ___/  |  \_/ __ \ | ||||||
|   | |||||||
| @@ -25,6 +25,6 @@ func newWebhookService() { | |||||||
| 	Webhook.QueueLength = sec.Key("QUEUE_LENGTH").MustInt(1000) | 	Webhook.QueueLength = sec.Key("QUEUE_LENGTH").MustInt(1000) | ||||||
| 	Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5) | 	Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5) | ||||||
| 	Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool() | 	Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool() | ||||||
| 	Webhook.Types = []string{"gitea", "gogs", "slack", "discord", "dingtalk", "telegram"} | 	Webhook.Types = []string{"gitea", "gogs", "slack", "discord", "dingtalk", "telegram", "msteams"} | ||||||
| 	Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10) | 	Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1212,6 +1212,7 @@ settings.slack_channel = Channel | |||||||
| settings.add_discord_hook_desc = Integrate <a href="%s">Discord</a> into your repository. | settings.add_discord_hook_desc = Integrate <a href="%s">Discord</a> into your repository. | ||||||
| settings.add_dingtalk_hook_desc = Integrate <a href="%s">Dingtalk</a> into your repository. | settings.add_dingtalk_hook_desc = Integrate <a href="%s">Dingtalk</a> into your repository. | ||||||
| settings.add_telegram_hook_desc = Integrate <a href="%s">Telegram</a> into your repository. | settings.add_telegram_hook_desc = Integrate <a href="%s">Telegram</a> into your repository. | ||||||
|  | settings.add_msteams_hook_desc = Integrate <a href="%s">Microsoft Teams</a> into your repository. | ||||||
| settings.deploy_keys = Deploy Keys | settings.deploy_keys = Deploy Keys | ||||||
| settings.add_deploy_key = Add Deploy Key | settings.add_deploy_key = Add Deploy Key | ||||||
| settings.deploy_key_desc = Deploy keys have read-only pull access to the repository. | settings.deploy_key_desc = Deploy keys have read-only pull access to the repository. | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								public/img/msteams.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/img/msteams.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 6.0 KiB | 
| @@ -380,6 +380,46 @@ func TelegramHooksNewPost(ctx *context.Context, form auth.NewTelegramHookForm) { | |||||||
| 	ctx.Redirect(orCtx.Link) | 	ctx.Redirect(orCtx.Link) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // MSTeamsHooksNewPost response for creating MS Teams hook | ||||||
|  | func MSTeamsHooksNewPost(ctx *context.Context, form auth.NewMSTeamsHookForm) { | ||||||
|  | 	ctx.Data["Title"] = ctx.Tr("repo.settings") | ||||||
|  | 	ctx.Data["PageIsSettingsHooks"] = true | ||||||
|  | 	ctx.Data["PageIsSettingsHooksNew"] = true | ||||||
|  | 	ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}} | ||||||
|  |  | ||||||
|  | 	orCtx, err := getOrgRepoCtx(ctx) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.ServerError("getOrgRepoCtx", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if ctx.HasError() { | ||||||
|  | 		ctx.HTML(200, orCtx.NewTemplate) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	w := &models.Webhook{ | ||||||
|  | 		RepoID:       orCtx.RepoID, | ||||||
|  | 		URL:          form.PayloadURL, | ||||||
|  | 		ContentType:  models.ContentTypeJSON, | ||||||
|  | 		HookEvent:    ParseHookEvent(form.WebhookForm), | ||||||
|  | 		IsActive:     form.Active, | ||||||
|  | 		HookTaskType: models.MSTEAMS, | ||||||
|  | 		Meta:         "", | ||||||
|  | 		OrgID:        orCtx.OrgID, | ||||||
|  | 	} | ||||||
|  | 	if err := w.UpdateEvent(); err != nil { | ||||||
|  | 		ctx.ServerError("UpdateEvent", err) | ||||||
|  | 		return | ||||||
|  | 	} else if err := models.CreateWebhook(w); err != nil { | ||||||
|  | 		ctx.ServerError("CreateWebhook", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success")) | ||||||
|  | 	ctx.Redirect(orCtx.Link) | ||||||
|  | } | ||||||
|  |  | ||||||
| // SlackHooksNewPost response for creating slack hook | // SlackHooksNewPost response for creating slack hook | ||||||
| func SlackHooksNewPost(ctx *context.Context, form auth.NewSlackHookForm) { | func SlackHooksNewPost(ctx *context.Context, form auth.NewSlackHookForm) { | ||||||
| 	ctx.Data["Title"] = ctx.Tr("repo.settings") | 	ctx.Data["Title"] = ctx.Tr("repo.settings") | ||||||
| @@ -738,6 +778,38 @@ func TelegramHooksEditPost(ctx *context.Context, form auth.NewTelegramHookForm) | |||||||
| 	ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID)) | 	ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID)) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // MSTeamsHooksEditPost response for editing MS Teams hook | ||||||
|  | func MSTeamsHooksEditPost(ctx *context.Context, form auth.NewMSTeamsHookForm) { | ||||||
|  | 	ctx.Data["Title"] = ctx.Tr("repo.settings") | ||||||
|  | 	ctx.Data["PageIsSettingsHooks"] = true | ||||||
|  | 	ctx.Data["PageIsSettingsHooksEdit"] = true | ||||||
|  |  | ||||||
|  | 	orCtx, w := checkWebhook(ctx) | ||||||
|  | 	if ctx.Written() { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	ctx.Data["Webhook"] = w | ||||||
|  |  | ||||||
|  | 	if ctx.HasError() { | ||||||
|  | 		ctx.HTML(200, orCtx.NewTemplate) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	w.URL = form.PayloadURL | ||||||
|  | 	w.HookEvent = ParseHookEvent(form.WebhookForm) | ||||||
|  | 	w.IsActive = form.Active | ||||||
|  | 	if err := w.UpdateEvent(); err != nil { | ||||||
|  | 		ctx.ServerError("UpdateEvent", err) | ||||||
|  | 		return | ||||||
|  | 	} else if err := models.UpdateWebhook(w); err != nil { | ||||||
|  | 		ctx.ServerError("UpdateWebhook", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success")) | ||||||
|  | 	ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID)) | ||||||
|  | } | ||||||
|  |  | ||||||
| // TestWebhook test if web hook is work fine | // TestWebhook test if web hook is work fine | ||||||
| func TestWebhook(ctx *context.Context) { | func TestWebhook(ctx *context.Context) { | ||||||
| 	hookID := ctx.ParamsInt64(":id") | 	hookID := ctx.ParamsInt64(":id") | ||||||
|   | |||||||
| @@ -422,12 +422,14 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||||
| 			m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost) | 			m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost) | ||||||
| 			m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost) | 			m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost) | ||||||
| 			m.Post("/dingtalk/new", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost) | 			m.Post("/dingtalk/new", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost) | ||||||
|  | 			m.Post("/msteams/new", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost) | ||||||
| 			m.Get("/:id", repo.WebHooksEdit) | 			m.Get("/:id", repo.WebHooksEdit) | ||||||
| 			m.Post("/gitea/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) | 			m.Post("/gitea/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) | ||||||
| 			m.Post("/gogs/:id", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksEditPost) | 			m.Post("/gogs/:id", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksEditPost) | ||||||
| 			m.Post("/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost) | 			m.Post("/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost) | ||||||
| 			m.Post("/discord/:id", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost) | 			m.Post("/discord/:id", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost) | ||||||
| 			m.Post("/dingtalk/:id", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) | 			m.Post("/dingtalk/:id", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) | ||||||
|  | 			m.Post("/msteams/:id", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost) | ||||||
| 		}) | 		}) | ||||||
|  |  | ||||||
| 		m.Group("/auths", func() { | 		m.Group("/auths", func() { | ||||||
| @@ -615,6 +617,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||||
| 				m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost) | 				m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost) | ||||||
| 				m.Post("/dingtalk/new", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost) | 				m.Post("/dingtalk/new", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost) | ||||||
| 				m.Post("/telegram/new", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksNewPost) | 				m.Post("/telegram/new", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksNewPost) | ||||||
|  | 				m.Post("/msteams/new", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost) | ||||||
| 				m.Get("/:id", repo.WebHooksEdit) | 				m.Get("/:id", repo.WebHooksEdit) | ||||||
| 				m.Post("/:id/test", repo.TestWebhook) | 				m.Post("/:id/test", repo.TestWebhook) | ||||||
| 				m.Post("/gitea/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) | 				m.Post("/gitea/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) | ||||||
| @@ -623,6 +626,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||||
| 				m.Post("/discord/:id", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost) | 				m.Post("/discord/:id", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost) | ||||||
| 				m.Post("/dingtalk/:id", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) | 				m.Post("/dingtalk/:id", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) | ||||||
| 				m.Post("/telegram/:id", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksEditPost) | 				m.Post("/telegram/:id", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksEditPost) | ||||||
|  | 				m.Post("/msteams/:id", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost) | ||||||
|  |  | ||||||
| 				m.Group("/git", func() { | 				m.Group("/git", func() { | ||||||
| 					m.Get("", repo.GitHooks) | 					m.Get("", repo.GitHooks) | ||||||
|   | |||||||
| @@ -20,6 +20,8 @@ | |||||||
| 					<img class="img-13" src="{{AppSubUrl}}/img/discord.png"> | 					<img class="img-13" src="{{AppSubUrl}}/img/discord.png"> | ||||||
| 				{{else if eq .HookType "dingtalk"}} | 				{{else if eq .HookType "dingtalk"}} | ||||||
| 					<img class="img-13" src="{{AppSubUrl}}/img/dingtalk.ico"> | 					<img class="img-13" src="{{AppSubUrl}}/img/dingtalk.ico"> | ||||||
|  | 				{{else if eq .HookType "msteams"}} | ||||||
|  | 					<img class="img-13" src="{{AppSubUrl}}/img/msteams.png"> | ||||||
| 				{{end}} | 				{{end}} | ||||||
| 			</div> | 			</div> | ||||||
| 		</h4> | 		</h4> | ||||||
| @@ -29,6 +31,7 @@ | |||||||
| 			{{template "repo/settings/webhook/slack" .}} | 			{{template "repo/settings/webhook/slack" .}} | ||||||
| 			{{template "repo/settings/webhook/discord" .}} | 			{{template "repo/settings/webhook/discord" .}} | ||||||
| 			{{template "repo/settings/webhook/dingtalk" .}} | 			{{template "repo/settings/webhook/dingtalk" .}} | ||||||
|  | 			{{template "repo/settings/webhook/msteams" .}} | ||||||
| 		</div> | 		</div> | ||||||
|  |  | ||||||
| 		{{template "repo/settings/webhook/history" .}} | 		{{template "repo/settings/webhook/history" .}} | ||||||
|   | |||||||
| @@ -21,6 +21,8 @@ | |||||||
| 							<img class="img-13" src="{{AppSubUrl}}/img/dingtalk.png"> | 							<img class="img-13" src="{{AppSubUrl}}/img/dingtalk.png"> | ||||||
| 						{{else if eq .HookType "telegram"}} | 						{{else if eq .HookType "telegram"}} | ||||||
| 							<img class="img-13" src="{{AppSubUrl}}/img/telegram.png"> | 							<img class="img-13" src="{{AppSubUrl}}/img/telegram.png"> | ||||||
|  | 						{{else if eq .HookType "msteams"}} | ||||||
|  | 							<img class="img-13" src="{{AppSubUrl}}/img/msteams.png"> | ||||||
| 						{{end}} | 						{{end}} | ||||||
| 					</div> | 					</div> | ||||||
| 				</h4> | 				</h4> | ||||||
| @@ -31,6 +33,7 @@ | |||||||
| 					{{template "repo/settings/webhook/discord" .}} | 					{{template "repo/settings/webhook/discord" .}} | ||||||
| 					{{template "repo/settings/webhook/dingtalk" .}} | 					{{template "repo/settings/webhook/dingtalk" .}} | ||||||
| 					{{template "repo/settings/webhook/telegram" .}} | 					{{template "repo/settings/webhook/telegram" .}} | ||||||
|  | 					{{template "repo/settings/webhook/msteams" .}} | ||||||
| 				</div> | 				</div> | ||||||
|  |  | ||||||
| 				{{template "repo/settings/webhook/history" .}} | 				{{template "repo/settings/webhook/history" .}} | ||||||
|   | |||||||
| @@ -23,6 +23,9 @@ | |||||||
| 				<a class="item" href="{{.BaseLink}}/telegram/new"> | 				<a class="item" href="{{.BaseLink}}/telegram/new"> | ||||||
| 					<img class="img-10" src="{{AppSubUrl}}/img/telegram.png">Telegram | 					<img class="img-10" src="{{AppSubUrl}}/img/telegram.png">Telegram | ||||||
| 				</a> | 				</a> | ||||||
|  | 				<a class="item" href="{{.BaseLink}}/msteams/new"> | ||||||
|  | 					<img class="img-10" src="{{AppSubUrl}}/img/msteams.png">Microsoft Teams | ||||||
|  | 				</a> | ||||||
| 			</div> | 			</div> | ||||||
| 		</div> | 		</div> | ||||||
| 	</div> | 	</div> | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								templates/repo/settings/webhook/msteams.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								templates/repo/settings/webhook/msteams.tmpl
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | {{if eq .HookType "msteams"}} | ||||||
|  | 	<p>{{.i18n.Tr "repo.settings.add_msteams_hook_desc" "https://teams.microsoft.com" | Str2html}}</p> | ||||||
|  | 	<form class="ui form" action="{{.BaseLink}}/msteams/{{or .Webhook.ID "new"}}" method="post"> | ||||||
|  | 		{{.CsrfTokenHtml}} | ||||||
|  | 		<div class="required field {{if .Err_PayloadURL}}error{{end}}"> | ||||||
|  | 			<label for="payload_url">{{.i18n.Tr "repo.settings.payload_url"}}</label> | ||||||
|  | 			<input id="payload_url" name="payload_url" type="url" value="{{.Webhook.URL}}" autofocus required> | ||||||
|  | 		</div> | ||||||
|  | 		{{template "repo/settings/webhook/settings" .}} | ||||||
|  | 	</form> | ||||||
|  | {{end}} | ||||||
| @@ -19,6 +19,8 @@ | |||||||
| 					<img class="img-13" src="{{AppSubUrl}}/img/dingtalk.ico"> | 					<img class="img-13" src="{{AppSubUrl}}/img/dingtalk.ico"> | ||||||
| 				{{else if eq .HookType "telegram"}} | 				{{else if eq .HookType "telegram"}} | ||||||
| 					<img class="img-13" src="{{AppSubUrl}}/img/telegram.png"> | 					<img class="img-13" src="{{AppSubUrl}}/img/telegram.png"> | ||||||
|  | 				{{else if eq .HookType "msteams"}} | ||||||
|  | 					<img class="img-13" src="{{AppSubUrl}}/img/msteams.png"> | ||||||
| 				{{end}} | 				{{end}} | ||||||
| 			</div> | 			</div> | ||||||
| 		</h4> | 		</h4> | ||||||
| @@ -29,6 +31,7 @@ | |||||||
| 			{{template "repo/settings/webhook/discord" .}} | 			{{template "repo/settings/webhook/discord" .}} | ||||||
| 			{{template "repo/settings/webhook/dingtalk" .}} | 			{{template "repo/settings/webhook/dingtalk" .}} | ||||||
| 			{{template "repo/settings/webhook/telegram" .}} | 			{{template "repo/settings/webhook/telegram" .}} | ||||||
|  | 			{{template "repo/settings/webhook/msteams" .}} | ||||||
| 		</div> | 		</div> | ||||||
|  |  | ||||||
| 		{{template "repo/settings/webhook/history" .}} | 		{{template "repo/settings/webhook/history" .}} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user