mirror of
https://github.com/hanzoai/forge.git
synced 2026-10-30 15:00:39 +00:00
Source files keep their SPDX-License-Identifier and Hanzo copyright line; the Gitea, Gogs and LFS Test Server copyrights they used to repeat now live once in NOTICE, alongside LICENSE. The header lint accepts any copyright line above the SPDX line, so it no longer demands an upstream one. Co-authored-by: Hanzo Dev <[email protected]>
100 lines
3.0 KiB
Go
100 lines
3.0 KiB
Go
// Copyright 2026 Hanzo AI, Inc. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package feed
|
|
|
|
import (
|
|
"time"
|
|
|
|
activities_model "github.com/hanzoai/git/models/activities"
|
|
"github.com/hanzoai/git/models/organization"
|
|
"github.com/hanzoai/git/models/renderhelper"
|
|
"github.com/hanzoai/git/modules/markup/markdown"
|
|
"github.com/hanzoai/git/services/context"
|
|
feed_service "github.com/hanzoai/git/services/feed"
|
|
|
|
"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)
|
|
isOrganisation := ctx.ContextUser.IsOrganization()
|
|
if ctx.IsSigned && isOrganisation && !includePrivate {
|
|
// When feed is requested by a member of the organization,
|
|
// include the private repo's the member has access to.
|
|
isOrgMember, err := organization.IsOrganizationMember(ctx, ctx.ContextUser.ID, ctx.Doer.ID)
|
|
if err != nil {
|
|
ctx.ServerError("IsOrganizationMember", err)
|
|
return
|
|
}
|
|
includePrivate = isOrgMember
|
|
}
|
|
|
|
// a public-only API token must not surface private activity, even for its own owner
|
|
if includePrivate && context.TokenIsPublicOnly(ctx) {
|
|
includePrivate = false
|
|
}
|
|
|
|
actions, _, err := feed_service.GetFeeds(ctx, activities_model.GetFeedsOptions{
|
|
RequestedUser: ctx.ContextUser,
|
|
Actor: ctx.Doer,
|
|
IncludePrivate: includePrivate,
|
|
OnlyPerformedBy: !isOrganisation,
|
|
IncludeDeleted: false,
|
|
Date: ctx.FormString("date"),
|
|
})
|
|
if err != nil {
|
|
ctx.ServerError("GetFeeds", err)
|
|
return
|
|
}
|
|
|
|
rctx := renderhelper.NewRenderContextSimpleDocument(ctx, ctx.ContextUser.HTMLURL(ctx))
|
|
ctxUserDescription, err := markdown.RenderString(rctx,
|
|
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(ctx)},
|
|
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)
|
|
}
|
|
}
|
|
}
|