forked from NYANDEV/forgejo
bd9ac9ac6f
Refs: https://codeberg.org/forgejo/forgejo/pulls/1595 (cherry picked from commit 35b962e6313df748e8855b4dfbf748f095ea1003) (cherry picked from commit 1004e35b84a4a0deae999cb8a4c2924b85b47c8b) (cherry picked from commit af51dd594db229f7a986325a6070d33782d85d28) (cherry picked from commit ef10fae29607533db3616a23043cc0f2fc2dc71a) (cherry picked from commit ff8027ed1b0a1274b7b6e4840e31e2ad4d18b159) (cherry picked from commit2540ff52ef
) (cherry picked from commit57b4d775e1
) (cherry picked from commit c388aba9b50bbdd7eb13518d91f8e00c5d1bce18) (cherry picked from commit 7a3b605c11d5a9033b7c3db882b606fb009afca3) (cherry picked from commit cc02354d0a6872e761d8215b6630a1467c6f8e75) (cherry picked from commit e11c5ce82aeaaa62ced4bead72aa3d37453b792a) (cherry picked from commit d1e7798bb2b32eb3a8bd1be669191d3e3a9a2510) (cherry picked from commit 1813af7391a47b79b4cd44d4feb64e3002032db6) (cherry picked from commit 0d55a8894508aae4225d76235d4bd7a9f862a849)
325 lines
13 KiB
Go
325 lines
13 KiB
Go
// Copyright 2021 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package feed
|
|
|
|
import (
|
|
"fmt"
|
|
"html"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
|
|
activities_model "code.gitea.io/gitea/models/activities"
|
|
repo_model "code.gitea.io/gitea/models/repo"
|
|
"code.gitea.io/gitea/modules/context"
|
|
"code.gitea.io/gitea/modules/markup"
|
|
"code.gitea.io/gitea/modules/markup/markdown"
|
|
"code.gitea.io/gitea/modules/setting"
|
|
"code.gitea.io/gitea/modules/templates"
|
|
"code.gitea.io/gitea/modules/util"
|
|
|
|
"github.com/gorilla/feeds"
|
|
"github.com/jaytaylor/html2text"
|
|
)
|
|
|
|
func toBranchLink(ctx *context.Context, act *activities_model.Action) string {
|
|
return act.GetRepoAbsoluteLink(ctx) + "/src/branch/" + util.PathEscapeSegments(act.GetBranch())
|
|
}
|
|
|
|
func toTagLink(ctx *context.Context, act *activities_model.Action) string {
|
|
return act.GetRepoAbsoluteLink(ctx) + "/src/tag/" + util.PathEscapeSegments(act.GetTag())
|
|
}
|
|
|
|
func toIssueLink(ctx *context.Context, act *activities_model.Action) string {
|
|
return act.GetRepoAbsoluteLink(ctx) + "/issues/" + url.PathEscape(act.GetIssueInfos()[0])
|
|
}
|
|
|
|
func toPullLink(ctx *context.Context, act *activities_model.Action) string {
|
|
return act.GetRepoAbsoluteLink(ctx) + "/pulls/" + url.PathEscape(act.GetIssueInfos()[0])
|
|
}
|
|
|
|
func toSrcLink(ctx *context.Context, act *activities_model.Action) string {
|
|
return act.GetRepoAbsoluteLink(ctx) + "/src/" + util.PathEscapeSegments(act.GetBranch())
|
|
}
|
|
|
|
func toReleaseLink(ctx *context.Context, act *activities_model.Action) string {
|
|
return act.GetRepoAbsoluteLink(ctx) + "/releases/tag/" + util.PathEscapeSegments(act.GetBranch())
|
|
}
|
|
|
|
// renderMarkdown creates a minimal markdown render context from an action.
|
|
// If rendering fails, the original markdown text is returned
|
|
func renderMarkdown(ctx *context.Context, act *activities_model.Action, content string) string {
|
|
markdownCtx := &markup.RenderContext{
|
|
Ctx: ctx,
|
|
Links: markup.Links{
|
|
Base: act.GetRepoLink(ctx),
|
|
},
|
|
Type: markdown.MarkupName,
|
|
Metas: map[string]string{
|
|
"user": act.GetRepoUserName(ctx),
|
|
"repo": act.GetRepoName(ctx),
|
|
},
|
|
}
|
|
markdown, err := markdown.RenderString(markdownCtx, content)
|
|
if err != nil {
|
|
return content
|
|
}
|
|
return markdown
|
|
}
|
|
|
|
// feedActionsToFeedItems convert gitea's Action feed to feeds Item
|
|
func feedActionsToFeedItems(ctx *context.Context, actions activities_model.ActionList) (items []*feeds.Item, err error) {
|
|
for _, act := range actions {
|
|
act.LoadActUser(ctx)
|
|
|
|
var content, desc, title string
|
|
|
|
link := &feeds.Link{Href: act.GetCommentHTMLURL(ctx)}
|
|
|
|
// title
|
|
title = act.ActUser.DisplayName() + " "
|
|
switch act.OpType {
|
|
case activities_model.ActionCreateRepo:
|
|
title += ctx.TrHTMLEscapeArgs("action.create_repo", act.GetRepoAbsoluteLink(ctx), act.ShortRepoPath(ctx))
|
|
link.Href = act.GetRepoAbsoluteLink(ctx)
|
|
case activities_model.ActionRenameRepo:
|
|
title += ctx.TrHTMLEscapeArgs("action.rename_repo", act.GetContent(), act.GetRepoAbsoluteLink(ctx), act.ShortRepoPath(ctx))
|
|
link.Href = act.GetRepoAbsoluteLink(ctx)
|
|
case activities_model.ActionCommitRepo:
|
|
link.Href = toBranchLink(ctx, act)
|
|
if len(act.Content) != 0 {
|
|
title += ctx.TrHTMLEscapeArgs("action.commit_repo", act.GetRepoAbsoluteLink(ctx), link.Href, act.GetBranch(), act.ShortRepoPath(ctx))
|
|
} else {
|
|
title += ctx.TrHTMLEscapeArgs("action.create_branch", act.GetRepoAbsoluteLink(ctx), link.Href, act.GetBranch(), act.ShortRepoPath(ctx))
|
|
}
|
|
case activities_model.ActionCreateIssue:
|
|
link.Href = toIssueLink(ctx, act)
|
|
title += ctx.TrHTMLEscapeArgs("action.create_issue", link.Href, act.GetIssueInfos()[0], act.ShortRepoPath(ctx))
|
|
case activities_model.ActionCreatePullRequest:
|
|
link.Href = toPullLink(ctx, act)
|
|
title += ctx.TrHTMLEscapeArgs("action.create_pull_request", link.Href, act.GetIssueInfos()[0], act.ShortRepoPath(ctx))
|
|
case activities_model.ActionTransferRepo:
|
|
link.Href = act.GetRepoAbsoluteLink(ctx)
|
|
title += ctx.TrHTMLEscapeArgs("action.transfer_repo", act.GetContent(), act.GetRepoAbsoluteLink(ctx), act.ShortRepoPath(ctx))
|
|
case activities_model.ActionPushTag:
|
|
link.Href = toTagLink(ctx, act)
|
|
title += ctx.TrHTMLEscapeArgs("action.push_tag", act.GetRepoAbsoluteLink(ctx), link.Href, act.GetTag(), act.ShortRepoPath(ctx))
|
|
case activities_model.ActionCommentIssue:
|
|
issueLink := toIssueLink(ctx, act)
|
|
if link.Href == "#" {
|
|
link.Href = issueLink
|
|
}
|
|
title += ctx.TrHTMLEscapeArgs("action.comment_issue", issueLink, act.GetIssueInfos()[0], act.ShortRepoPath(ctx))
|
|
case activities_model.ActionMergePullRequest:
|
|
pullLink := toPullLink(ctx, act)
|
|
if link.Href == "#" {
|
|
link.Href = pullLink
|
|
}
|
|
title += ctx.TrHTMLEscapeArgs("action.merge_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath(ctx))
|
|
case activities_model.ActionAutoMergePullRequest:
|
|
pullLink := toPullLink(ctx, act)
|
|
if link.Href == "#" {
|
|
link.Href = pullLink
|
|
}
|
|
title += ctx.TrHTMLEscapeArgs("action.auto_merge_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath(ctx))
|
|
case activities_model.ActionCloseIssue:
|
|
issueLink := toIssueLink(ctx, act)
|
|
if link.Href == "#" {
|
|
link.Href = issueLink
|
|
}
|
|
title += ctx.TrHTMLEscapeArgs("action.close_issue", issueLink, act.GetIssueInfos()[0], act.ShortRepoPath(ctx))
|
|
case activities_model.ActionReopenIssue:
|
|
issueLink := toIssueLink(ctx, act)
|
|
if link.Href == "#" {
|
|
link.Href = issueLink
|
|
}
|
|
title += ctx.TrHTMLEscapeArgs("action.reopen_issue", issueLink, act.GetIssueInfos()[0], act.ShortRepoPath(ctx))
|
|
case activities_model.ActionClosePullRequest:
|
|
pullLink := toPullLink(ctx, act)
|
|
if link.Href == "#" {
|
|
link.Href = pullLink
|
|
}
|
|
title += ctx.TrHTMLEscapeArgs("action.close_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath(ctx))
|
|
case activities_model.ActionReopenPullRequest:
|
|
pullLink := toPullLink(ctx, act)
|
|
if link.Href == "#" {
|
|
link.Href = pullLink
|
|
}
|
|
title += ctx.TrHTMLEscapeArgs("action.reopen_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath(ctx))
|
|
case activities_model.ActionDeleteTag:
|
|
link.Href = act.GetRepoAbsoluteLink(ctx)
|
|
title += ctx.TrHTMLEscapeArgs("action.delete_tag", act.GetRepoAbsoluteLink(ctx), act.GetTag(), act.ShortRepoPath(ctx))
|
|
case activities_model.ActionDeleteBranch:
|
|
link.Href = act.GetRepoAbsoluteLink(ctx)
|
|
title += ctx.TrHTMLEscapeArgs("action.delete_branch", act.GetRepoAbsoluteLink(ctx), html.EscapeString(act.GetBranch()), act.ShortRepoPath(ctx))
|
|
case activities_model.ActionMirrorSyncPush:
|
|
srcLink := toSrcLink(ctx, act)
|
|
if link.Href == "#" {
|
|
link.Href = srcLink
|
|
}
|
|
title += ctx.TrHTMLEscapeArgs("action.mirror_sync_push", act.GetRepoAbsoluteLink(ctx), srcLink, act.GetBranch(), act.ShortRepoPath(ctx))
|
|
case activities_model.ActionMirrorSyncCreate:
|
|
srcLink := toSrcLink(ctx, act)
|
|
if link.Href == "#" {
|
|
link.Href = srcLink
|
|
}
|
|
title += ctx.TrHTMLEscapeArgs("action.mirror_sync_create", act.GetRepoAbsoluteLink(ctx), srcLink, act.GetBranch(), act.ShortRepoPath(ctx))
|
|
case activities_model.ActionMirrorSyncDelete:
|
|
link.Href = act.GetRepoAbsoluteLink(ctx)
|
|
title += ctx.TrHTMLEscapeArgs("action.mirror_sync_delete", act.GetRepoAbsoluteLink(ctx), act.GetBranch(), act.ShortRepoPath(ctx))
|
|
case activities_model.ActionApprovePullRequest:
|
|
pullLink := toPullLink(ctx, act)
|
|
title += ctx.TrHTMLEscapeArgs("action.approve_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath(ctx))
|
|
case activities_model.ActionRejectPullRequest:
|
|
pullLink := toPullLink(ctx, act)
|
|
title += ctx.TrHTMLEscapeArgs("action.reject_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath(ctx))
|
|
case activities_model.ActionCommentPull:
|
|
pullLink := toPullLink(ctx, act)
|
|
title += ctx.TrHTMLEscapeArgs("action.comment_pull", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath(ctx))
|
|
case activities_model.ActionPublishRelease:
|
|
releaseLink := toReleaseLink(ctx, act)
|
|
if link.Href == "#" {
|
|
link.Href = releaseLink
|
|
}
|
|
title += ctx.TrHTMLEscapeArgs("action.publish_release", act.GetRepoAbsoluteLink(ctx), releaseLink, act.ShortRepoPath(ctx), act.Content)
|
|
case activities_model.ActionPullReviewDismissed:
|
|
pullLink := toPullLink(ctx, act)
|
|
title += ctx.TrHTMLEscapeArgs("action.review_dismissed", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath(ctx), act.GetIssueInfos()[1])
|
|
case activities_model.ActionStarRepo:
|
|
link.Href = act.GetRepoAbsoluteLink(ctx)
|
|
title += ctx.TrHTMLEscapeArgs("action.starred_repo", act.GetRepoAbsoluteLink(ctx), act.GetRepoPath(ctx))
|
|
case activities_model.ActionWatchRepo:
|
|
link.Href = act.GetRepoAbsoluteLink(ctx)
|
|
title += ctx.TrHTMLEscapeArgs("action.watched_repo", act.GetRepoAbsoluteLink(ctx), act.GetRepoPath(ctx))
|
|
default:
|
|
return nil, fmt.Errorf("unknown action type: %v", act.OpType)
|
|
}
|
|
|
|
// description & content
|
|
{
|
|
switch act.OpType {
|
|
case activities_model.ActionCommitRepo, activities_model.ActionMirrorSyncPush:
|
|
push := templates.ActionContent2Commits(act)
|
|
|
|
for _, commit := range push.Commits {
|
|
if len(desc) != 0 {
|
|
desc += "\n\n"
|
|
}
|
|
desc += fmt.Sprintf("<a href=\"%s\">%s</a>\n%s",
|
|
html.EscapeString(fmt.Sprintf("%s/commit/%s", act.GetRepoAbsoluteLink(ctx), commit.Sha1)),
|
|
commit.Sha1,
|
|
templates.RenderCommitMessage(ctx, commit.Message, nil),
|
|
)
|
|
}
|
|
|
|
if push.Len > 1 {
|
|
link = &feeds.Link{Href: fmt.Sprintf("%s/%s", setting.AppSubURL, push.CompareURL)}
|
|
} else if push.Len == 1 {
|
|
link = &feeds.Link{Href: fmt.Sprintf("%s/commit/%s", act.GetRepoAbsoluteLink(ctx), push.Commits[0].Sha1)}
|
|
}
|
|
|
|
case activities_model.ActionCreateIssue, activities_model.ActionCreatePullRequest:
|
|
desc = strings.Join(act.GetIssueInfos(), "#")
|
|
content = renderMarkdown(ctx, act, act.GetIssueContent(ctx))
|
|
case activities_model.ActionCommentIssue, activities_model.ActionApprovePullRequest, activities_model.ActionRejectPullRequest, activities_model.ActionCommentPull:
|
|
desc = act.GetIssueTitle(ctx)
|
|
comment := act.GetIssueInfos()[1]
|
|
if len(comment) != 0 {
|
|
desc += "\n\n" + renderMarkdown(ctx, act, comment)
|
|
}
|
|
case activities_model.ActionMergePullRequest, activities_model.ActionAutoMergePullRequest:
|
|
desc = act.GetIssueInfos()[1]
|
|
case activities_model.ActionCloseIssue, activities_model.ActionReopenIssue, activities_model.ActionClosePullRequest, activities_model.ActionReopenPullRequest:
|
|
desc = act.GetIssueTitle(ctx)
|
|
case activities_model.ActionPullReviewDismissed:
|
|
desc = ctx.Tr("action.review_dismissed_reason") + "\n\n" + act.GetIssueInfos()[2]
|
|
}
|
|
}
|
|
if len(content) == 0 {
|
|
content = desc
|
|
}
|
|
|
|
// It's a common practice for feed generators to use plain text titles.
|
|
// See https://codeberg.org/forgejo/forgejo/pulls/1595
|
|
plainTitle, err := html2text.FromString(title, html2text.Options{OmitLinks: true})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
items = append(items, &feeds.Item{
|
|
Title: plainTitle,
|
|
Link: link,
|
|
Description: desc,
|
|
Author: &feeds.Author{
|
|
Name: act.ActUser.DisplayName(),
|
|
Email: act.ActUser.GetEmail(),
|
|
},
|
|
Id: fmt.Sprintf("%v: %v", strconv.FormatInt(act.ID, 10), link.Href),
|
|
Created: act.CreatedUnix.AsTime(),
|
|
Content: content,
|
|
})
|
|
}
|
|
return items, err
|
|
}
|
|
|
|
// GetFeedType return if it is a feed request and altered name and feed type.
|
|
func GetFeedType(name string, req *http.Request) (bool, string, string) {
|
|
if strings.HasSuffix(name, ".rss") ||
|
|
strings.Contains(req.Header.Get("Accept"), "application/rss+xml") {
|
|
return true, strings.TrimSuffix(name, ".rss"), "rss"
|
|
}
|
|
|
|
if strings.HasSuffix(name, ".atom") ||
|
|
strings.Contains(req.Header.Get("Accept"), "application/atom+xml") {
|
|
return true, strings.TrimSuffix(name, ".atom"), "atom"
|
|
}
|
|
|
|
return false, name, ""
|
|
}
|
|
|
|
// feedActionsToFeedItems convert gitea's Repo's Releases to feeds Item
|
|
func releasesToFeedItems(ctx *context.Context, releases []*repo_model.Release, isReleasesOnly bool) (items []*feeds.Item, err error) {
|
|
for _, rel := range releases {
|
|
err := rel.LoadAttributes(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var title, content string
|
|
|
|
if rel.IsTag {
|
|
title = rel.TagName
|
|
} else {
|
|
title = rel.Title
|
|
}
|
|
|
|
link := &feeds.Link{Href: rel.HTMLURL()}
|
|
content, err = markdown.RenderString(&markup.RenderContext{
|
|
Ctx: ctx,
|
|
Links: markup.Links{
|
|
Base: rel.Repo.Link(),
|
|
},
|
|
Metas: rel.Repo.ComposeMetas(ctx),
|
|
}, rel.Note)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
items = append(items, &feeds.Item{
|
|
Title: title,
|
|
Link: link,
|
|
Created: rel.CreatedUnix.AsTime(),
|
|
Author: &feeds.Author{
|
|
Name: rel.Publisher.DisplayName(),
|
|
Email: rel.Publisher.GetEmail(),
|
|
},
|
|
Id: fmt.Sprintf("%v: %v", strconv.FormatInt(rel.ID, 10), link.Href),
|
|
Content: content,
|
|
})
|
|
}
|
|
|
|
return items, err
|
|
}
|