diff options
Diffstat (limited to 'routers/web/feed')
-rw-r--r-- | routers/web/feed/branch.go | 50 | ||||
-rw-r--r-- | routers/web/feed/convert.go | 344 | ||||
-rw-r--r-- | routers/web/feed/file.go | 62 | ||||
-rw-r--r-- | routers/web/feed/profile.go | 87 | ||||
-rw-r--r-- | routers/web/feed/release.go | 52 | ||||
-rw-r--r-- | routers/web/feed/render.go | 19 | ||||
-rw-r--r-- | routers/web/feed/repo.go | 44 |
7 files changed, 658 insertions, 0 deletions
diff --git a/routers/web/feed/branch.go b/routers/web/feed/branch.go new file mode 100644 index 0000000..80ce2ad --- /dev/null +++ b/routers/web/feed/branch.go @@ -0,0 +1,50 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package feed + +import ( + "fmt" + "strings" + "time" + + "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/services/context" + + "github.com/gorilla/feeds" +) + +// ShowBranchFeed shows tags and/or releases on the repo as RSS / Atom feed +func ShowBranchFeed(ctx *context.Context, repo *repo.Repository, formatType string) { + commits, err := ctx.Repo.Commit.CommitsByRange(0, 10, "") + if err != nil { + ctx.ServerError("ShowBranchFeed", err) + return + } + + title := fmt.Sprintf("Latest commits for branch %s", ctx.Repo.BranchName) + link := &feeds.Link{Href: repo.HTMLURL() + "/" + ctx.Repo.BranchNameSubURL()} + + feed := &feeds.Feed{ + Title: title, + Link: link, + Description: repo.Description, + Created: time.Now(), + } + + for _, commit := range commits { + feed.Items = append(feed.Items, &feeds.Item{ + Id: commit.ID.String(), + Title: strings.TrimSpace(strings.Split(commit.Message(), "\n")[0]), + Link: &feeds.Link{Href: repo.HTMLURL() + "/commit/" + commit.ID.String()}, + Author: &feeds.Author{ + Name: commit.Author.Name, + Email: commit.Author.Email, + }, + Description: commit.Message(), + Content: commit.Message(), + }) + } + + writeFeed(ctx, feed, formatType) +} diff --git a/routers/web/feed/convert.go b/routers/web/feed/convert.go new file mode 100644 index 0000000..d3d01a8 --- /dev/null +++ b/routers/web/feed/convert.go @@ -0,0 +1,344 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package feed + +import ( + "fmt" + "html" + "html/template" + "net/http" + "net/url" + "strconv" + "strings" + + activities_model "code.gitea.io/gitea/models/activities" + issues_model "code.gitea.io/gitea/models/issues" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/modules/log" + "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" + "code.gitea.io/gitea/services/context" + + "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) template.HTML { + 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 templates.SanitizeHTML(content) // old code did so: use SanitizeHTML to render in tmpl + } + 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) + + // TODO: the code seems quite strange (maybe not right) + // sometimes it uses text content but sometimes it uses HTML content + // it should clearly defines which kind of content it should use for the feed items: plan text or rich HTML + var title, desc string + var content template.HTML + + link := &feeds.Link{Href: act.GetCommentHTMLURL(ctx)} + + // title + title = act.ActUser.GetDisplayName() + " " + var titleExtra template.HTML + switch act.OpType { + case activities_model.ActionCreateRepo: + titleExtra = ctx.Locale.Tr("action.create_repo", act.GetRepoAbsoluteLink(ctx), act.ShortRepoPath(ctx)) + link.Href = act.GetRepoAbsoluteLink(ctx) + case activities_model.ActionRenameRepo: + titleExtra = ctx.Locale.Tr("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 { + titleExtra = ctx.Locale.Tr("action.commit_repo", act.GetRepoAbsoluteLink(ctx), link.Href, act.GetBranch(), act.ShortRepoPath(ctx)) + } else { + titleExtra = ctx.Locale.Tr("action.create_branch", act.GetRepoAbsoluteLink(ctx), link.Href, act.GetBranch(), act.ShortRepoPath(ctx)) + } + case activities_model.ActionCreateIssue: + link.Href = toIssueLink(ctx, act) + titleExtra = ctx.Locale.Tr("action.create_issue", link.Href, act.GetIssueInfos()[0], act.ShortRepoPath(ctx)) + case activities_model.ActionCreatePullRequest: + link.Href = toPullLink(ctx, act) + titleExtra = ctx.Locale.Tr("action.create_pull_request", link.Href, act.GetIssueInfos()[0], act.ShortRepoPath(ctx)) + case activities_model.ActionTransferRepo: + link.Href = act.GetRepoAbsoluteLink(ctx) + titleExtra = ctx.Locale.Tr("action.transfer_repo", act.GetContent(), act.GetRepoAbsoluteLink(ctx), act.ShortRepoPath(ctx)) + case activities_model.ActionPushTag: + link.Href = toTagLink(ctx, act) + titleExtra = ctx.Locale.Tr("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 + } + titleExtra = ctx.Locale.Tr("action.comment_issue", issueLink, act.GetIssueInfos()[0], act.ShortRepoPath(ctx)) + case activities_model.ActionMergePullRequest: + pullLink := toPullLink(ctx, act) + if link.Href == "#" { + link.Href = pullLink + } + titleExtra = ctx.Locale.Tr("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 + } + titleExtra = ctx.Locale.Tr("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 + } + titleExtra = ctx.Locale.Tr("action.close_issue", issueLink, act.GetIssueInfos()[0], act.ShortRepoPath(ctx)) + case activities_model.ActionReopenIssue: + issueLink := toIssueLink(ctx, act) + if link.Href == "#" { + link.Href = issueLink + } + titleExtra = ctx.Locale.Tr("action.reopen_issue", issueLink, act.GetIssueInfos()[0], act.ShortRepoPath(ctx)) + case activities_model.ActionClosePullRequest: + pullLink := toPullLink(ctx, act) + if link.Href == "#" { + link.Href = pullLink + } + titleExtra = ctx.Locale.Tr("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 + } + titleExtra = ctx.Locale.Tr("action.reopen_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath(ctx)) + case activities_model.ActionDeleteTag: + link.Href = act.GetRepoAbsoluteLink(ctx) + titleExtra = ctx.Locale.Tr("action.delete_tag", act.GetRepoAbsoluteLink(ctx), act.GetTag(), act.ShortRepoPath(ctx)) + case activities_model.ActionDeleteBranch: + link.Href = act.GetRepoAbsoluteLink(ctx) + titleExtra = ctx.Locale.Tr("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 + } + titleExtra = ctx.Locale.Tr("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 + } + titleExtra = ctx.Locale.Tr("action.mirror_sync_create", act.GetRepoAbsoluteLink(ctx), srcLink, act.GetBranch(), act.ShortRepoPath(ctx)) + case activities_model.ActionMirrorSyncDelete: + link.Href = act.GetRepoAbsoluteLink(ctx) + titleExtra = ctx.Locale.Tr("action.mirror_sync_delete", act.GetRepoAbsoluteLink(ctx), act.GetBranch(), act.ShortRepoPath(ctx)) + case activities_model.ActionApprovePullRequest: + pullLink := toPullLink(ctx, act) + titleExtra = ctx.Locale.Tr("action.approve_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath(ctx)) + case activities_model.ActionRejectPullRequest: + pullLink := toPullLink(ctx, act) + titleExtra = ctx.Locale.Tr("action.reject_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath(ctx)) + case activities_model.ActionCommentPull: + pullLink := toPullLink(ctx, act) + titleExtra = ctx.Locale.Tr("action.comment_pull", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath(ctx)) + case activities_model.ActionPublishRelease: + releaseLink := toReleaseLink(ctx, act) + if link.Href == "#" { + link.Href = releaseLink + } + titleExtra = ctx.Locale.Tr("action.publish_release", act.GetRepoAbsoluteLink(ctx), releaseLink, act.ShortRepoPath(ctx), act.Content) + case activities_model.ActionPullReviewDismissed: + pullLink := toPullLink(ctx, act) + titleExtra = ctx.Locale.Tr("action.review_dismissed", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath(ctx), act.GetIssueInfos()[1]) + case activities_model.ActionStarRepo: + link.Href = act.GetRepoAbsoluteLink(ctx) + titleExtra = ctx.Locale.Tr("action.starred_repo", act.GetRepoAbsoluteLink(ctx), act.GetRepoPath(ctx)) + case activities_model.ActionWatchRepo: + link.Href = act.GetRepoAbsoluteLink(ctx) + titleExtra = ctx.Locale.Tr("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 strings.HasSuffix(comment, "…") { + // Comment was truncated get the full content from the database. + // This truncation is done in `NotifyCreateIssueComment`. + commentModel, err := issues_model.GetCommentByID(ctx, act.CommentID) + if err != nil { + log.Error("Couldn't get comment[%d] for RSS feed: %v", act.CommentID, err) + } else { + comment = commentModel.Content + } + } + if len(comment) != 0 { + desc += "\n\n" + string(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.Locale.TrString("action.review_dismissed_reason") + "\n\n" + act.GetIssueInfos()[2] + } + } + if len(content) == 0 { + content = templates.SanitizeHTML(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+" "+string(titleExtra), html2text.Options{OmitLinks: true}) + if err != nil { + return nil, err + } + + items = append(items, &feeds.Item{ + Title: plainTitle, + Link: link, + Description: desc, + IsPermaLink: "false", + Author: &feeds.Author{ + Name: act.ActUser.GetDisplayName(), + Email: act.ActUser.GetEmail(), + }, + Id: fmt.Sprintf("%v: %v", strconv.FormatInt(act.ID, 10), link.Href), + Created: act.CreatedUnix.AsTime(), + Content: string(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) (items []*feeds.Item, err error) { + for _, rel := range releases { + err := rel.LoadAttributes(ctx) + if err != nil { + return nil, err + } + + var title string + var content template.HTML + + 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.GetDisplayName(), + Email: rel.Publisher.GetEmail(), + }, + Id: fmt.Sprintf("%v: %v", strconv.FormatInt(rel.ID, 10), link.Href), + Content: string(content), + }) + } + + return items, err +} diff --git a/routers/web/feed/file.go b/routers/web/feed/file.go new file mode 100644 index 0000000..1ab768f --- /dev/null +++ b/routers/web/feed/file.go @@ -0,0 +1,62 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package feed + +import ( + "fmt" + "strings" + "time" + + "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/context" + + "github.com/gorilla/feeds" +) + +// ShowFileFeed shows tags and/or releases on the repo as RSS / Atom feed +func ShowFileFeed(ctx *context.Context, repo *repo.Repository, formatType string) { + fileName := ctx.Repo.TreePath + if len(fileName) == 0 { + return + } + commits, err := ctx.Repo.GitRepo.CommitsByFileAndRange( + git.CommitsByFileAndRangeOptions{ + Revision: ctx.Repo.RefName, + File: fileName, + Page: 1, + }) + if err != nil { + ctx.ServerError("ShowBranchFeed", err) + return + } + + title := fmt.Sprintf("Latest commits for file %s", ctx.Repo.TreePath) + + link := &feeds.Link{Href: repo.HTMLURL() + "/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)} + + feed := &feeds.Feed{ + Title: title, + Link: link, + Description: repo.Description, + Created: time.Now(), + } + + for _, commit := range commits { + feed.Items = append(feed.Items, &feeds.Item{ + Id: commit.ID.String(), + Title: strings.TrimSpace(strings.Split(commit.Message(), "\n")[0]), + Link: &feeds.Link{Href: repo.HTMLURL() + "/commit/" + commit.ID.String()}, + Author: &feeds.Author{ + Name: commit.Author.Name, + Email: commit.Author.Email, + }, + Description: commit.Message(), + Content: commit.Message(), + }) + } + + writeFeed(ctx, feed, formatType) +} diff --git a/routers/web/feed/profile.go b/routers/web/feed/profile.go new file mode 100644 index 0000000..08cbcd9 --- /dev/null +++ b/routers/web/feed/profile.go @@ -0,0 +1,87 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package feed + +import ( + "time" + + activities_model "code.gitea.io/gitea/models/activities" + "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/markup/markdown" + "code.gitea.io/gitea/services/context" + + "github.com/gorilla/feeds" +) + +// ShowUserFeedRSS show user activity as RSS feed +func ShowUserFeedRSS(ctx *context.Context) { + showUserFeed(ctx, "rss") +} + +// ShowUserFeedAtom show user activity as Atom feed +func ShowUserFeedAtom(ctx *context.Context) { + showUserFeed(ctx, "atom") +} + +// showUserFeed show user activity as RSS / Atom feed +func showUserFeed(ctx *context.Context, formatType string) { + includePrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID) + + actions, _, err := activities_model.GetFeeds(ctx, activities_model.GetFeedsOptions{ + RequestedUser: ctx.ContextUser, + Actor: ctx.Doer, + IncludePrivate: includePrivate, + OnlyPerformedBy: !ctx.ContextUser.IsOrganization(), + IncludeDeleted: false, + Date: ctx.FormString("date"), + }) + if err != nil { + ctx.ServerError("GetFeeds", err) + return + } + + ctxUserDescription, err := markdown.RenderString(&markup.RenderContext{ + Ctx: ctx, + Links: markup.Links{ + Base: ctx.ContextUser.HTMLURL(), + }, + Metas: map[string]string{ + "user": ctx.ContextUser.GetDisplayName(), + }, + }, ctx.ContextUser.Description) + if err != nil { + ctx.ServerError("RenderString", err) + return + } + + feed := &feeds.Feed{ + Title: ctx.Locale.TrString("home.feed_of", ctx.ContextUser.DisplayName()), + Link: &feeds.Link{Href: ctx.ContextUser.HTMLURL()}, + Description: string(ctxUserDescription), + Created: time.Now(), + } + + feed.Items, err = feedActionsToFeedItems(ctx, actions) + if err != nil { + ctx.ServerError("convert feed", err) + return + } + + writeFeed(ctx, feed, formatType) +} + +// writeFeed write a feeds.Feed as atom or rss to ctx.Resp +func writeFeed(ctx *context.Context, feed *feeds.Feed, formatType string) { + if formatType == "atom" { + ctx.Resp.Header().Set("Content-Type", "application/atom+xml;charset=utf-8") + if err := feed.WriteAtom(ctx.Resp); err != nil { + ctx.ServerError("Render Atom failed", err) + } + } else { + ctx.Resp.Header().Set("Content-Type", "application/rss+xml;charset=utf-8") + if err := feed.WriteRss(ctx.Resp); err != nil { + ctx.ServerError("Render RSS failed", err) + } + } +} diff --git a/routers/web/feed/release.go b/routers/web/feed/release.go new file mode 100644 index 0000000..fb6e3ad --- /dev/null +++ b/routers/web/feed/release.go @@ -0,0 +1,52 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package feed + +import ( + "time" + + "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/services/context" + + "github.com/gorilla/feeds" +) + +// shows tags and/or releases on the repo as RSS / Atom feed +func ShowReleaseFeed(ctx *context.Context, repo *repo_model.Repository, isReleasesOnly bool, formatType string) { + releases, err := db.Find[repo_model.Release](ctx, repo_model.FindReleasesOptions{ + IncludeTags: !isReleasesOnly, + RepoID: ctx.Repo.Repository.ID, + }) + if err != nil { + ctx.ServerError("GetReleasesByRepoID", err) + return + } + + var title string + var link *feeds.Link + + if isReleasesOnly { + title = ctx.Locale.TrString("repo.release.releases_for", repo.FullName()) + link = &feeds.Link{Href: repo.HTMLURL() + "/release"} + } else { + title = ctx.Locale.TrString("repo.release.tags_for", repo.FullName()) + link = &feeds.Link{Href: repo.HTMLURL() + "/tags"} + } + + feed := &feeds.Feed{ + Title: title, + Link: link, + Description: repo.Description, + Created: time.Now(), + } + + feed.Items, err = releasesToFeedItems(ctx, releases) + if err != nil { + ctx.ServerError("releasesToFeedItems", err) + return + } + + writeFeed(ctx, feed, formatType) +} diff --git a/routers/web/feed/render.go b/routers/web/feed/render.go new file mode 100644 index 0000000..dc99fb4 --- /dev/null +++ b/routers/web/feed/render.go @@ -0,0 +1,19 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package feed + +import ( + "code.gitea.io/gitea/services/context" +) + +// RenderBranchFeed render format for branch or file +func RenderBranchFeed(feedType string) func(ctx *context.Context) { + return func(ctx *context.Context) { + if ctx.Repo.TreePath == "" { + ShowBranchFeed(ctx, ctx.Repo.Repository, feedType) + } else { + ShowFileFeed(ctx, ctx.Repo.Repository, feedType) + } + } +} diff --git a/routers/web/feed/repo.go b/routers/web/feed/repo.go new file mode 100644 index 0000000..a0033c7 --- /dev/null +++ b/routers/web/feed/repo.go @@ -0,0 +1,44 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package feed + +import ( + "time" + + activities_model "code.gitea.io/gitea/models/activities" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/services/context" + + "github.com/gorilla/feeds" +) + +// ShowRepoFeed shows user activity on the repo as RSS / Atom feed +func ShowRepoFeed(ctx *context.Context, repo *repo_model.Repository, formatType string) { + actions, _, err := activities_model.GetFeeds(ctx, activities_model.GetFeedsOptions{ + OnlyPerformedByActor: true, + RequestedRepo: repo, + Actor: ctx.Doer, + IncludePrivate: true, + Date: ctx.FormString("date"), + }) + if err != nil { + ctx.ServerError("GetFeeds", err) + return + } + + feed := &feeds.Feed{ + Title: ctx.Locale.TrString("home.feed_of", repo.FullName()), + Link: &feeds.Link{Href: repo.HTMLURL()}, + Description: repo.Description, + Created: time.Now(), + } + + feed.Items, err = feedActionsToFeedItems(ctx, actions) + if err != nil { + ctx.ServerError("convert feed", err) + return + } + + writeFeed(ctx, feed, formatType) +} |