Merge branch 'main' into lunny/glob_protected_branch_rule

This commit is contained in:
Lunny Xiao 2022-10-22 17:59:34 +08:00
commit 3425685a81
54 changed files with 458 additions and 301 deletions

View file

@ -1,6 +1,5 @@
- -
id: 1 id: 1
repo_id: 1
hook_id: 1 hook_id: 1
uuid: uuid1 uuid: uuid1
is_delivered: true is_delivered: true

View file

@ -123,6 +123,11 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
return err return err
} }
if _, err := db.GetEngine(ctx).In("hook_id", builder.Select("id").From("webhook").Where(builder.Eq{"webhook.repo_id": repo.ID})).
Delete(&webhook.HookTask{}); err != nil {
return err
}
if err := db.DeleteBeans(ctx, if err := db.DeleteBeans(ctx,
&access_model.Access{RepoID: repo.ID}, &access_model.Access{RepoID: repo.ID},
&activities_model.Action{RepoID: repo.ID}, &activities_model.Action{RepoID: repo.ID},
@ -130,7 +135,6 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
&issues_model.Comment{RefRepoID: repoID}, &issues_model.Comment{RefRepoID: repoID},
&git_model.CommitStatus{RepoID: repoID}, &git_model.CommitStatus{RepoID: repoID},
&git_model.DeletedBranch{RepoID: repoID}, &git_model.DeletedBranch{RepoID: repoID},
&webhook.HookTask{RepoID: repoID},
&git_model.LFSLock{RepoID: repoID}, &git_model.LFSLock{RepoID: repoID},
&repo_model.LanguageStat{RepoID: repoID}, &repo_model.LanguageStat{RepoID: repoID},
&issues_model.Milestone{RepoID: repoID}, &issues_model.Milestone{RepoID: repoID},

View file

@ -104,7 +104,6 @@ type HookResponse struct {
// HookTask represents a hook task. // HookTask represents a hook task.
type HookTask struct { type HookTask struct {
ID int64 `xorm:"pk autoincr"` ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"INDEX"`
HookID int64 HookID int64
UUID string UUID string
api.Payloader `xorm:"-"` api.Payloader `xorm:"-"`
@ -178,14 +177,29 @@ func HookTasks(hookID int64, page int) ([]*HookTask, error) {
// CreateHookTask creates a new hook task, // CreateHookTask creates a new hook task,
// it handles conversion from Payload to PayloadContent. // it handles conversion from Payload to PayloadContent.
func CreateHookTask(t *HookTask) error { func CreateHookTask(ctx context.Context, t *HookTask) (*HookTask, error) {
data, err := t.Payloader.JSONPayload() data, err := t.Payloader.JSONPayload()
if err != nil { if err != nil {
return err return nil, err
} }
t.UUID = gouuid.New().String() t.UUID = gouuid.New().String()
t.PayloadContent = string(data) t.PayloadContent = string(data)
return db.Insert(db.DefaultContext, t) return t, db.Insert(ctx, t)
}
func GetHookTaskByID(ctx context.Context, id int64) (*HookTask, error) {
t := &HookTask{}
has, err := db.GetEngine(ctx).ID(id).Get(t)
if err != nil {
return nil, err
}
if !has {
return nil, ErrHookTaskNotExist{
TaskID: id,
}
}
return t, nil
} }
// UpdateHookTask updates information of hook task. // UpdateHookTask updates information of hook task.
@ -195,53 +209,36 @@ func UpdateHookTask(t *HookTask) error {
} }
// ReplayHookTask copies a hook task to get re-delivered // ReplayHookTask copies a hook task to get re-delivered
func ReplayHookTask(hookID int64, uuid string) (*HookTask, error) { func ReplayHookTask(ctx context.Context, hookID int64, uuid string) (*HookTask, error) {
var newTask *HookTask
err := db.WithTx(func(ctx context.Context) error {
task := &HookTask{ task := &HookTask{
HookID: hookID, HookID: hookID,
UUID: uuid, UUID: uuid,
} }
has, err := db.GetByBean(ctx, task) has, err := db.GetByBean(ctx, task)
if err != nil { if err != nil {
return err return nil, err
} else if !has { } else if !has {
return ErrHookTaskNotExist{ return nil, ErrHookTaskNotExist{
HookID: hookID, HookID: hookID,
UUID: uuid, UUID: uuid,
} }
} }
newTask = &HookTask{ newTask := &HookTask{
UUID: gouuid.New().String(), UUID: gouuid.New().String(),
RepoID: task.RepoID,
HookID: task.HookID, HookID: task.HookID,
PayloadContent: task.PayloadContent, PayloadContent: task.PayloadContent,
EventType: task.EventType, EventType: task.EventType,
} }
return db.Insert(ctx, newTask) return newTask, db.Insert(ctx, newTask)
})
return newTask, err
} }
// FindUndeliveredHookTasks represents find the undelivered hook tasks // FindUndeliveredHookTasks represents find the undelivered hook tasks
func FindUndeliveredHookTasks() ([]*HookTask, error) { func FindUndeliveredHookTasks(ctx context.Context) ([]*HookTask, error) {
tasks := make([]*HookTask, 0, 10) tasks := make([]*HookTask, 0, 10)
if err := db.GetEngine(db.DefaultContext).Where("is_delivered=?", false).Find(&tasks); err != nil { return tasks, db.GetEngine(ctx).
return nil, err Where("is_delivered=?", false).
} Find(&tasks)
return tasks, nil
}
// FindRepoUndeliveredHookTasks represents find the undelivered hook tasks of one repository
func FindRepoUndeliveredHookTasks(repoID int64) ([]*HookTask, error) {
tasks := make([]*HookTask, 0, 5)
if err := db.GetEngine(db.DefaultContext).Where("repo_id=? AND is_delivered=?", repoID, false).Find(&tasks); err != nil {
return nil, err
}
return tasks, nil
} }
// CleanupHookTaskTable deletes rows from hook_task as needed. // CleanupHookTaskTable deletes rows from hook_task as needed.
@ -250,7 +247,7 @@ func CleanupHookTaskTable(ctx context.Context, cleanupType HookTaskCleanupType,
if cleanupType == OlderThan { if cleanupType == OlderThan {
deleteOlderThan := time.Now().Add(-olderThan).UnixNano() deleteOlderThan := time.Now().Add(-olderThan).UnixNano()
deletes, err := db.GetEngine(db.DefaultContext). deletes, err := db.GetEngine(ctx).
Where("is_delivered = ? and delivered < ?", true, deleteOlderThan). Where("is_delivered = ? and delivered < ?", true, deleteOlderThan).
Delete(new(HookTask)) Delete(new(HookTask))
if err != nil { if err != nil {
@ -259,7 +256,8 @@ func CleanupHookTaskTable(ctx context.Context, cleanupType HookTaskCleanupType,
log.Trace("Deleted %d rows from hook_task", deletes) log.Trace("Deleted %d rows from hook_task", deletes)
} else if cleanupType == PerWebhook { } else if cleanupType == PerWebhook {
hookIDs := make([]int64, 0, 10) hookIDs := make([]int64, 0, 10)
err := db.GetEngine(db.DefaultContext).Table("webhook"). err := db.GetEngine(ctx).
Table("webhook").
Where("id > 0"). Where("id > 0").
Cols("id"). Cols("id").
Find(&hookIDs) Find(&hookIDs)

View file

@ -19,13 +19,6 @@ import (
"xorm.io/builder" "xorm.io/builder"
) )
// __ __ ___. .__ __
// / \ / \ ____\_ |__ | |__ ____ ____ | | __
// \ \/\/ // __ \| __ \| | \ / _ \ / _ \| |/ /
// \ /\ ___/| \_\ \ Y ( <_> | <_> ) <
// \__/\ / \___ >___ /___| /\____/ \____/|__|_ \
// \/ \/ \/ \/ \/
// ErrWebhookNotExist represents a "WebhookNotExist" kind of error. // ErrWebhookNotExist represents a "WebhookNotExist" kind of error.
type ErrWebhookNotExist struct { type ErrWebhookNotExist struct {
ID int64 ID int64
@ -47,6 +40,7 @@ func (err ErrWebhookNotExist) Unwrap() error {
// ErrHookTaskNotExist represents a "HookTaskNotExist" kind of error. // ErrHookTaskNotExist represents a "HookTaskNotExist" kind of error.
type ErrHookTaskNotExist struct { type ErrHookTaskNotExist struct {
TaskID int64
HookID int64 HookID int64
UUID string UUID string
} }
@ -58,7 +52,7 @@ func IsErrHookTaskNotExist(err error) bool {
} }
func (err ErrHookTaskNotExist) Error() string { func (err ErrHookTaskNotExist) Error() string {
return fmt.Sprintf("hook task does not exist [hook: %d, uuid: %s]", err.HookID, err.UUID) return fmt.Sprintf("hook task does not exist [task: %d, hook: %d, uuid: %s]", err.TaskID, err.HookID, err.UUID)
} }
func (err ErrHookTaskNotExist) Unwrap() error { func (err ErrHookTaskNotExist) Unwrap() error {

View file

@ -208,12 +208,12 @@ func TestHookTasks(t *testing.T) {
func TestCreateHookTask(t *testing.T) { func TestCreateHookTask(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
hookTask := &HookTask{ hookTask := &HookTask{
RepoID: 3,
HookID: 3, HookID: 3,
Payloader: &api.PushPayload{}, Payloader: &api.PushPayload{},
} }
unittest.AssertNotExistsBean(t, hookTask) unittest.AssertNotExistsBean(t, hookTask)
assert.NoError(t, CreateHookTask(hookTask)) _, err := CreateHookTask(db.DefaultContext, hookTask)
assert.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, hookTask) unittest.AssertExistsAndLoadBean(t, hookTask)
} }
@ -232,14 +232,14 @@ func TestUpdateHookTask(t *testing.T) {
func TestCleanupHookTaskTable_PerWebhook_DeletesDelivered(t *testing.T) { func TestCleanupHookTaskTable_PerWebhook_DeletesDelivered(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
hookTask := &HookTask{ hookTask := &HookTask{
RepoID: 3,
HookID: 3, HookID: 3,
Payloader: &api.PushPayload{}, Payloader: &api.PushPayload{},
IsDelivered: true, IsDelivered: true,
Delivered: time.Now().UnixNano(), Delivered: time.Now().UnixNano(),
} }
unittest.AssertNotExistsBean(t, hookTask) unittest.AssertNotExistsBean(t, hookTask)
assert.NoError(t, CreateHookTask(hookTask)) _, err := CreateHookTask(db.DefaultContext, hookTask)
assert.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, hookTask) unittest.AssertExistsAndLoadBean(t, hookTask)
assert.NoError(t, CleanupHookTaskTable(context.Background(), PerWebhook, 168*time.Hour, 0)) assert.NoError(t, CleanupHookTaskTable(context.Background(), PerWebhook, 168*time.Hour, 0))
@ -249,13 +249,13 @@ func TestCleanupHookTaskTable_PerWebhook_DeletesDelivered(t *testing.T) {
func TestCleanupHookTaskTable_PerWebhook_LeavesUndelivered(t *testing.T) { func TestCleanupHookTaskTable_PerWebhook_LeavesUndelivered(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
hookTask := &HookTask{ hookTask := &HookTask{
RepoID: 2,
HookID: 4, HookID: 4,
Payloader: &api.PushPayload{}, Payloader: &api.PushPayload{},
IsDelivered: false, IsDelivered: false,
} }
unittest.AssertNotExistsBean(t, hookTask) unittest.AssertNotExistsBean(t, hookTask)
assert.NoError(t, CreateHookTask(hookTask)) _, err := CreateHookTask(db.DefaultContext, hookTask)
assert.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, hookTask) unittest.AssertExistsAndLoadBean(t, hookTask)
assert.NoError(t, CleanupHookTaskTable(context.Background(), PerWebhook, 168*time.Hour, 0)) assert.NoError(t, CleanupHookTaskTable(context.Background(), PerWebhook, 168*time.Hour, 0))
@ -265,14 +265,14 @@ func TestCleanupHookTaskTable_PerWebhook_LeavesUndelivered(t *testing.T) {
func TestCleanupHookTaskTable_PerWebhook_LeavesMostRecentTask(t *testing.T) { func TestCleanupHookTaskTable_PerWebhook_LeavesMostRecentTask(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
hookTask := &HookTask{ hookTask := &HookTask{
RepoID: 2,
HookID: 4, HookID: 4,
Payloader: &api.PushPayload{}, Payloader: &api.PushPayload{},
IsDelivered: true, IsDelivered: true,
Delivered: time.Now().UnixNano(), Delivered: time.Now().UnixNano(),
} }
unittest.AssertNotExistsBean(t, hookTask) unittest.AssertNotExistsBean(t, hookTask)
assert.NoError(t, CreateHookTask(hookTask)) _, err := CreateHookTask(db.DefaultContext, hookTask)
assert.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, hookTask) unittest.AssertExistsAndLoadBean(t, hookTask)
assert.NoError(t, CleanupHookTaskTable(context.Background(), PerWebhook, 168*time.Hour, 1)) assert.NoError(t, CleanupHookTaskTable(context.Background(), PerWebhook, 168*time.Hour, 1))
@ -282,14 +282,14 @@ func TestCleanupHookTaskTable_PerWebhook_LeavesMostRecentTask(t *testing.T) {
func TestCleanupHookTaskTable_OlderThan_DeletesDelivered(t *testing.T) { func TestCleanupHookTaskTable_OlderThan_DeletesDelivered(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
hookTask := &HookTask{ hookTask := &HookTask{
RepoID: 3,
HookID: 3, HookID: 3,
Payloader: &api.PushPayload{}, Payloader: &api.PushPayload{},
IsDelivered: true, IsDelivered: true,
Delivered: time.Now().AddDate(0, 0, -8).UnixNano(), Delivered: time.Now().AddDate(0, 0, -8).UnixNano(),
} }
unittest.AssertNotExistsBean(t, hookTask) unittest.AssertNotExistsBean(t, hookTask)
assert.NoError(t, CreateHookTask(hookTask)) _, err := CreateHookTask(db.DefaultContext, hookTask)
assert.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, hookTask) unittest.AssertExistsAndLoadBean(t, hookTask)
assert.NoError(t, CleanupHookTaskTable(context.Background(), OlderThan, 168*time.Hour, 0)) assert.NoError(t, CleanupHookTaskTable(context.Background(), OlderThan, 168*time.Hour, 0))
@ -299,13 +299,13 @@ func TestCleanupHookTaskTable_OlderThan_DeletesDelivered(t *testing.T) {
func TestCleanupHookTaskTable_OlderThan_LeavesUndelivered(t *testing.T) { func TestCleanupHookTaskTable_OlderThan_LeavesUndelivered(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
hookTask := &HookTask{ hookTask := &HookTask{
RepoID: 2,
HookID: 4, HookID: 4,
Payloader: &api.PushPayload{}, Payloader: &api.PushPayload{},
IsDelivered: false, IsDelivered: false,
} }
unittest.AssertNotExistsBean(t, hookTask) unittest.AssertNotExistsBean(t, hookTask)
assert.NoError(t, CreateHookTask(hookTask)) _, err := CreateHookTask(db.DefaultContext, hookTask)
assert.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, hookTask) unittest.AssertExistsAndLoadBean(t, hookTask)
assert.NoError(t, CleanupHookTaskTable(context.Background(), OlderThan, 168*time.Hour, 0)) assert.NoError(t, CleanupHookTaskTable(context.Background(), OlderThan, 168*time.Hour, 0))
@ -315,14 +315,14 @@ func TestCleanupHookTaskTable_OlderThan_LeavesUndelivered(t *testing.T) {
func TestCleanupHookTaskTable_OlderThan_LeavesTaskEarlierThanAgeToDelete(t *testing.T) { func TestCleanupHookTaskTable_OlderThan_LeavesTaskEarlierThanAgeToDelete(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
hookTask := &HookTask{ hookTask := &HookTask{
RepoID: 2,
HookID: 4, HookID: 4,
Payloader: &api.PushPayload{}, Payloader: &api.PushPayload{},
IsDelivered: true, IsDelivered: true,
Delivered: time.Now().AddDate(0, 0, -6).UnixNano(), Delivered: time.Now().AddDate(0, 0, -6).UnixNano(),
} }
unittest.AssertNotExistsBean(t, hookTask) unittest.AssertNotExistsBean(t, hookTask)
assert.NoError(t, CreateHookTask(hookTask)) _, err := CreateHookTask(db.DefaultContext, hookTask)
assert.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, hookTask) unittest.AssertExistsAndLoadBean(t, hookTask)
assert.NoError(t, CleanupHookTaskTable(context.Background(), OlderThan, 168*time.Hour, 0)) assert.NoError(t, CleanupHookTaskTable(context.Background(), OlderThan, 168*time.Hour, 0))

View file

@ -144,3 +144,39 @@ func IsIcon(node ast.Node) bool {
_, ok := node.(*Icon) _, ok := node.(*Icon)
return ok return ok
} }
// ColorPreview is an inline for a color preview
type ColorPreview struct {
ast.BaseInline
Color []byte
}
// Dump implements Node.Dump.
func (n *ColorPreview) Dump(source []byte, level int) {
m := map[string]string{}
m["Color"] = string(n.Color)
ast.DumpHelper(n, source, level, m, nil)
}
// KindColorPreview is the NodeKind for ColorPreview
var KindColorPreview = ast.NewNodeKind("ColorPreview")
// Kind implements Node.Kind.
func (n *ColorPreview) Kind() ast.NodeKind {
return KindColorPreview
}
// NewColorPreview returns a new Span node.
func NewColorPreview(color []byte) *ColorPreview {
return &ColorPreview{
BaseInline: ast.BaseInline{},
Color: color,
}
}
// IsColorPreview returns true if the given node implements the ColorPreview interface,
// otherwise false.
func IsColorPreview(node ast.Node) bool {
_, ok := node.(*ColorPreview)
return ok
}

View file

@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
giteautil "code.gitea.io/gitea/modules/util" giteautil "code.gitea.io/gitea/modules/util"
"github.com/microcosm-cc/bluemonday/css"
"github.com/yuin/goldmark/ast" "github.com/yuin/goldmark/ast"
east "github.com/yuin/goldmark/extension/ast" east "github.com/yuin/goldmark/extension/ast"
"github.com/yuin/goldmark/parser" "github.com/yuin/goldmark/parser"
@ -178,6 +179,11 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
v.SetHardLineBreak(setting.Markdown.EnableHardLineBreakInDocuments) v.SetHardLineBreak(setting.Markdown.EnableHardLineBreakInDocuments)
} }
} }
case *ast.CodeSpan:
colorContent := n.Text(reader.Source())
if css.ColorHandler(strings.ToLower(string(colorContent))) {
v.AppendChild(v, NewColorPreview(colorContent))
}
} }
return ast.WalkContinue, nil return ast.WalkContinue, nil
}) })
@ -266,10 +272,43 @@ func (r *HTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(KindDetails, r.renderDetails) reg.Register(KindDetails, r.renderDetails)
reg.Register(KindSummary, r.renderSummary) reg.Register(KindSummary, r.renderSummary)
reg.Register(KindIcon, r.renderIcon) reg.Register(KindIcon, r.renderIcon)
reg.Register(ast.KindCodeSpan, r.renderCodeSpan)
reg.Register(KindTaskCheckBoxListItem, r.renderTaskCheckBoxListItem) reg.Register(KindTaskCheckBoxListItem, r.renderTaskCheckBoxListItem)
reg.Register(east.KindTaskCheckBox, r.renderTaskCheckBox) reg.Register(east.KindTaskCheckBox, r.renderTaskCheckBox)
} }
// renderCodeSpan renders CodeSpan elements (like goldmark upstream does) but also renders ColorPreview elements.
// See #21474 for reference
func (r *HTMLRenderer) renderCodeSpan(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering {
if n.Attributes() != nil {
_, _ = w.WriteString("<code")
html.RenderAttributes(w, n, html.CodeAttributeFilter)
_ = w.WriteByte('>')
} else {
_, _ = w.WriteString("<code>")
}
for c := n.FirstChild(); c != nil; c = c.NextSibling() {
switch v := c.(type) {
case *ast.Text:
segment := v.Segment
value := segment.Value(source)
if bytes.HasSuffix(value, []byte("\n")) {
r.Writer.RawWrite(w, value[:len(value)-1])
r.Writer.RawWrite(w, []byte(" "))
} else {
r.Writer.RawWrite(w, value)
}
case *ColorPreview:
_, _ = w.WriteString(fmt.Sprintf(`<span class="color-preview" style="background-color: %v"></span>`, string(v.Color)))
}
}
return ast.WalkSkipChildren, nil
}
_, _ = w.WriteString("</code>")
return ast.WalkContinue, nil
}
func (r *HTMLRenderer) renderDocument(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { func (r *HTMLRenderer) renderDocument(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*ast.Document) n := node.(*ast.Document)

View file

@ -429,6 +429,61 @@ func TestRenderEmojiInLinks_Issue12331(t *testing.T) {
assert.Equal(t, expected, res) assert.Equal(t, expected, res)
} }
func TestColorPreview(t *testing.T) {
const nl = "\n"
positiveTests := []struct {
testcase string
expected string
}{
{ // hex
"`#FF0000`",
`<p><code>#FF0000<span class="color-preview" style="background-color: #FF0000"></span></code></p>` + nl,
},
{ // rgb
"`rgb(16, 32, 64)`",
`<p><code>rgb(16, 32, 64)<span class="color-preview" style="background-color: rgb(16, 32, 64)"></span></code></p>` + nl,
},
{ // short hex
"This is the color white `#000`",
`<p>This is the color white <code>#000<span class="color-preview" style="background-color: #000"></span></code></p>` + nl,
},
{ // hsl
"HSL stands for hue, saturation, and lightness. An example: `hsl(0, 100%, 50%)`.",
`<p>HSL stands for hue, saturation, and lightness. An example: <code>hsl(0, 100%, 50%)<span class="color-preview" style="background-color: hsl(0, 100%, 50%)"></span></code>.</p>` + nl,
},
{ // uppercase hsl
"HSL stands for hue, saturation, and lightness. An example: `HSL(0, 100%, 50%)`.",
`<p>HSL stands for hue, saturation, and lightness. An example: <code>HSL(0, 100%, 50%)<span class="color-preview" style="background-color: HSL(0, 100%, 50%)"></span></code>.</p>` + nl,
},
}
for _, test := range positiveTests {
res, err := RenderString(&markup.RenderContext{}, test.testcase)
assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
assert.Equal(t, test.expected, res, "Unexpected result in testcase %q", test.testcase)
}
negativeTests := []string{
// not a color code
"`FF0000`",
// inside a code block
"```javascript" + nl + `const red = "#FF0000";` + nl + "```",
// no backticks
"rgb(166, 32, 64)",
// typo
"`hsI(0, 100%, 50%)`",
// looks like a color but not really
"`hsl(40, 60, 80)`",
}
for _, test := range negativeTests {
res, err := RenderString(&markup.RenderContext{}, test)
assert.NoError(t, err, "Unexpected error in testcase: %q", test)
assert.NotContains(t, res, `<span class="color-preview" style="background-color: `, "Unexpected result in testcase %q", test)
}
}
func TestMathBlock(t *testing.T) { func TestMathBlock(t *testing.T) {
const nl = "\n" const nl = "\n"
testcases := []struct { testcases := []struct {

View file

@ -10,8 +10,6 @@ import (
"unicode" "unicode"
"unicode/utf8" "unicode/utf8"
"code.gitea.io/gitea/modules/log"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@ -99,8 +97,6 @@ func ExtractMetadataBytes(contents []byte, out interface{}) ([]byte, error) {
return contents, errors.New("could not determine metadata") return contents, errors.New("could not determine metadata")
} }
log.Info("%s", string(front))
if err := yaml.Unmarshal(front, out); err != nil { if err := yaml.Unmarshal(front, out); err != nil {
return contents, err return contents, err
} }

View file

@ -55,6 +55,9 @@ func createDefaultPolicy() *bluemonday.Policy {
// For JS code copy and Mermaid loading state // For JS code copy and Mermaid loading state
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^code-block( is-loading)?$`)).OnElements("pre") policy.AllowAttrs("class").Matching(regexp.MustCompile(`^code-block( is-loading)?$`)).OnElements("pre")
// For color preview
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^color-preview$`)).OnElements("span")
// For Chroma markdown plugin // For Chroma markdown plugin
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^(chroma )?language-[\w-]+( display)?( is-loading)?$`)).OnElements("code") policy.AllowAttrs("class").Matching(regexp.MustCompile(`^(chroma )?language-[\w-]+( display)?( is-loading)?$`)).OnElements("code")
@ -88,8 +91,8 @@ func createDefaultPolicy() *bluemonday.Policy {
// Allow 'style' attribute on text elements. // Allow 'style' attribute on text elements.
policy.AllowAttrs("style").OnElements("span", "p") policy.AllowAttrs("style").OnElements("span", "p")
// Allow 'color' property for the style attribute on text elements. // Allow 'color' and 'background-color' properties for the style attribute on text elements.
policy.AllowStyles("color").OnElements("span", "p") policy.AllowStyles("color", "background-color").OnElements("span", "p")
// Allow generally safe attributes // Allow generally safe attributes
generalSafeAttrs := []string{ generalSafeAttrs := []string{

View file

@ -61,7 +61,7 @@ func (m *webhookNotifier) NotifyIssueClearLabels(doer *user_model.User, issue *i
return return
} }
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequestLabel, &api.PullRequestPayload{ err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventPullRequestLabel, &api.PullRequestPayload{
Action: api.HookIssueLabelCleared, Action: api.HookIssueLabelCleared,
Index: issue.Index, Index: issue.Index,
PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil), PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil),
@ -69,7 +69,7 @@ func (m *webhookNotifier) NotifyIssueClearLabels(doer *user_model.User, issue *i
Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(doer, nil),
}) })
} else { } else {
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssueLabel, &api.IssuePayload{ err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventIssueLabel, &api.IssuePayload{
Action: api.HookIssueLabelCleared, Action: api.HookIssueLabelCleared,
Index: issue.Index, Index: issue.Index,
Issue: convert.ToAPIIssue(issue), Issue: convert.ToAPIIssue(issue),
@ -87,7 +87,7 @@ func (m *webhookNotifier) NotifyForkRepository(doer *user_model.User, oldRepo, r
mode, _ := access_model.AccessLevel(doer, repo) mode, _ := access_model.AccessLevel(doer, repo)
// forked webhook // forked webhook
if err := webhook_services.PrepareWebhooks(oldRepo, webhook.HookEventFork, &api.ForkPayload{ if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: oldRepo}, webhook.HookEventFork, &api.ForkPayload{
Forkee: convert.ToRepo(oldRepo, oldMode), Forkee: convert.ToRepo(oldRepo, oldMode),
Repo: convert.ToRepo(repo, mode), Repo: convert.ToRepo(repo, mode),
Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(doer, nil),
@ -99,7 +99,7 @@ func (m *webhookNotifier) NotifyForkRepository(doer *user_model.User, oldRepo, r
// Add to hook queue for created repo after session commit. // Add to hook queue for created repo after session commit.
if u.IsOrganization() { if u.IsOrganization() {
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventRepository, &api.RepositoryPayload{ if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: repo}, webhook.HookEventRepository, &api.RepositoryPayload{
Action: api.HookRepoCreated, Action: api.HookRepoCreated,
Repository: convert.ToRepo(repo, perm.AccessModeOwner), Repository: convert.ToRepo(repo, perm.AccessModeOwner),
Organization: convert.ToUser(u, nil), Organization: convert.ToUser(u, nil),
@ -112,7 +112,7 @@ func (m *webhookNotifier) NotifyForkRepository(doer *user_model.User, oldRepo, r
func (m *webhookNotifier) NotifyCreateRepository(doer, u *user_model.User, repo *repo_model.Repository) { func (m *webhookNotifier) NotifyCreateRepository(doer, u *user_model.User, repo *repo_model.Repository) {
// Add to hook queue for created repo after session commit. // Add to hook queue for created repo after session commit.
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventRepository, &api.RepositoryPayload{ if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: repo}, webhook.HookEventRepository, &api.RepositoryPayload{
Action: api.HookRepoCreated, Action: api.HookRepoCreated,
Repository: convert.ToRepo(repo, perm.AccessModeOwner), Repository: convert.ToRepo(repo, perm.AccessModeOwner),
Organization: convert.ToUser(u, nil), Organization: convert.ToUser(u, nil),
@ -125,7 +125,7 @@ func (m *webhookNotifier) NotifyCreateRepository(doer, u *user_model.User, repo
func (m *webhookNotifier) NotifyDeleteRepository(doer *user_model.User, repo *repo_model.Repository) { func (m *webhookNotifier) NotifyDeleteRepository(doer *user_model.User, repo *repo_model.Repository) {
u := repo.MustOwner() u := repo.MustOwner()
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventRepository, &api.RepositoryPayload{ if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: repo}, webhook.HookEventRepository, &api.RepositoryPayload{
Action: api.HookRepoDeleted, Action: api.HookRepoDeleted,
Repository: convert.ToRepo(repo, perm.AccessModeOwner), Repository: convert.ToRepo(repo, perm.AccessModeOwner),
Organization: convert.ToUser(u, nil), Organization: convert.ToUser(u, nil),
@ -137,7 +137,7 @@ func (m *webhookNotifier) NotifyDeleteRepository(doer *user_model.User, repo *re
func (m *webhookNotifier) NotifyMigrateRepository(doer, u *user_model.User, repo *repo_model.Repository) { func (m *webhookNotifier) NotifyMigrateRepository(doer, u *user_model.User, repo *repo_model.Repository) {
// Add to hook queue for created repo after session commit. // Add to hook queue for created repo after session commit.
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventRepository, &api.RepositoryPayload{ if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: repo}, webhook.HookEventRepository, &api.RepositoryPayload{
Action: api.HookRepoCreated, Action: api.HookRepoCreated,
Repository: convert.ToRepo(repo, perm.AccessModeOwner), Repository: convert.ToRepo(repo, perm.AccessModeOwner),
Organization: convert.ToUser(u, nil), Organization: convert.ToUser(u, nil),
@ -171,7 +171,7 @@ func (m *webhookNotifier) NotifyIssueChangeAssignee(doer *user_model.User, issue
apiPullRequest.Action = api.HookIssueAssigned apiPullRequest.Action = api.HookIssueAssigned
} }
// Assignee comment triggers a webhook // Assignee comment triggers a webhook
if err := webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequestAssign, apiPullRequest); err != nil { if err := webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventPullRequestAssign, apiPullRequest); err != nil {
log.Error("PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, removed, err) log.Error("PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, removed, err)
return return
} }
@ -189,7 +189,7 @@ func (m *webhookNotifier) NotifyIssueChangeAssignee(doer *user_model.User, issue
apiIssue.Action = api.HookIssueAssigned apiIssue.Action = api.HookIssueAssigned
} }
// Assignee comment triggers a webhook // Assignee comment triggers a webhook
if err := webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssueAssign, apiIssue); err != nil { if err := webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventIssueAssign, apiIssue); err != nil {
log.Error("PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, removed, err) log.Error("PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, removed, err)
return return
} }
@ -208,7 +208,7 @@ func (m *webhookNotifier) NotifyIssueChangeTitle(doer *user_model.User, issue *i
return return
} }
issue.PullRequest.Issue = issue issue.PullRequest.Issue = issue
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequest, &api.PullRequestPayload{ err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventPullRequest, &api.PullRequestPayload{
Action: api.HookIssueEdited, Action: api.HookIssueEdited,
Index: issue.Index, Index: issue.Index,
Changes: &api.ChangesPayload{ Changes: &api.ChangesPayload{
@ -221,7 +221,7 @@ func (m *webhookNotifier) NotifyIssueChangeTitle(doer *user_model.User, issue *i
Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(doer, nil),
}) })
} else { } else {
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssues, &api.IssuePayload{ err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventIssues, &api.IssuePayload{
Action: api.HookIssueEdited, Action: api.HookIssueEdited,
Index: issue.Index, Index: issue.Index,
Changes: &api.ChangesPayload{ Changes: &api.ChangesPayload{
@ -263,7 +263,7 @@ func (m *webhookNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *
} else { } else {
apiPullRequest.Action = api.HookIssueReOpened apiPullRequest.Action = api.HookIssueReOpened
} }
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequest, apiPullRequest) err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventPullRequest, apiPullRequest)
} else { } else {
apiIssue := &api.IssuePayload{ apiIssue := &api.IssuePayload{
Index: issue.Index, Index: issue.Index,
@ -276,7 +276,7 @@ func (m *webhookNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *
} else { } else {
apiIssue.Action = api.HookIssueReOpened apiIssue.Action = api.HookIssueReOpened
} }
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssues, apiIssue) err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventIssues, apiIssue)
} }
if err != nil { if err != nil {
log.Error("PrepareWebhooks [is_pull: %v, is_closed: %v]: %v", issue.IsPull, isClosed, err) log.Error("PrepareWebhooks [is_pull: %v, is_closed: %v]: %v", issue.IsPull, isClosed, err)
@ -294,7 +294,7 @@ func (m *webhookNotifier) NotifyNewIssue(issue *issues_model.Issue, mentions []*
} }
mode, _ := access_model.AccessLevel(issue.Poster, issue.Repo) mode, _ := access_model.AccessLevel(issue.Poster, issue.Repo)
if err := webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssues, &api.IssuePayload{ if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventIssues, &api.IssuePayload{
Action: api.HookIssueOpened, Action: api.HookIssueOpened,
Index: issue.Index, Index: issue.Index,
Issue: convert.ToAPIIssue(issue), Issue: convert.ToAPIIssue(issue),
@ -323,7 +323,7 @@ func (m *webhookNotifier) NotifyNewPullRequest(pull *issues_model.PullRequest, m
} }
mode, _ := access_model.AccessLevel(pull.Issue.Poster, pull.Issue.Repo) mode, _ := access_model.AccessLevel(pull.Issue.Poster, pull.Issue.Repo)
if err := webhook_services.PrepareWebhooks(pull.Issue.Repo, webhook.HookEventPullRequest, &api.PullRequestPayload{ if err := webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: pull.Issue.Repo}, webhook.HookEventPullRequest, &api.PullRequestPayload{
Action: api.HookIssueOpened, Action: api.HookIssueOpened,
Index: pull.Issue.Index, Index: pull.Issue.Index,
PullRequest: convert.ToAPIPullRequest(ctx, pull, nil), PullRequest: convert.ToAPIPullRequest(ctx, pull, nil),
@ -342,7 +342,7 @@ func (m *webhookNotifier) NotifyIssueChangeContent(doer *user_model.User, issue
var err error var err error
if issue.IsPull { if issue.IsPull {
issue.PullRequest.Issue = issue issue.PullRequest.Issue = issue
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequest, &api.PullRequestPayload{ err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventPullRequest, &api.PullRequestPayload{
Action: api.HookIssueEdited, Action: api.HookIssueEdited,
Index: issue.Index, Index: issue.Index,
Changes: &api.ChangesPayload{ Changes: &api.ChangesPayload{
@ -355,7 +355,7 @@ func (m *webhookNotifier) NotifyIssueChangeContent(doer *user_model.User, issue
Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(doer, nil),
}) })
} else { } else {
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssues, &api.IssuePayload{ err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventIssues, &api.IssuePayload{
Action: api.HookIssueEdited, Action: api.HookIssueEdited,
Index: issue.Index, Index: issue.Index,
Changes: &api.ChangesPayload{ Changes: &api.ChangesPayload{
@ -374,54 +374,41 @@ func (m *webhookNotifier) NotifyIssueChangeContent(doer *user_model.User, issue
} }
func (m *webhookNotifier) NotifyUpdateComment(doer *user_model.User, c *issues_model.Comment, oldContent string) { func (m *webhookNotifier) NotifyUpdateComment(doer *user_model.User, c *issues_model.Comment, oldContent string) {
var err error if err := c.LoadPoster(); err != nil {
if err = c.LoadPoster(); err != nil {
log.Error("LoadPoster: %v", err) log.Error("LoadPoster: %v", err)
return return
} }
if err = c.LoadIssue(); err != nil { if err := c.LoadIssue(); err != nil {
log.Error("LoadIssue: %v", err) log.Error("LoadIssue: %v", err)
return return
} }
if err = c.Issue.LoadAttributes(db.DefaultContext); err != nil { if err := c.Issue.LoadAttributes(db.DefaultContext); err != nil {
log.Error("LoadAttributes: %v", err) log.Error("LoadAttributes: %v", err)
return return
} }
mode, _ := access_model.AccessLevel(doer, c.Issue.Repo) var eventType webhook.HookEventType
if c.Issue.IsPull { if c.Issue.IsPull {
err = webhook_services.PrepareWebhooks(c.Issue.Repo, webhook.HookEventPullRequestComment, &api.IssueCommentPayload{ eventType = webhook.HookEventPullRequestComment
Action: api.HookIssueCommentEdited,
Issue: convert.ToAPIIssue(c.Issue),
Comment: convert.ToComment(c),
Changes: &api.ChangesPayload{
Body: &api.ChangesFromPayload{
From: oldContent,
},
},
Repository: convert.ToRepo(c.Issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
IsPull: true,
})
} else { } else {
err = webhook_services.PrepareWebhooks(c.Issue.Repo, webhook.HookEventIssueComment, &api.IssueCommentPayload{ eventType = webhook.HookEventIssueComment
Action: api.HookIssueCommentEdited,
Issue: convert.ToAPIIssue(c.Issue),
Comment: convert.ToComment(c),
Changes: &api.ChangesPayload{
Body: &api.ChangesFromPayload{
From: oldContent,
},
},
Repository: convert.ToRepo(c.Issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
IsPull: false,
})
} }
if err != nil { mode, _ := access_model.AccessLevel(doer, c.Issue.Repo)
if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: c.Issue.Repo}, eventType, &api.IssueCommentPayload{
Action: api.HookIssueCommentEdited,
Issue: convert.ToAPIIssue(c.Issue),
Comment: convert.ToComment(c),
Changes: &api.ChangesPayload{
Body: &api.ChangesFromPayload{
From: oldContent,
},
},
Repository: convert.ToRepo(c.Issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
IsPull: c.Issue.IsPull,
}); err != nil {
log.Error("PrepareWebhooks [comment_id: %d]: %v", c.ID, err) log.Error("PrepareWebhooks [comment_id: %d]: %v", c.ID, err)
} }
} }
@ -429,30 +416,22 @@ func (m *webhookNotifier) NotifyUpdateComment(doer *user_model.User, c *issues_m
func (m *webhookNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *repo_model.Repository, func (m *webhookNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *repo_model.Repository,
issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User, issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User,
) { ) {
mode, _ := access_model.AccessLevel(doer, repo) var eventType webhook.HookEventType
var err error
if issue.IsPull { if issue.IsPull {
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequestComment, &api.IssueCommentPayload{ eventType = webhook.HookEventPullRequestComment
Action: api.HookIssueCommentCreated,
Issue: convert.ToAPIIssue(issue),
Comment: convert.ToComment(comment),
Repository: convert.ToRepo(repo, mode),
Sender: convert.ToUser(doer, nil),
IsPull: true,
})
} else { } else {
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssueComment, &api.IssueCommentPayload{ eventType = webhook.HookEventIssueComment
Action: api.HookIssueCommentCreated,
Issue: convert.ToAPIIssue(issue),
Comment: convert.ToComment(comment),
Repository: convert.ToRepo(repo, mode),
Sender: convert.ToUser(doer, nil),
IsPull: false,
})
} }
if err != nil { mode, _ := access_model.AccessLevel(doer, repo)
if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: issue.Repo}, eventType, &api.IssueCommentPayload{
Action: api.HookIssueCommentCreated,
Issue: convert.ToAPIIssue(issue),
Comment: convert.ToComment(comment),
Repository: convert.ToRepo(repo, mode),
Sender: convert.ToUser(doer, nil),
IsPull: issue.IsPull,
}); err != nil {
log.Error("PrepareWebhooks [comment_id: %d]: %v", comment.ID, err) log.Error("PrepareWebhooks [comment_id: %d]: %v", comment.ID, err)
} }
} }
@ -474,36 +453,29 @@ func (m *webhookNotifier) NotifyDeleteComment(doer *user_model.User, comment *is
return return
} }
mode, _ := access_model.AccessLevel(doer, comment.Issue.Repo) var eventType webhook.HookEventType
if comment.Issue.IsPull { if comment.Issue.IsPull {
err = webhook_services.PrepareWebhooks(comment.Issue.Repo, webhook.HookEventPullRequestComment, &api.IssueCommentPayload{ eventType = webhook.HookEventPullRequestComment
Action: api.HookIssueCommentDeleted,
Issue: convert.ToAPIIssue(comment.Issue),
Comment: convert.ToComment(comment),
Repository: convert.ToRepo(comment.Issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
IsPull: true,
})
} else { } else {
err = webhook_services.PrepareWebhooks(comment.Issue.Repo, webhook.HookEventIssueComment, &api.IssueCommentPayload{ eventType = webhook.HookEventIssueComment
Action: api.HookIssueCommentDeleted,
Issue: convert.ToAPIIssue(comment.Issue),
Comment: convert.ToComment(comment),
Repository: convert.ToRepo(comment.Issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
IsPull: false,
})
} }
if err != nil { mode, _ := access_model.AccessLevel(doer, comment.Issue.Repo)
if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: comment.Issue.Repo}, eventType, &api.IssueCommentPayload{
Action: api.HookIssueCommentDeleted,
Issue: convert.ToAPIIssue(comment.Issue),
Comment: convert.ToComment(comment),
Repository: convert.ToRepo(comment.Issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
IsPull: comment.Issue.IsPull,
}); err != nil {
log.Error("PrepareWebhooks [comment_id: %d]: %v", comment.ID, err) log.Error("PrepareWebhooks [comment_id: %d]: %v", comment.ID, err)
} }
} }
func (m *webhookNotifier) NotifyNewWikiPage(doer *user_model.User, repo *repo_model.Repository, page, comment string) { func (m *webhookNotifier) NotifyNewWikiPage(doer *user_model.User, repo *repo_model.Repository, page, comment string) {
// Add to hook queue for created wiki page. // Add to hook queue for created wiki page.
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventWiki, &api.WikiPayload{ if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: repo}, webhook.HookEventWiki, &api.WikiPayload{
Action: api.HookWikiCreated, Action: api.HookWikiCreated,
Repository: convert.ToRepo(repo, perm.AccessModeOwner), Repository: convert.ToRepo(repo, perm.AccessModeOwner),
Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(doer, nil),
@ -516,7 +488,7 @@ func (m *webhookNotifier) NotifyNewWikiPage(doer *user_model.User, repo *repo_mo
func (m *webhookNotifier) NotifyEditWikiPage(doer *user_model.User, repo *repo_model.Repository, page, comment string) { func (m *webhookNotifier) NotifyEditWikiPage(doer *user_model.User, repo *repo_model.Repository, page, comment string) {
// Add to hook queue for edit wiki page. // Add to hook queue for edit wiki page.
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventWiki, &api.WikiPayload{ if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: repo}, webhook.HookEventWiki, &api.WikiPayload{
Action: api.HookWikiEdited, Action: api.HookWikiEdited,
Repository: convert.ToRepo(repo, perm.AccessModeOwner), Repository: convert.ToRepo(repo, perm.AccessModeOwner),
Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(doer, nil),
@ -529,7 +501,7 @@ func (m *webhookNotifier) NotifyEditWikiPage(doer *user_model.User, repo *repo_m
func (m *webhookNotifier) NotifyDeleteWikiPage(doer *user_model.User, repo *repo_model.Repository, page string) { func (m *webhookNotifier) NotifyDeleteWikiPage(doer *user_model.User, repo *repo_model.Repository, page string) {
// Add to hook queue for edit wiki page. // Add to hook queue for edit wiki page.
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventWiki, &api.WikiPayload{ if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: repo}, webhook.HookEventWiki, &api.WikiPayload{
Action: api.HookWikiDeleted, Action: api.HookWikiDeleted,
Repository: convert.ToRepo(repo, perm.AccessModeOwner), Repository: convert.ToRepo(repo, perm.AccessModeOwner),
Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(doer, nil),
@ -567,7 +539,7 @@ func (m *webhookNotifier) NotifyIssueChangeLabels(doer *user_model.User, issue *
log.Error("LoadIssue: %v", err) log.Error("LoadIssue: %v", err)
return return
} }
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequestLabel, &api.PullRequestPayload{ err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventPullRequestLabel, &api.PullRequestPayload{
Action: api.HookIssueLabelUpdated, Action: api.HookIssueLabelUpdated,
Index: issue.Index, Index: issue.Index,
PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil), PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil),
@ -575,7 +547,7 @@ func (m *webhookNotifier) NotifyIssueChangeLabels(doer *user_model.User, issue *
Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(doer, nil),
}) })
} else { } else {
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssueLabel, &api.IssuePayload{ err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventIssueLabel, &api.IssuePayload{
Action: api.HookIssueLabelUpdated, Action: api.HookIssueLabelUpdated,
Index: issue.Index, Index: issue.Index,
Issue: convert.ToAPIIssue(issue), Issue: convert.ToAPIIssue(issue),
@ -612,7 +584,7 @@ func (m *webhookNotifier) NotifyIssueChangeMilestone(doer *user_model.User, issu
log.Error("LoadIssue: %v", err) log.Error("LoadIssue: %v", err)
return return
} }
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequestMilestone, &api.PullRequestPayload{ err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventPullRequestMilestone, &api.PullRequestPayload{
Action: hookAction, Action: hookAction,
Index: issue.Index, Index: issue.Index,
PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil), PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil),
@ -620,7 +592,7 @@ func (m *webhookNotifier) NotifyIssueChangeMilestone(doer *user_model.User, issu
Sender: convert.ToUser(doer, nil), Sender: convert.ToUser(doer, nil),
}) })
} else { } else {
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssueMilestone, &api.IssuePayload{ err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventIssueMilestone, &api.IssuePayload{
Action: hookAction, Action: hookAction,
Index: issue.Index, Index: issue.Index,
Issue: convert.ToAPIIssue(issue), Issue: convert.ToAPIIssue(issue),
@ -644,7 +616,7 @@ func (m *webhookNotifier) NotifyPushCommits(pusher *user_model.User, repo *repo_
return return
} }
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventPush, &api.PushPayload{ if err := webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: repo}, webhook.HookEventPush, &api.PushPayload{
Ref: opts.RefFullName, Ref: opts.RefFullName,
Before: opts.OldCommitID, Before: opts.OldCommitID,
After: opts.NewCommitID, After: opts.NewCommitID,
@ -695,7 +667,7 @@ func (*webhookNotifier) NotifyMergePullRequest(pr *issues_model.PullRequest, doe
Action: api.HookIssueClosed, Action: api.HookIssueClosed,
} }
err = webhook_services.PrepareWebhooks(pr.Issue.Repo, webhook.HookEventPullRequest, apiPullRequest) err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: pr.Issue.Repo}, webhook.HookEventPullRequest, apiPullRequest)
if err != nil { if err != nil {
log.Error("PrepareWebhooks: %v", err) log.Error("PrepareWebhooks: %v", err)
} }
@ -717,7 +689,7 @@ func (m *webhookNotifier) NotifyPullRequestChangeTargetBranch(doer *user_model.U
} }
issue.PullRequest.Issue = issue issue.PullRequest.Issue = issue
mode, _ := access_model.AccessLevel(issue.Poster, issue.Repo) mode, _ := access_model.AccessLevel(issue.Poster, issue.Repo)
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequest, &api.PullRequestPayload{ err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventPullRequest, &api.PullRequestPayload{
Action: api.HookIssueEdited, Action: api.HookIssueEdited,
Index: issue.Index, Index: issue.Index,
Changes: &api.ChangesPayload{ Changes: &api.ChangesPayload{
@ -764,7 +736,7 @@ func (m *webhookNotifier) NotifyPullRequestReview(pr *issues_model.PullRequest,
log.Error("models.AccessLevel: %v", err) log.Error("models.AccessLevel: %v", err)
return return
} }
if err := webhook_services.PrepareWebhooks(review.Issue.Repo, reviewHookType, &api.PullRequestPayload{ if err := webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: review.Issue.Repo}, reviewHookType, &api.PullRequestPayload{
Action: api.HookIssueReviewed, Action: api.HookIssueReviewed,
Index: review.Issue.Index, Index: review.Issue.Index,
PullRequest: convert.ToAPIPullRequest(ctx, pr, nil), PullRequest: convert.ToAPIPullRequest(ctx, pr, nil),
@ -784,7 +756,7 @@ func (m *webhookNotifier) NotifyCreateRef(pusher *user_model.User, repo *repo_mo
apiRepo := convert.ToRepo(repo, perm.AccessModeNone) apiRepo := convert.ToRepo(repo, perm.AccessModeNone)
refName := git.RefEndName(refFullName) refName := git.RefEndName(refFullName)
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventCreate, &api.CreatePayload{ if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: repo}, webhook.HookEventCreate, &api.CreatePayload{
Ref: refName, Ref: refName,
Sha: refID, Sha: refID,
RefType: refType, RefType: refType,
@ -808,7 +780,7 @@ func (m *webhookNotifier) NotifyPullRequestSynchronized(doer *user_model.User, p
return return
} }
if err := webhook_services.PrepareWebhooks(pr.Issue.Repo, webhook.HookEventPullRequestSync, &api.PullRequestPayload{ if err := webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: pr.Issue.Repo}, webhook.HookEventPullRequestSync, &api.PullRequestPayload{
Action: api.HookIssueSynchronized, Action: api.HookIssueSynchronized,
Index: pr.Issue.Index, Index: pr.Issue.Index,
PullRequest: convert.ToAPIPullRequest(ctx, pr, nil), PullRequest: convert.ToAPIPullRequest(ctx, pr, nil),
@ -824,7 +796,7 @@ func (m *webhookNotifier) NotifyDeleteRef(pusher *user_model.User, repo *repo_mo
apiRepo := convert.ToRepo(repo, perm.AccessModeNone) apiRepo := convert.ToRepo(repo, perm.AccessModeNone)
refName := git.RefEndName(refFullName) refName := git.RefEndName(refFullName)
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventDelete, &api.DeletePayload{ if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: repo}, webhook.HookEventDelete, &api.DeletePayload{
Ref: refName, Ref: refName,
RefType: refType, RefType: refType,
PusherType: api.PusherTypeUser, PusherType: api.PusherTypeUser,
@ -842,7 +814,7 @@ func sendReleaseHook(doer *user_model.User, rel *repo_model.Release, action api.
} }
mode, _ := access_model.AccessLevel(doer, rel.Repo) mode, _ := access_model.AccessLevel(doer, rel.Repo)
if err := webhook_services.PrepareWebhooks(rel.Repo, webhook.HookEventRelease, &api.ReleasePayload{ if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: rel.Repo}, webhook.HookEventRelease, &api.ReleasePayload{
Action: action, Action: action,
Release: convert.ToRelease(rel), Release: convert.ToRelease(rel),
Repository: convert.ToRepo(rel.Repo, mode), Repository: convert.ToRepo(rel.Repo, mode),
@ -875,7 +847,7 @@ func (m *webhookNotifier) NotifySyncPushCommits(pusher *user_model.User, repo *r
return return
} }
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventPush, &api.PushPayload{ if err := webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: repo}, webhook.HookEventPush, &api.PushPayload{
Ref: opts.RefFullName, Ref: opts.RefFullName,
Before: opts.OldCommitID, Before: opts.OldCommitID,
After: opts.NewCommitID, After: opts.NewCommitID,
@ -908,9 +880,9 @@ func (m *webhookNotifier) NotifyPackageDelete(doer *user_model.User, pd *package
} }
func notifyPackage(sender *user_model.User, pd *packages_model.PackageDescriptor, action api.HookPackageAction) { func notifyPackage(sender *user_model.User, pd *packages_model.PackageDescriptor, action api.HookPackageAction) {
if pd.Repository == nil { source := webhook_services.EventSource{
// TODO https://github.com/go-gitea/gitea/pull/17940 Repository: pd.Repository,
return Owner: pd.Owner,
} }
ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("webhook.notifyPackage Package: %s[%d]", pd.Package.Name, pd.Package.ID)) ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("webhook.notifyPackage Package: %s[%d]", pd.Package.Name, pd.Package.ID))
@ -922,7 +894,7 @@ func notifyPackage(sender *user_model.User, pd *packages_model.PackageDescriptor
return return
} }
if err := webhook_services.PrepareWebhooks(pd.Repository, webhook.HookEventPackage, &api.PackagePayload{ if err := webhook_services.PrepareWebhooks(ctx, source, webhook.HookEventPackage, &api.PackagePayload{
Action: action, Action: action,
Package: apiPackage, Package: apiPackage,
Sender: convert.ToUser(sender, nil), Sender: convert.ToUser(sender, nil),

View file

@ -459,6 +459,19 @@ func NewFuncMap() []template.FuncMap {
return items return items
}, },
"HasPrefix": strings.HasPrefix, "HasPrefix": strings.HasPrefix,
"CompareLink": func(baseRepo, repo *repo_model.Repository, branchName string) string {
var curBranch string
if repo.ID != baseRepo.ID {
curBranch += fmt.Sprintf("%s/%s:", url.PathEscape(repo.OwnerName), url.PathEscape(repo.Name))
}
curBranch += util.PathEscapeSegments(branchName)
return fmt.Sprintf("%s/compare/%s...%s",
baseRepo.Link(),
util.PathEscapeSegments(baseRepo.DefaultBranch),
curBranch,
)
},
}} }}
} }

View file

@ -261,6 +261,7 @@ register_success=Успешна регистрация
[modal] [modal]
yes=Да yes=Да
no=Не no=Не

View file

@ -412,6 +412,7 @@ repo.transfer.body=Chcete-li ji přijmout nebo odmítnout, navštivte %s nebo ji
repo.collaborator.added.subject=%s vás přidal do %s repo.collaborator.added.subject=%s vás přidal do %s
repo.collaborator.added.text=Byl jste přidán jako spolupracovník repozitáře: repo.collaborator.added.text=Byl jste přidán jako spolupracovník repozitáře:
[modal] [modal]
yes=Ano yes=Ano
no=Ne no=Ne

View file

@ -407,6 +407,7 @@ repo.transfer.body=Um es anzunehmen oder abzulehnen, öffne %s, oder ignoriere e
repo.collaborator.added.subject=%s hat dich zu %s hinzugefügt repo.collaborator.added.subject=%s hat dich zu %s hinzugefügt
repo.collaborator.added.text=Du wurdest als Mitarbeiter für folgendes Repository hinzugefügt: repo.collaborator.added.text=Du wurdest als Mitarbeiter für folgendes Repository hinzugefügt:
[modal] [modal]
yes=Ja yes=Ja
no=Abbrechen no=Abbrechen

View file

@ -409,6 +409,7 @@ repo.transfer.body=Για να το αποδεχτείτε ή να το απορ
repo.collaborator.added.subject=%s σας πρόσθεσε στο %s repo.collaborator.added.subject=%s σας πρόσθεσε στο %s
repo.collaborator.added.text=Έχετε προστεθεί ως συνεργάτης του αποθετηρίου: repo.collaborator.added.text=Έχετε προστεθεί ως συνεργάτης του αποθετηρίου:
[modal] [modal]
yes=Ναι yes=Ναι
no=Όχι no=Όχι

View file

@ -411,6 +411,7 @@ repo.transfer.body=Para aceptarlo o rechazarlo, visita %s o simplemente ignórel
repo.collaborator.added.subject=%s le añadió en %s repo.collaborator.added.subject=%s le añadió en %s
repo.collaborator.added.text=Has sido añadido como colaborador del repositorio: repo.collaborator.added.text=Has sido añadido como colaborador del repositorio:
[modal] [modal]
yes= yes=
no=No no=No

View file

@ -379,6 +379,7 @@ repo.transfer.body=برای تایید یا رد آن %s را ببینید یا
repo.collaborator.added.subject=%s شما را به پروژه %s اضافه کرد repo.collaborator.added.subject=%s شما را به پروژه %s اضافه کرد
repo.collaborator.added.text=شما به عنوان مشارکت‌کننده در این مخزن اضافه شدید: repo.collaborator.added.text=شما به عنوان مشارکت‌کننده در این مخزن اضافه شدید:
[modal] [modal]
yes=بله yes=بله
no=خیر no=خیر

View file

@ -358,6 +358,7 @@ release.download.targz=Lähdekoodi (TAR.GZ)
repo.transfer.to_you=sinä repo.transfer.to_you=sinä
[modal] [modal]
yes=Kyllä yes=Kyllä
no=Ei no=Ei

View file

@ -412,6 +412,7 @@ repo.transfer.body=Pour l'accepter ou le rejeter, visitez %s ou ignorez-le.
repo.collaborator.added.subject=%s vous a ajouté à %s repo.collaborator.added.subject=%s vous a ajouté à %s
repo.collaborator.added.text=Vous avez été ajouté en tant que collaborateur du dépôt : repo.collaborator.added.text=Vous avez été ajouté en tant que collaborateur du dépôt :
[modal] [modal]
yes=Oui yes=Oui
no=Non no=Non

View file

@ -291,6 +291,7 @@ register_success=Sikeres regisztráció
[modal] [modal]
yes=Igen yes=Igen
no=Nem no=Nem

View file

@ -282,6 +282,7 @@ register_success=Pendaftaran berhasil
[modal] [modal]
yes=Ya yes=Ya
no=Tidak no=Tidak

View file

@ -339,6 +339,7 @@ repo.transfer.body=Til að samþykkja eða hafna því skaltu fara á %s eða hu
repo.collaborator.added.subject=%s bætti þér við í %s repo.collaborator.added.subject=%s bætti þér við í %s
repo.collaborator.added.text=Þér hefur verið bætt við sem aðila hugbúnaðarsafns: repo.collaborator.added.text=Þér hefur verið bætt við sem aðila hugbúnaðarsafns:
[modal] [modal]
yes= yes=
no=Nei no=Nei

View file

@ -407,6 +407,7 @@ repo.transfer.body=Per accettare o respingerla visita %s o semplicemente ignorar
repo.collaborator.added.subject=%s ti ha aggiunto a %s repo.collaborator.added.subject=%s ti ha aggiunto a %s
repo.collaborator.added.text=Sei stato aggiunto come collaboratore del repository: repo.collaborator.added.text=Sei stato aggiunto come collaboratore del repository:
[modal] [modal]
yes= yes=
no=No no=No

View file

@ -412,6 +412,7 @@ repo.transfer.body=承認または拒否するには %s を開きます。 も
repo.collaborator.added.subject=%s が %s にあなたを追加しました repo.collaborator.added.subject=%s が %s にあなたを追加しました
repo.collaborator.added.text=あなたは次のリポジトリの共同作業者に追加されました: repo.collaborator.added.text=あなたは次のリポジトリの共同作業者に追加されました:
[modal] [modal]
yes=はい yes=はい
no=いいえ no=いいえ

View file

@ -273,6 +273,7 @@ register_success=등록 완료
[modal] [modal]
yes= yes=
no=아니오 no=아니오

View file

@ -407,6 +407,7 @@ repo.transfer.body=Ja vēlaties to noraidīt vai apstiprināt, tad apmeklējiet
repo.collaborator.added.subject=%s pievienoja Jūs repozitorijam %s repo.collaborator.added.subject=%s pievienoja Jūs repozitorijam %s
repo.collaborator.added.text=Jūs tikāt pievienots kā līdzstrādnieks repozitorijam: repo.collaborator.added.text=Jūs tikāt pievienots kā līdzstrādnieks repozitorijam:
[modal] [modal]
yes= yes=
no= no=

View file

@ -261,6 +261,7 @@ register_success=രജിസ്ട്രേഷൻ വിജയകരം
[modal] [modal]
yes=അതെ yes=അതെ
no=ഇല്ല no=ഇല്ല

View file

@ -407,6 +407,7 @@ repo.transfer.body=Om het te accepteren of afwijzen, bezoek %s of negeer het gew
repo.collaborator.added.subject=%s heeft jou toegevoegd aan %s repo.collaborator.added.subject=%s heeft jou toegevoegd aan %s
repo.collaborator.added.text=U bent toegevoegd als een medewerker van de repository: repo.collaborator.added.text=U bent toegevoegd als een medewerker van de repository:
[modal] [modal]
yes=Ja yes=Ja
no=Nee no=Nee

View file

@ -392,6 +392,7 @@ repo.transfer.body=Aby zaakceptować lub odrzucić go, odwiedź %s lub po prostu
repo.collaborator.added.subject=%s dodał Cię do %s repo.collaborator.added.subject=%s dodał Cię do %s
repo.collaborator.added.text=Zostałeś dodany jako współtwórca repozytorium: repo.collaborator.added.text=Zostałeś dodany jako współtwórca repozytorium:
[modal] [modal]
yes=Tak yes=Tak
no=Nie no=Nie

View file

@ -409,6 +409,7 @@ repo.transfer.body=Para o aceitar ou rejeitar visite %s, ou simplesmente o ignor
repo.collaborator.added.subject=%s adicionou você a %s repo.collaborator.added.subject=%s adicionou você a %s
repo.collaborator.added.text=Você foi adicionado como um colaborador do repositório: repo.collaborator.added.text=Você foi adicionado como um colaborador do repositório:
[modal] [modal]
yes=Sim yes=Sim
no=Não no=Não

View file

@ -412,6 +412,11 @@ repo.transfer.body=Para o aceitar ou rejeitar visite %s, ou ignore-o, simplesmen
repo.collaborator.added.subject=%s adicionou você a %s repo.collaborator.added.subject=%s adicionou você a %s
repo.collaborator.added.text=Foi adicionado(a) como colaborador(a) do repositório: repo.collaborator.added.text=Foi adicionado(a) como colaborador(a) do repositório:
team_invite.subject=%[1]s fez-lhe um convite para se juntar à organização %[2]s
team_invite.text_1=%[1]s fez-lhe um convite para se juntar à equipa %[2]s na organização %[3]s.
team_invite.text_2=Clique na ligação seguinte para se juntar à equipa:
team_invite.text_3=Nota: Este convite é dirigido a %[1]s. Se não estava à espera deste convite, pode ignorar este email.
[modal] [modal]
yes=Sim yes=Sim
no=Não no=Não
@ -487,6 +492,7 @@ user_not_exist=O utilizador não existe.
team_not_exist=A equipa não existe. team_not_exist=A equipa não existe.
last_org_owner=Não pode remover o último utilizador da equipa 'proprietários'. Tem que haver pelo menos um proprietário numa organização. last_org_owner=Não pode remover o último utilizador da equipa 'proprietários'. Tem que haver pelo menos um proprietário numa organização.
cannot_add_org_to_team=Uma organização não pode ser adicionada como membro de uma equipa. cannot_add_org_to_team=Uma organização não pode ser adicionada como membro de uma equipa.
duplicate_invite_to_team=O(A) utilizador(a) já tinha sido convidado(a) para ser membro da equipa.
invalid_ssh_key=Não é possível validar a sua chave SSH: %s invalid_ssh_key=Não é possível validar a sua chave SSH: %s
invalid_gpg_key=Não é possível validar a sua chave GPG: %s invalid_gpg_key=Não é possível validar a sua chave GPG: %s
@ -2402,6 +2408,8 @@ teams.members=Membros da equipa
teams.update_settings=Modificar configurações teams.update_settings=Modificar configurações
teams.delete_team=Eliminar equipa teams.delete_team=Eliminar equipa
teams.add_team_member=Adicionar membro da equipa teams.add_team_member=Adicionar membro da equipa
teams.invite_team_member=Convidar para %s
teams.invite_team_member.list=Convites pendentes
teams.delete_team_title=Eliminar equipa teams.delete_team_title=Eliminar equipa
teams.delete_team_desc=Eliminar uma equipa revoga o acesso dos seus membros ao repositório. Quer continuar? teams.delete_team_desc=Eliminar uma equipa revoga o acesso dos seus membros ao repositório. Quer continuar?
teams.delete_team_success=A equipa foi eliminada. teams.delete_team_success=A equipa foi eliminada.
@ -2426,6 +2434,9 @@ teams.all_repositories_helper=A equipa tem acesso a todos os repositórios. Esco
teams.all_repositories_read_permission_desc=Esta equipa atribui o acesso de <strong>leitura</strong> a <strong>todos os repositórios</strong>: os seus membros podem ver e clonar os repositórios. teams.all_repositories_read_permission_desc=Esta equipa atribui o acesso de <strong>leitura</strong> a <strong>todos os repositórios</strong>: os seus membros podem ver e clonar os repositórios.
teams.all_repositories_write_permission_desc=Esta equipa atribui o acesso de <strong>escrita</strong> a <strong>todos os repositórios</strong>: os seus membros podem ler de, e enviar para os repositórios. teams.all_repositories_write_permission_desc=Esta equipa atribui o acesso de <strong>escrita</strong> a <strong>todos os repositórios</strong>: os seus membros podem ler de, e enviar para os repositórios.
teams.all_repositories_admin_permission_desc=Esta equipa atribui o acesso de <strong>administração</strong> a <strong>todos os repositórios</strong>: os seus membros podem ler de, enviar para, e adicionar colaboradores aos repositórios. teams.all_repositories_admin_permission_desc=Esta equipa atribui o acesso de <strong>administração</strong> a <strong>todos os repositórios</strong>: os seus membros podem ler de, enviar para, e adicionar colaboradores aos repositórios.
teams.invite.title=Foi-lhe feito um convite para se juntar à equipa <strong>%s</strong> na organização<strong>%s</strong>.
teams.invite.by=Convidado(a) por %s
teams.invite.description=Clique no botão abaixo para se juntar à equipa.
[admin] [admin]
dashboard=Painel de controlo dashboard=Painel de controlo

View file

@ -403,6 +403,7 @@ repo.transfer.body=Для того чтобы принять или отклон
repo.collaborator.added.subject=%s добавил вас в %s repo.collaborator.added.subject=%s добавил вас в %s
repo.collaborator.added.text=Вы были добавлены в качестве соавтора репозитория: repo.collaborator.added.text=Вы были добавлены в качестве соавтора репозитория:
[modal] [modal]
yes=Да yes=Да
no=Нет no=Нет

View file

@ -365,6 +365,7 @@ repo.transfer.body=එය පිළිගැනීමට හෝ ප්රති
repo.collaborator.added.subject=%s ඔබව %s ට එකතු කළා repo.collaborator.added.subject=%s ඔබව %s ට එකතු කළා
repo.collaborator.added.text=ඔබ ගබඩාවේ සහයෝගිතාකරුවෙකු ලෙස එකතු කර ඇත: repo.collaborator.added.text=ඔබ ගබඩාවේ සහයෝගිතාකරුවෙකු ලෙස එකතු කර ඇත:
[modal] [modal]
yes=ඔව් yes=ඔව්
no=නැහැ no=නැහැ

View file

@ -409,6 +409,7 @@ repo.transfer.body=Ak to chcete prijať alebo odmietnuť, navštívte %s alebo t
repo.collaborator.added.subject=%s vás pridal do %s repo.collaborator.added.subject=%s vás pridal do %s
repo.collaborator.added.text=Boli ste pridaný ako spolupracovník repozitára: repo.collaborator.added.text=Boli ste pridaný ako spolupracovník repozitára:
[modal] [modal]
yes=Áno yes=Áno
no=Nie no=Nie

View file

@ -304,6 +304,7 @@ register_success=Registreringen lyckades
[modal] [modal]
yes=Ja yes=Ja
no=Nej no=Nej

View file

@ -412,6 +412,7 @@ repo.transfer.body=Kabul veya reddetmek için %s ziyaret edin veya görmezden ge
repo.collaborator.added.subject=%s sizi %s ekledi repo.collaborator.added.subject=%s sizi %s ekledi
repo.collaborator.added.text=Bu depo için katkıcı olarak eklendiniz: repo.collaborator.added.text=Bu depo için katkıcı olarak eklendiniz:
[modal] [modal]
yes=Evet yes=Evet
no=Hayır no=Hayır

View file

@ -382,6 +382,7 @@ repo.transfer.body=Щоб прийняти або відхилити перей
repo.collaborator.added.subject=%s додав вас до %s repo.collaborator.added.subject=%s додав вас до %s
repo.collaborator.added.text=Ви були додані в якості співавтора репозиторію: repo.collaborator.added.text=Ви були додані в якості співавтора репозиторію:
[modal] [modal]
yes=Так yes=Так
no=Ні no=Ні

View file

@ -412,6 +412,11 @@ repo.transfer.body=访问 %s 以接受或拒绝转移,亦可忽略此邮件。
repo.collaborator.added.subject=%s 把你添加到了 %s repo.collaborator.added.subject=%s 把你添加到了 %s
repo.collaborator.added.text=您已被添加为代码库的协作者: repo.collaborator.added.text=您已被添加为代码库的协作者:
team_invite.subject=%[1]s 邀请您加入组织 %[2]s
team_invite.text_1=%[1]s 邀请您加入组织 %[3]s 中的团队 %[2]s。
team_invite.text_2=请点击下面的链接加入团队:
team_invite.text_3=注意:这是发送给 %[1]s 的邀请。如果您未曾收到过此类邀请,请忽略这封电子邮件。
[modal] [modal]
yes=确认操作 yes=确认操作
no=取消操作 no=取消操作
@ -487,6 +492,7 @@ user_not_exist=该用户不存在
team_not_exist=团队不存在 team_not_exist=团队不存在
last_org_owner=您不能从 "所有者" 团队中删除最后一个用户。组织中必须至少有一个所有者。 last_org_owner=您不能从 "所有者" 团队中删除最后一个用户。组织中必须至少有一个所有者。
cannot_add_org_to_team=组织不能被加入到团队中。 cannot_add_org_to_team=组织不能被加入到团队中。
duplicate_invite_to_team=此用户已被邀请为团队成员。
invalid_ssh_key=无法验证您的 SSH 密钥: %s invalid_ssh_key=无法验证您的 SSH 密钥: %s
invalid_gpg_key=无法验证您的 GPG 密钥: %s invalid_gpg_key=无法验证您的 GPG 密钥: %s
@ -861,8 +867,8 @@ readme_helper_desc=这是您可以为您的项目撰写完整描述的地方。
auto_init=初始化仓库(添加. gitignore、许可证和自述文件) auto_init=初始化仓库(添加. gitignore、许可证和自述文件)
trust_model_helper=选择签名验证的“信任模型”。可能的选项是: trust_model_helper=选择签名验证的“信任模型”。可能的选项是:
trust_model_helper_collaborator=协作者:信任协作者的签名 trust_model_helper_collaborator=协作者:信任协作者的签名
trust_model_helper_committer=提交者:信任匹配提交者的签名 trust_model_helper_committer=提交者:信任与提交者相符的签名
trust_model_helper_collaborator_committer=协作者+提交者:信任与提交者匹配的协作者的签名 trust_model_helper_collaborator_committer=协作者+提交者:信任协作者同时是提交者的签名
trust_model_helper_default=默认:使用此安装的默认信任模型 trust_model_helper_default=默认:使用此安装的默认信任模型
create_repo=创建仓库 create_repo=创建仓库
default_branch=默认分支 default_branch=默认分支
@ -1882,13 +1888,13 @@ settings.trust_model.default=默认信任模型
settings.trust_model.default.desc=为此安装使用默认仓库信任模型。 settings.trust_model.default.desc=为此安装使用默认仓库信任模型。
settings.trust_model.collaborator=协作者 settings.trust_model.collaborator=协作者
settings.trust_model.collaborator.long=协作者:信任协作者的签名 settings.trust_model.collaborator.long=协作者:信任协作者的签名
settings.trust_model.collaborator.desc=此仓库中协作者的有效签名将被标记为“可信” - 不管他们是否是提交者。否则,如果签名匹配了提交者,有效的签名将被标记为“不可信” settings.trust_model.collaborator.desc=此仓库中协作者的有效签名将被标记为「可信」(无论它们是否是提交者),签名只符合提交者时将标记为「不可信」,都不匹配时标记为「不匹配」
settings.trust_model.committer=提交者 settings.trust_model.committer=提交者
settings.trust_model.committer.long=提交者: 信任与提交者匹配的签名 (匹配GitHub 并强制Gitea签名的提交者将Gitea作为提交者) settings.trust_model.committer.long=提交者: 信任与提交者相符的签名 (此特性类似 GitHub这会强制采用 Gitea 作为提交者和签名者)
settings.trust_model.committer.desc=有效的签名只有与提交者匹配时才会被标记为“可信”否则会被标记为“不匹配”。这会强制Gitea成为已签名提交的提交者而实际提交者在提交中被标记为Co-authored-by: 和Co-committed-by: trailer。默认的Gitea密钥必须与数据库中的一位用户相匹配 settings.trust_model.committer.desc=提交者的有效签名将被标记为「可信」,否则将被标记为「不匹配」。这会强制 Gitea 成为签名者和提交者实际的提交者将被标记于提交消息结尾处的「Co-Authored-By:」和「Co-Committed-By:」。默认的 Gitea 签名密钥必须匹配数据库中的一个用户密钥
settings.trust_model.collaboratorcommitter=协作者+提交者 settings.trust_model.collaboratorcommitter=协作者+提交者
settings.trust_model.collaboratorcommitter.long=协作者+提交者:信任协作者同时是提交者的签名 settings.trust_model.collaboratorcommitter.long=协作者+提交者:信任协作者同时是提交者的签名
settings.trust_model.collaboratorcommitter.desc=如果匹配为提交者,此仓库中协作者的有效签名将被标记为“可信”。否则,如果签名匹配了提交者或者未匹配,有效的签名将被标记为“不可信”。这将强制 Gitea 在签名提交上将实际提交者加上 Co-Authored-By: 和 Co-Committed-By: 。默认的Gitea密钥必须匹配Gitea用户 settings.trust_model.collaboratorcommitter.desc=此仓库中协作者的有效签名在他同时是提交者时将被标记为「可信」,签名只匹配了提交者时将标记为「不可信」,都不匹配时标记为「不匹配」。这会强制 Gitea 成为签名者和提交者实际的提交者将被标记于提交消息结尾处的「Co-Authored-By:」和「Co-Committed-By:」。默认的 Gitea 签名密钥必须匹配数据库中的一个用户密钥
settings.wiki_delete=删除百科数据 settings.wiki_delete=删除百科数据
settings.wiki_delete_desc=删除仓库百科数据是永久性的,无法撤消。 settings.wiki_delete_desc=删除仓库百科数据是永久性的,无法撤消。
settings.wiki_delete_notices_1=- 这将永久删除和禁用 %s 的百科。 settings.wiki_delete_notices_1=- 这将永久删除和禁用 %s 的百科。
@ -2402,6 +2408,8 @@ teams.members=团队成员
teams.update_settings=更新团队设置 teams.update_settings=更新团队设置
teams.delete_team=删除团队 teams.delete_team=删除团队
teams.add_team_member=添加团队成员 teams.add_team_member=添加团队成员
teams.invite_team_member=邀请加入 %s
teams.invite_team_member.list=待处理的邀请
teams.delete_team_title=删除团队 teams.delete_team_title=删除团队
teams.delete_team_desc=删除一个团队将删除团队成员的访问权限,继续? teams.delete_team_desc=删除一个团队将删除团队成员的访问权限,继续?
teams.delete_team_success=该团队已被删除。 teams.delete_team_success=该团队已被删除。
@ -2426,6 +2434,9 @@ teams.all_repositories_helper=团队可以访问所有仓库。选择此选项
teams.all_repositories_read_permission_desc=此团队授予<strong>读取</strong><strong>所有仓库</strong>的访问权限: 成员可以查看和克隆仓库。 teams.all_repositories_read_permission_desc=此团队授予<strong>读取</strong><strong>所有仓库</strong>的访问权限: 成员可以查看和克隆仓库。
teams.all_repositories_write_permission_desc=此团队授予<strong>修改</strong><strong>所有仓库</strong>的访问权限: 成员可以查看和推送至仓库。 teams.all_repositories_write_permission_desc=此团队授予<strong>修改</strong><strong>所有仓库</strong>的访问权限: 成员可以查看和推送至仓库。
teams.all_repositories_admin_permission_desc=该团队拥有 <strong>管理</strong> <strong>所有仓库</strong>的权限:团队成员可以读取、克隆、推送以及添加其它仓库协作者。 teams.all_repositories_admin_permission_desc=该团队拥有 <strong>管理</strong> <strong>所有仓库</strong>的权限:团队成员可以读取、克隆、推送以及添加其它仓库协作者。
teams.invite.title=您已被邀请加入组织 <strong>%s</strong> 中的团队 <strong>%s</strong>。
teams.invite.by=邀请人 %s
teams.invite.description=请点击下面的按钮加入团队。
[admin] [admin]
dashboard=管理面板 dashboard=管理面板

View file

@ -124,6 +124,7 @@ register_success=註冊成功
[modal] [modal]
yes=確認操作 yes=確認操作
no=取消操作 no=取消操作

View file

@ -410,6 +410,7 @@ repo.transfer.body=請造訪 %s 以接受或拒絕轉移,您也可以忽略它
repo.collaborator.added.subject=%s 把您加入到 %s repo.collaborator.added.subject=%s 把您加入到 %s
repo.collaborator.added.text=您已被新增為儲存庫的協作者: repo.collaborator.added.text=您已被新增為儲存庫的協作者:
[modal] [modal]
yes= yes=
no= no=

View file

@ -168,7 +168,7 @@ func TestHook(ctx *context.APIContext) {
commit := convert.ToPayloadCommit(ctx.Repo.Repository, ctx.Repo.Commit) commit := convert.ToPayloadCommit(ctx.Repo.Repository, ctx.Repo.Commit)
commitID := ctx.Repo.Commit.ID.String() commitID := ctx.Repo.Commit.ID.String()
if err := webhook_service.PrepareWebhook(hook, ctx.Repo.Repository, webhook.HookEventPush, &api.PushPayload{ if err := webhook_service.PrepareWebhook(ctx, hook, webhook.HookEventPush, &api.PushPayload{
Ref: ref, Ref: ref,
Before: commitID, Before: commitID,
After: commitID, After: commitID,

View file

@ -28,7 +28,6 @@ func TestTestHook(t *testing.T) {
assert.EqualValues(t, http.StatusNoContent, ctx.Resp.Status()) assert.EqualValues(t, http.StatusNoContent, ctx.Resp.Status())
unittest.AssertExistsAndLoadBean(t, &webhook.HookTask{ unittest.AssertExistsAndLoadBean(t, &webhook.HookTask{
RepoID: 1,
HookID: 1, HookID: 1,
}, unittest.Cond("is_delivered=?", false)) }, unittest.Cond("is_delivered=?", false))
} }

View file

@ -110,6 +110,18 @@ func Code(ctx *context.Context) {
} }
ctx.Data["RepoMaps"] = repoMaps ctx.Data["RepoMaps"] = repoMaps
if len(loadRepoIDs) != len(repoMaps) {
// Remove deleted repos from search results
cleanedSearchResults := make([]*code_indexer.Result, 0, len(repoMaps))
for _, sr := range searchResults {
if _, found := repoMaps[sr.RepoID]; found {
cleanedSearchResults = append(cleanedSearchResults, sr)
}
}
searchResults = cleanedSearchResults
}
} }
ctx.Data["SearchResults"] = searchResults ctx.Data["SearchResults"] = searchResults

View file

@ -633,7 +633,7 @@ func TestWebhook(ctx *context.Context) {
hookID := ctx.ParamsInt64(":id") hookID := ctx.ParamsInt64(":id")
w, err := webhook.GetWebhookByRepoID(ctx.Repo.Repository.ID, hookID) w, err := webhook.GetWebhookByRepoID(ctx.Repo.Repository.ID, hookID)
if err != nil { if err != nil {
ctx.Flash.Error("GetWebhookByID: " + err.Error()) ctx.Flash.Error("GetWebhookByRepoID: " + err.Error())
ctx.Status(http.StatusInternalServerError) ctx.Status(http.StatusInternalServerError)
return return
} }
@ -679,7 +679,7 @@ func TestWebhook(ctx *context.Context) {
Pusher: apiUser, Pusher: apiUser,
Sender: apiUser, Sender: apiUser,
} }
if err := webhook_service.PrepareWebhook(w, ctx.Repo.Repository, webhook.HookEventPush, p); err != nil { if err := webhook_service.PrepareWebhook(ctx, w, webhook.HookEventPush, p); err != nil {
ctx.Flash.Error("PrepareWebhook: " + err.Error()) ctx.Flash.Error("PrepareWebhook: " + err.Error())
ctx.Status(http.StatusInternalServerError) ctx.Status(http.StatusInternalServerError)
} else { } else {
@ -697,7 +697,7 @@ func ReplayWebhook(ctx *context.Context) {
return return
} }
if err := webhook_service.ReplayHookTask(w, hookTaskUUID); err != nil { if err := webhook_service.ReplayHookTask(ctx, w, hookTaskUUID); err != nil {
if webhook.IsErrHookTaskNotExist(err) { if webhook.IsErrHookTaskNotExist(err) {
ctx.NotFound("ReplayHookTask", nil) ctx.NotFound("ReplayHookTask", nil)
} else { } else {

View file

@ -591,6 +591,7 @@ func RegisterRoutes(m *web.Route) {
}) })
}, func(ctx *context.Context) { }, func(ctx *context.Context) {
ctx.Data["EnableOAuth2"] = setting.OAuth2.Enable ctx.Data["EnableOAuth2"] = setting.OAuth2.Enable
ctx.Data["EnablePackages"] = setting.Packages.Enabled
}, adminReq) }, adminReq)
// ***** END: Admin ***** // ***** END: Admin *****

View file

@ -1235,8 +1235,13 @@ func SyncAndGetUserSpecificDiff(ctx context.Context, userID int64, pull *issues_
} }
changedFiles, err := gitRepo.GetFilesChangedBetween(review.CommitSHA, latestCommit) changedFiles, err := gitRepo.GetFilesChangedBetween(review.CommitSHA, latestCommit)
// There are way too many possible errors.
// Examples are various git errors such as the commit the review was based on was gc'ed and hence doesn't exist anymore as well as unrecoverable errors where we should serve a 500 response
// Due to the current architecture and physical limitation of needing to compare explicit error messages, we can only choose one approach without the code getting ugly
// For SOME of the errors such as the gc'ed commit, it would be best to mark all files as changed
// But as that does not work for all potential errors, we simply mark all files as unchanged and drop the error which always works, even if not as good as possible
if err != nil { if err != nil {
return diff, err log.Error("Could not get changed files between %s and %s for pull request %d in repo with path %s. Assuming no changes. Error: %w", review.CommitSHA, latestCommit, pull.Index, gitRepo.Path, err)
} }
filesChangedSinceLastDiff := make(map[string]pull_model.ViewedState) filesChangedSinceLastDiff := make(map[string]pull_model.ViewedState)

View file

@ -23,7 +23,6 @@ import (
"code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/hostmatcher" "code.gitea.io/gitea/modules/hostmatcher"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/proxy" "code.gitea.io/gitea/modules/proxy"
"code.gitea.io/gitea/modules/queue" "code.gitea.io/gitea/modules/queue"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
@ -44,7 +43,7 @@ func Deliver(ctx context.Context, t *webhook_model.HookTask) error {
return return
} }
// There was a panic whilst delivering a hook... // There was a panic whilst delivering a hook...
log.Error("PANIC whilst trying to deliver webhook[%d] for repo[%d] to %s Panic: %v\nStacktrace: %s", t.ID, t.RepoID, w.URL, err, log.Stack(2)) log.Error("PANIC whilst trying to deliver webhook[%d] to %s Panic: %v\nStacktrace: %s", t.ID, w.URL, err, log.Stack(2))
}() }()
t.IsDelivered = true t.IsDelivered = true
@ -202,35 +201,6 @@ func Deliver(ctx context.Context, t *webhook_model.HookTask) error {
return nil return nil
} }
// populateDeliverHooks checks and delivers undelivered hooks.
func populateDeliverHooks(ctx context.Context) {
select {
case <-ctx.Done():
return
default:
}
ctx, _, finished := process.GetManager().AddTypedContext(ctx, "Service: DeliverHooks", process.SystemProcessType, true)
defer finished()
tasks, err := webhook_model.FindUndeliveredHookTasks()
if err != nil {
log.Error("DeliverHooks: %v", err)
return
}
// Update hook task status.
for _, t := range tasks {
select {
case <-ctx.Done():
return
default:
}
if err := addToTask(t.RepoID); err != nil {
log.Error("DeliverHook failed [%d]: %v", t.RepoID, err)
}
}
}
var ( var (
webhookHTTPClient *http.Client webhookHTTPClient *http.Client
once sync.Once once sync.Once
@ -281,13 +251,23 @@ func Init() error {
}, },
} }
hookQueue = queue.CreateUniqueQueue("webhook_sender", handle, "") hookQueue = queue.CreateUniqueQueue("webhook_sender", handle, int64(0))
if hookQueue == nil { if hookQueue == nil {
return fmt.Errorf("Unable to create webhook_sender Queue") return fmt.Errorf("Unable to create webhook_sender Queue")
} }
go graceful.GetManager().RunWithShutdownFns(hookQueue.Run) go graceful.GetManager().RunWithShutdownFns(hookQueue.Run)
populateDeliverHooks(graceful.GetManager().HammerContext()) tasks, err := webhook_model.FindUndeliveredHookTasks(graceful.GetManager().HammerContext())
if err != nil {
log.Error("FindUndeliveredHookTasks failed: %v", err)
return err
}
for _, task := range tasks {
if err := enqueueHookTask(task); err != nil {
log.Error("enqueueHookTask failed: %v", err)
}
}
return nil return nil
} }

View file

@ -7,11 +7,10 @@ package webhook
import ( import (
"context" "context"
"fmt" "fmt"
"strconv"
"strings" "strings"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
webhook_model "code.gitea.io/gitea/models/webhook" webhook_model "code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/graceful"
@ -104,49 +103,38 @@ func getPayloadBranch(p api.Payloader) string {
return "" return ""
} }
// handle passed PR IDs and test the PRs // EventSource represents the source of a webhook action. Repository and/or Owner must be set.
type EventSource struct {
Repository *repo_model.Repository
Owner *user_model.User
}
// handle delivers hook tasks
func handle(data ...queue.Data) []queue.Data { func handle(data ...queue.Data) []queue.Data {
for _, datum := range data { ctx := graceful.GetManager().HammerContext()
repoIDStr := datum.(string)
log.Trace("DeliverHooks [repo_id: %v]", repoIDStr)
repoID, err := strconv.ParseInt(repoIDStr, 10, 64) for _, taskID := range data {
task, err := webhook_model.GetHookTaskByID(ctx, taskID.(int64))
if err != nil { if err != nil {
log.Error("Invalid repo ID: %s", repoIDStr) log.Error("GetHookTaskByID failed: %v", err)
continue } else {
if err := Deliver(ctx, task); err != nil {
log.Error("webhook.Deliver failed: %v", err)
}
}
} }
tasks, err := webhook_model.FindRepoUndeliveredHookTasks(repoID)
if err != nil {
log.Error("Get repository [%d] hook tasks: %v", repoID, err)
continue
}
for _, t := range tasks {
if err = Deliver(graceful.GetManager().HammerContext(), t); err != nil {
log.Error("deliver: %v", err)
}
}
}
return nil return nil
} }
func addToTask(repoID int64) error { func enqueueHookTask(task *webhook_model.HookTask) error {
err := hookQueue.PushFunc(strconv.FormatInt(repoID, 10), nil) err := hookQueue.PushFunc(task.ID, nil)
if err != nil && err != queue.ErrAlreadyInQueue { if err != nil && err != queue.ErrAlreadyInQueue {
return err return err
} }
return nil return nil
} }
// PrepareWebhook adds special webhook to task queue for given payload.
func PrepareWebhook(w *webhook_model.Webhook, repo *repo_model.Repository, event webhook_model.HookEventType, p api.Payloader) error {
if err := prepareWebhook(w, repo, event, p); err != nil {
return err
}
return addToTask(repo.ID)
}
func checkBranch(w *webhook_model.Webhook, branch string) bool { func checkBranch(w *webhook_model.Webhook, branch string) bool {
if w.BranchFilter == "" || w.BranchFilter == "*" { if w.BranchFilter == "" || w.BranchFilter == "*" {
return true return true
@ -162,7 +150,8 @@ func checkBranch(w *webhook_model.Webhook, branch string) bool {
return g.Match(branch) return g.Match(branch)
} }
func prepareWebhook(w *webhook_model.Webhook, repo *repo_model.Repository, event webhook_model.HookEventType, p api.Payloader) error { // PrepareWebhook creates a hook task and enqueues it for processing
func PrepareWebhook(ctx context.Context, w *webhook_model.Webhook, event webhook_model.HookEventType, p api.Payloader) error {
// Skip sending if webhooks are disabled. // Skip sending if webhooks are disabled.
if setting.DisableWebhooks { if setting.DisableWebhooks {
return nil return nil
@ -207,44 +196,45 @@ func prepareWebhook(w *webhook_model.Webhook, repo *repo_model.Repository, event
payloader = p payloader = p
} }
if err = webhook_model.CreateHookTask(&webhook_model.HookTask{ task, err := webhook_model.CreateHookTask(ctx, &webhook_model.HookTask{
RepoID: repo.ID,
HookID: w.ID, HookID: w.ID,
Payloader: payloader, Payloader: payloader,
EventType: event, EventType: event,
}); err != nil { })
if err != nil {
return fmt.Errorf("CreateHookTask: %v", err) return fmt.Errorf("CreateHookTask: %v", err)
} }
return nil
return enqueueHookTask(task)
} }
// PrepareWebhooks adds new webhooks to task queue for given payload. // PrepareWebhooks adds new webhooks to task queue for given payload.
func PrepareWebhooks(repo *repo_model.Repository, event webhook_model.HookEventType, p api.Payloader) error { func PrepareWebhooks(ctx context.Context, source EventSource, event webhook_model.HookEventType, p api.Payloader) error {
if err := prepareWebhooks(db.DefaultContext, repo, event, p); err != nil { owner := source.Owner
return err
}
return addToTask(repo.ID) var ws []*webhook_model.Webhook
}
func prepareWebhooks(ctx context.Context, repo *repo_model.Repository, event webhook_model.HookEventType, p api.Payloader) error { if source.Repository != nil {
ws, err := webhook_model.ListWebhooksByOpts(ctx, &webhook_model.ListWebhookOptions{ repoHooks, err := webhook_model.ListWebhooksByOpts(ctx, &webhook_model.ListWebhookOptions{
RepoID: repo.ID, RepoID: source.Repository.ID,
IsActive: util.OptionalBoolTrue, IsActive: util.OptionalBoolTrue,
}) })
if err != nil { if err != nil {
return fmt.Errorf("GetActiveWebhooksByRepoID: %v", err) return fmt.Errorf("ListWebhooksByOpts: %v", err)
}
ws = append(ws, repoHooks...)
owner = source.Repository.MustOwner()
} }
// check if repo belongs to org and append additional webhooks // check if owner is an org and append additional webhooks
if repo.MustOwner().IsOrganization() { if owner != nil && owner.IsOrganization() {
// get hooks for org
orgHooks, err := webhook_model.ListWebhooksByOpts(ctx, &webhook_model.ListWebhookOptions{ orgHooks, err := webhook_model.ListWebhooksByOpts(ctx, &webhook_model.ListWebhookOptions{
OrgID: repo.OwnerID, OrgID: owner.ID,
IsActive: util.OptionalBoolTrue, IsActive: util.OptionalBoolTrue,
}) })
if err != nil { if err != nil {
return fmt.Errorf("GetActiveWebhooksByOrgID: %v", err) return fmt.Errorf("ListWebhooksByOpts: %v", err)
} }
ws = append(ws, orgHooks...) ws = append(ws, orgHooks...)
} }
@ -261,7 +251,7 @@ func prepareWebhooks(ctx context.Context, repo *repo_model.Repository, event web
} }
for _, w := range ws { for _, w := range ws {
if err = prepareWebhook(w, repo, event, p); err != nil { if err := PrepareWebhook(ctx, w, event, p); err != nil {
return err return err
} }
} }
@ -269,11 +259,11 @@ func prepareWebhooks(ctx context.Context, repo *repo_model.Repository, event web
} }
// ReplayHookTask replays a webhook task // ReplayHookTask replays a webhook task
func ReplayHookTask(w *webhook_model.Webhook, uuid string) error { func ReplayHookTask(ctx context.Context, w *webhook_model.Webhook, uuid string) error {
t, err := webhook_model.ReplayHookTask(w.ID, uuid) task, err := webhook_model.ReplayHookTask(ctx, w.ID, uuid)
if err != nil { if err != nil {
return err return err
} }
return addToTask(t.RepoID) return enqueueHookTask(task)
} }

View file

@ -7,6 +7,7 @@ package webhook
import ( import (
"testing" "testing"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
webhook_model "code.gitea.io/gitea/models/webhook" webhook_model "code.gitea.io/gitea/models/webhook"
@ -32,12 +33,12 @@ func TestPrepareWebhooks(t *testing.T) {
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
hookTasks := []*webhook_model.HookTask{ hookTasks := []*webhook_model.HookTask{
{RepoID: repo.ID, HookID: 1, EventType: webhook_model.HookEventPush}, {HookID: 1, EventType: webhook_model.HookEventPush},
} }
for _, hookTask := range hookTasks { for _, hookTask := range hookTasks {
unittest.AssertNotExistsBean(t, hookTask) unittest.AssertNotExistsBean(t, hookTask)
} }
assert.NoError(t, PrepareWebhooks(repo, webhook_model.HookEventPush, &api.PushPayload{Commits: []*api.PayloadCommit{{}}})) assert.NoError(t, PrepareWebhooks(db.DefaultContext, EventSource{Repository: repo}, webhook_model.HookEventPush, &api.PushPayload{Commits: []*api.PayloadCommit{{}}}))
for _, hookTask := range hookTasks { for _, hookTask := range hookTasks {
unittest.AssertExistsAndLoadBean(t, hookTask) unittest.AssertExistsAndLoadBean(t, hookTask)
} }
@ -48,13 +49,13 @@ func TestPrepareWebhooksBranchFilterMatch(t *testing.T) {
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
hookTasks := []*webhook_model.HookTask{ hookTasks := []*webhook_model.HookTask{
{RepoID: repo.ID, HookID: 4, EventType: webhook_model.HookEventPush}, {HookID: 4, EventType: webhook_model.HookEventPush},
} }
for _, hookTask := range hookTasks { for _, hookTask := range hookTasks {
unittest.AssertNotExistsBean(t, hookTask) unittest.AssertNotExistsBean(t, hookTask)
} }
// this test also ensures that * doesn't handle / in any special way (like shell would) // this test also ensures that * doesn't handle / in any special way (like shell would)
assert.NoError(t, PrepareWebhooks(repo, webhook_model.HookEventPush, &api.PushPayload{Ref: "refs/heads/feature/7791", Commits: []*api.PayloadCommit{{}}})) assert.NoError(t, PrepareWebhooks(db.DefaultContext, EventSource{Repository: repo}, webhook_model.HookEventPush, &api.PushPayload{Ref: "refs/heads/feature/7791", Commits: []*api.PayloadCommit{{}}}))
for _, hookTask := range hookTasks { for _, hookTask := range hookTasks {
unittest.AssertExistsAndLoadBean(t, hookTask) unittest.AssertExistsAndLoadBean(t, hookTask)
} }
@ -65,12 +66,12 @@ func TestPrepareWebhooksBranchFilterNoMatch(t *testing.T) {
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
hookTasks := []*webhook_model.HookTask{ hookTasks := []*webhook_model.HookTask{
{RepoID: repo.ID, HookID: 4, EventType: webhook_model.HookEventPush}, {HookID: 4, EventType: webhook_model.HookEventPush},
} }
for _, hookTask := range hookTasks { for _, hookTask := range hookTasks {
unittest.AssertNotExistsBean(t, hookTask) unittest.AssertNotExistsBean(t, hookTask)
} }
assert.NoError(t, PrepareWebhooks(repo, webhook_model.HookEventPush, &api.PushPayload{Ref: "refs/heads/fix_weird_bug"})) assert.NoError(t, PrepareWebhooks(db.DefaultContext, EventSource{Repository: repo}, webhook_model.HookEventPush, &api.PushPayload{Ref: "refs/heads/fix_weird_bug"}))
for _, hookTask := range hookTasks { for _, hookTask := range hookTasks {
unittest.AssertNotExistsBean(t, hookTask) unittest.AssertNotExistsBean(t, hookTask)

View file

@ -12,9 +12,11 @@
<a class="{{if .PageIsAdminRepositories}}active{{end}} item" href="{{AppSubUrl}}/admin/repos"> <a class="{{if .PageIsAdminRepositories}}active{{end}} item" href="{{AppSubUrl}}/admin/repos">
{{.locale.Tr "admin.repositories"}} {{.locale.Tr "admin.repositories"}}
</a> </a>
{{if .EnablePackages}}
<a class="{{if .PageIsAdminPackages}}active{{end}} item" href="{{AppSubUrl}}/admin/packages"> <a class="{{if .PageIsAdminPackages}}active{{end}} item" href="{{AppSubUrl}}/admin/packages">
{{.locale.Tr "packages.title"}} {{.locale.Tr "packages.title"}}
</a> </a>
{{end}}
{{if not DisableWebhooks}} {{if not DisableWebhooks}}
<a class="{{if or .PageIsAdminDefaultHooks .PageIsAdminSystemHooks}}active{{end}} item" href="{{AppSubUrl}}/admin/hooks"> <a class="{{if or .PageIsAdminDefaultHooks .PageIsAdminSystemHooks}}active{{end}} item" href="{{AppSubUrl}}/admin/hooks">
{{.locale.Tr "admin.hooks"}} {{.locale.Tr "admin.hooks"}}

View file

@ -70,7 +70,7 @@
<!-- If home page, show new PR. If not, show breadcrumb --> <!-- If home page, show new PR. If not, show breadcrumb -->
{{if eq $n 0}} {{if eq $n 0}}
{{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}} {{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}}
<a href="{{.BaseRepo.Link}}/compare/{{PathEscapeSegments .BaseRepo.DefaultBranch}}...{{if ne .Repository.Owner.Name .BaseRepo.Owner.Name}}{{PathEscape .Repository.Owner.Name}}{{if .BaseRepo.IsFork}}/{{PathEscape .Repository.Name}}{{end}}:{{end}}{{PathEscapeSegments .BranchName}}"> <a href="{{CompareLink .BaseRepo .Repository .BranchName}}">
<button id="new-pull-request" class="ui compact basic button tooltip" data-content="{{if .PullRequestCtx.Allowed}}{{.locale.Tr "repo.pulls.compare_changes"}}{{else}}{{.locale.Tr "action.compare_branch"}}{{end}}"><span class="text">{{svg "octicon-git-pull-request"}}</span></button> <button id="new-pull-request" class="ui compact basic button tooltip" data-content="{{if .PullRequestCtx.Allowed}}{{.locale.Tr "repo.pulls.compare_changes"}}{{else}}{{.locale.Tr "action.compare_branch"}}{{end}}"><span class="text">{{svg "octicon-git-pull-request"}}</span></button>
</a> </a>
{{end}} {{end}}

View file

@ -18,6 +18,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"sync/atomic"
"testing" "testing"
"time" "time"
@ -263,19 +264,19 @@ var tokenCounter int64
func getTokenForLoggedInUser(t testing.TB, session *TestSession) string { func getTokenForLoggedInUser(t testing.TB, session *TestSession) string {
t.Helper() t.Helper()
tokenCounter++
req := NewRequest(t, "GET", "/user/settings/applications") req := NewRequest(t, "GET", "/user/settings/applications")
resp := session.MakeRequest(t, req, http.StatusOK) resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body) doc := NewHTMLParser(t, resp.Body)
req = NewRequestWithValues(t, "POST", "/user/settings/applications", map[string]string{ req = NewRequestWithValues(t, "POST", "/user/settings/applications", map[string]string{
"_csrf": doc.GetCSRF(), "_csrf": doc.GetCSRF(),
"name": fmt.Sprintf("api-testing-token-%d", tokenCounter), "name": fmt.Sprintf("api-testing-token-%d", atomic.AddInt64(&tokenCounter, 1)),
}) })
session.MakeRequest(t, req, http.StatusSeeOther) session.MakeRequest(t, req, http.StatusSeeOther)
req = NewRequest(t, "GET", "/user/settings/applications") req = NewRequest(t, "GET", "/user/settings/applications")
resp = session.MakeRequest(t, req, http.StatusOK) resp = session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body) htmlDoc := NewHTMLParser(t, resp.Body)
token := htmlDoc.doc.Find(".ui.info p").Text() token := htmlDoc.doc.Find(".ui.info p").Text()
assert.NotEmpty(t, token)
return token return token
} }

View file

@ -1371,6 +1371,14 @@ a.ui.card:hover,
border-color: var(--color-secondary); border-color: var(--color-secondary);
} }
.color-preview {
display: inline-block;
margin-left: .4em;
height: .67em;
width: .67em;
border-radius: .15em;
}
footer { footer {
background-color: var(--color-footer); background-color: var(--color-footer);
border-top: 1px solid var(--color-secondary); border-top: 1px solid var(--color-secondary);