mirror of
https://github.com/go-gitea/gitea
synced 2025-07-22 18:28:37 +00:00
RSS/Atom support for Repos (#19055)
* support for repos * refactor * advertise the feeds via meta tags * allow feed suffix and feed header * optimize performance
This commit is contained in:
@@ -7,6 +7,7 @@ package feed
|
||||
import (
|
||||
"fmt"
|
||||
"html"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -66,7 +67,7 @@ func renderMarkdown(ctx *context.Context, act *models.Action, content string) st
|
||||
}
|
||||
|
||||
// feedActionsToFeedItems convert gitea's Action feed to feeds Item
|
||||
func feedActionsToFeedItems(ctx *context.Context, actions []*models.Action) (items []*feeds.Item, err error) {
|
||||
func feedActionsToFeedItems(ctx *context.Context, actions models.ActionList) (items []*feeds.Item, err error) {
|
||||
for _, act := range actions {
|
||||
act.LoadActUser()
|
||||
|
||||
@@ -247,3 +248,18 @@ func feedActionsToFeedItems(ctx *context.Context, actions []*models.Action) (ite
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 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, ""
|
||||
}
|
||||
|
@@ -15,48 +15,9 @@ import (
|
||||
"github.com/gorilla/feeds"
|
||||
)
|
||||
|
||||
// RetrieveFeeds loads feeds for the specified user
|
||||
func RetrieveFeeds(ctx *context.Context, options models.GetFeedsOptions) []*models.Action {
|
||||
actions, err := models.GetFeeds(options)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetFeeds", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: move load repoOwner of act.Repo into models.GetFeeds->loadAttributes()
|
||||
{
|
||||
userCache := map[int64]*user_model.User{options.RequestedUser.ID: options.RequestedUser}
|
||||
if ctx.User != nil {
|
||||
userCache[ctx.User.ID] = ctx.User
|
||||
}
|
||||
for _, act := range actions {
|
||||
if act.ActUser != nil {
|
||||
userCache[act.ActUserID] = act.ActUser
|
||||
}
|
||||
}
|
||||
for _, act := range actions {
|
||||
repoOwner, ok := userCache[act.Repo.OwnerID]
|
||||
if !ok {
|
||||
repoOwner, err = user_model.GetUserByID(act.Repo.OwnerID)
|
||||
if err != nil {
|
||||
if user_model.IsErrUserNotExist(err) {
|
||||
continue
|
||||
}
|
||||
ctx.ServerError("GetUserByID", err)
|
||||
return nil
|
||||
}
|
||||
userCache[repoOwner.ID] = repoOwner
|
||||
}
|
||||
act.Repo.Owner = repoOwner
|
||||
}
|
||||
}
|
||||
|
||||
return actions
|
||||
}
|
||||
|
||||
// ShowUserFeed show user activity as RSS / Atom feed
|
||||
func ShowUserFeed(ctx *context.Context, ctxUser *user_model.User, formatType string) {
|
||||
actions := RetrieveFeeds(ctx, models.GetFeedsOptions{
|
||||
actions, err := models.GetFeeds(ctx, models.GetFeedsOptions{
|
||||
RequestedUser: ctxUser,
|
||||
Actor: ctx.User,
|
||||
IncludePrivate: false,
|
||||
@@ -64,7 +25,8 @@ func ShowUserFeed(ctx *context.Context, ctxUser *user_model.User, formatType str
|
||||
IncludeDeleted: false,
|
||||
Date: ctx.FormString("date"),
|
||||
})
|
||||
if ctx.Written() {
|
||||
if err != nil {
|
||||
ctx.ServerError("GetFeeds", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -75,7 +37,6 @@ func ShowUserFeed(ctx *context.Context, ctxUser *user_model.User, formatType str
|
||||
Created: time.Now(),
|
||||
}
|
||||
|
||||
var err error
|
||||
feed.Items, err = feedActionsToFeedItems(ctx, actions)
|
||||
if err != nil {
|
||||
ctx.ServerError("convert feed", err)
|
||||
|
44
routers/web/feed/repo.go
Normal file
44
routers/web/feed/repo.go
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package feed
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/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 := models.GetFeeds(ctx, models.GetFeedsOptions{
|
||||
RequestedRepo: repo,
|
||||
Actor: ctx.User,
|
||||
IncludePrivate: true,
|
||||
Date: ctx.FormString("date"),
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("GetFeeds", err)
|
||||
return
|
||||
}
|
||||
|
||||
feed := &feeds.Feed{
|
||||
Title: ctx.Tr("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)
|
||||
}
|
@@ -38,6 +38,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/typesniffer"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/routers/web/feed"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -691,6 +692,14 @@ func checkHomeCodeViewable(ctx *context.Context) {
|
||||
|
||||
// Home render repository home page
|
||||
func Home(ctx *context.Context) {
|
||||
isFeed, _, showFeedType := feed.GetFeedType(ctx.Params(":reponame"), ctx.Req)
|
||||
if isFeed {
|
||||
feed.ShowRepoFeed(ctx, ctx.Repo.Repository, showFeedType)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["FeedURL"] = ctx.Repo.Repository.HTMLURL()
|
||||
|
||||
checkHomeCodeViewable(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
|
@@ -29,7 +29,6 @@ import (
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/routers/web/feed"
|
||||
issue_service "code.gitea.io/gitea/services/issue"
|
||||
pull_service "code.gitea.io/gitea/services/pull"
|
||||
|
||||
@@ -131,7 +130,7 @@ func Dashboard(ctx *context.Context) {
|
||||
ctx.Data["MirrorCount"] = len(mirrors)
|
||||
ctx.Data["Mirrors"] = mirrors
|
||||
|
||||
ctx.Data["Feeds"] = feed.RetrieveFeeds(ctx, models.GetFeedsOptions{
|
||||
ctx.Data["Feeds"], err = models.GetFeeds(ctx, models.GetFeedsOptions{
|
||||
RequestedUser: ctxUser,
|
||||
RequestedTeam: ctx.Org.Team,
|
||||
Actor: ctx.User,
|
||||
@@ -140,8 +139,8 @@ func Dashboard(ctx *context.Context) {
|
||||
IncludeDeleted: false,
|
||||
Date: ctx.FormString("date"),
|
||||
})
|
||||
|
||||
if ctx.Written() {
|
||||
if err != nil {
|
||||
ctx.ServerError("GetFeeds", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@@ -74,19 +74,7 @@ func Profile(ctx *context.Context) {
|
||||
uname = strings.TrimSuffix(uname, ".gpg")
|
||||
}
|
||||
|
||||
showFeedType := ""
|
||||
if strings.HasSuffix(uname, ".rss") {
|
||||
showFeedType = "rss"
|
||||
uname = strings.TrimSuffix(uname, ".rss")
|
||||
} else if strings.Contains(ctx.Req.Header.Get("Accept"), "application/rss+xml") {
|
||||
showFeedType = "rss"
|
||||
}
|
||||
if strings.HasSuffix(uname, ".atom") {
|
||||
showFeedType = "atom"
|
||||
uname = strings.TrimSuffix(uname, ".atom")
|
||||
} else if strings.Contains(ctx.Req.Header.Get("Accept"), "application/atom+xml") {
|
||||
showFeedType = "atom"
|
||||
}
|
||||
isShowFeed, uname, showFeedType := feed.GetFeedType(uname, ctx.Req)
|
||||
|
||||
ctxUser := GetUserByName(ctx, uname)
|
||||
if ctx.Written() {
|
||||
@@ -95,7 +83,7 @@ func Profile(ctx *context.Context) {
|
||||
|
||||
if ctxUser.IsOrganization() {
|
||||
// Show Org RSS feed
|
||||
if len(showFeedType) != 0 {
|
||||
if isShowFeed {
|
||||
feed.ShowUserFeed(ctx, ctxUser, showFeedType)
|
||||
return
|
||||
}
|
||||
@@ -123,11 +111,14 @@ func Profile(ctx *context.Context) {
|
||||
}
|
||||
|
||||
// Show User RSS feed
|
||||
if len(showFeedType) != 0 {
|
||||
if isShowFeed {
|
||||
feed.ShowUserFeed(ctx, ctxUser, showFeedType)
|
||||
return
|
||||
}
|
||||
|
||||
// advertise feed via meta tag
|
||||
ctx.Data["FeedURL"] = ctxUser.HTMLURL()
|
||||
|
||||
// Show OpenID URIs
|
||||
openIDs, err := user_model.GetUserOpenIDs(ctxUser.ID)
|
||||
if err != nil {
|
||||
@@ -259,7 +250,7 @@ func Profile(ctx *context.Context) {
|
||||
|
||||
total = ctxUser.NumFollowing
|
||||
case "activity":
|
||||
ctx.Data["Feeds"] = feed.RetrieveFeeds(ctx, models.GetFeedsOptions{
|
||||
ctx.Data["Feeds"], err = models.GetFeeds(ctx, models.GetFeedsOptions{
|
||||
RequestedUser: ctxUser,
|
||||
Actor: ctx.User,
|
||||
IncludePrivate: showPrivate,
|
||||
@@ -267,7 +258,8 @@ func Profile(ctx *context.Context) {
|
||||
IncludeDeleted: false,
|
||||
Date: ctx.FormString("date"),
|
||||
})
|
||||
if ctx.Written() {
|
||||
if err != nil {
|
||||
ctx.ServerError("GetFeeds", err)
|
||||
return
|
||||
}
|
||||
case "stars":
|
||||
|
Reference in New Issue
Block a user