2015-12-21 15:24:11 +03:00
// Copyright 2015 The Gogs Authors. All rights reserved.
2019-02-18 19:00:27 +03:00
// Copyright 2019 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2015-12-21 15:24:11 +03:00
package user
import (
"fmt"
2021-04-05 18:30:52 +03:00
"net/http"
2015-12-21 15:24:11 +03:00
"strings"
2022-08-25 05:31:57 +03:00
activities_model "code.gitea.io/gitea/models/activities"
2021-09-24 14:32:56 +03:00
"code.gitea.io/gitea/models/db"
2022-03-29 09:29:02 +03:00
"code.gitea.io/gitea/models/organization"
2022-03-29 17:16:31 +03:00
project_model "code.gitea.io/gitea/models/project"
2021-12-10 04:27:50 +03:00
repo_model "code.gitea.io/gitea/models/repo"
2021-11-11 10:03:30 +03:00
user_model "code.gitea.io/gitea/models/user"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/context"
2023-05-09 08:57:24 +03:00
"code.gitea.io/gitea/modules/git"
2021-04-20 01:25:08 +03:00
"code.gitea.io/gitea/modules/markup"
2020-08-05 10:48:37 +03:00
"code.gitea.io/gitea/modules/markup/markdown"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/setting"
2017-10-27 00:16:13 +03:00
"code.gitea.io/gitea/modules/util"
2021-10-16 17:21:16 +03:00
"code.gitea.io/gitea/routers/web/feed"
2021-06-09 02:33:54 +03:00
"code.gitea.io/gitea/routers/web/org"
2015-12-21 15:24:11 +03:00
)
2016-11-18 06:03:03 +03:00
// Profile render user's profile page
2016-03-11 19:56:52 +03:00
func Profile ( ctx * context . Context ) {
2022-03-26 12:04:22 +03:00
if strings . Contains ( ctx . Req . Header . Get ( "Accept" ) , "application/rss+xml" ) {
feed . ShowUserFeedRSS ( ctx )
2015-12-21 15:24:11 +03:00
return
2021-04-28 15:35:06 +03:00
}
2022-03-26 12:04:22 +03:00
if strings . Contains ( ctx . Req . Header . Get ( "Accept" ) , "application/atom+xml" ) {
feed . ShowUserFeedAtom ( ctx )
2015-12-21 15:24:11 +03:00
return
}
2022-03-26 12:04:22 +03:00
if ctx . ContextUser . IsOrganization ( ) {
2021-06-26 22:53:14 +03:00
org . Home ( ctx )
return
}
// check view permissions
2022-05-20 17:08:52 +03:00
if ! user_model . IsUserVisibleToViewer ( ctx , ctx . ContextUser , ctx . Doer ) {
2022-03-26 12:04:22 +03:00
ctx . NotFound ( "user" , fmt . Errorf ( ctx . ContextUser . Name ) )
2021-10-16 17:21:16 +03:00
return
}
2022-03-13 19:40:47 +03:00
// advertise feed via meta tag
2023-02-11 09:34:11 +03:00
ctx . Data [ "FeedURL" ] = ctx . ContextUser . HomeLink ( )
2022-03-13 19:40:47 +03:00
2017-03-20 11:31:08 +03:00
// Show OpenID URIs
2022-03-26 12:04:22 +03:00
openIDs , err := user_model . GetUserOpenIDs ( ctx . ContextUser . ID )
2017-03-20 11:31:08 +03:00
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetUserOpenIDs" , err )
2017-03-20 11:31:08 +03:00
return
}
2021-11-22 18:21:55 +03:00
var isFollowing bool
2022-03-26 12:04:22 +03:00
if ctx . Doer != nil {
isFollowing = user_model . IsFollowing ( ctx . Doer . ID , ctx . ContextUser . ID )
2021-11-22 18:21:55 +03:00
}
2022-03-26 12:04:22 +03:00
ctx . Data [ "Title" ] = ctx . ContextUser . DisplayName ( )
2015-12-21 15:24:11 +03:00
ctx . Data [ "PageIsUserProfile" ] = true
2023-04-20 20:33:30 +03:00
ctx . Data [ "ContextUser" ] = ctx . ContextUser
2017-03-20 11:31:08 +03:00
ctx . Data [ "OpenIDs" ] = openIDs
2021-11-22 18:21:55 +03:00
ctx . Data [ "IsFollowing" ] = isFollowing
2020-11-19 01:00:16 +03:00
2021-03-05 01:59:13 +03:00
if setting . Service . EnableUserHeatmap {
2022-08-25 05:31:57 +03:00
data , err := activities_model . GetUserHeatmapDataByUser ( ctx . ContextUser , ctx . Doer )
2020-11-19 01:00:16 +03:00
if err != nil {
ctx . ServerError ( "GetUserHeatmapDataByUser" , err )
return
}
ctx . Data [ "HeatmapData" ] = data
2023-04-17 21:26:01 +03:00
ctx . Data [ "HeatmapTotalContributions" ] = activities_model . GetTotalContributionsInHeatmap ( data )
2020-11-19 01:00:16 +03:00
}
2022-03-26 12:04:22 +03:00
if len ( ctx . ContextUser . Description ) != 0 {
2021-04-20 01:25:08 +03:00
content , err := markdown . RenderString ( & markup . RenderContext {
URLPrefix : ctx . Repo . RepoLink ,
Metas : map [ string ] string { "mode" : "document" } ,
2021-06-21 01:39:12 +03:00
GitRepo : ctx . Repo . GitRepo ,
2021-08-28 23:15:56 +03:00
Ctx : ctx ,
2022-03-26 12:04:22 +03:00
} , ctx . ContextUser . Description )
2021-04-20 01:25:08 +03:00
if err != nil {
ctx . ServerError ( "RenderString" , err )
return
}
ctx . Data [ "RenderedDescription" ] = content
2020-08-05 10:48:37 +03:00
}
2023-05-09 08:57:24 +03:00
repo , err := repo_model . GetRepositoryByName ( ctx . ContextUser . ID , ".profile" )
if err == nil && ! repo . IsEmpty {
gitRepo , err := git . OpenRepository ( ctx , repo . RepoPath ( ) )
if err != nil {
ctx . ServerError ( "OpenRepository" , err )
return
}
defer gitRepo . Close ( )
commit , err := gitRepo . GetBranchCommit ( repo . DefaultBranch )
if err != nil {
ctx . ServerError ( "GetBranchCommit" , err )
return
}
blob , err := commit . GetBlobByPath ( "README.md" )
if err == nil {
2023-06-13 12:02:25 +03:00
bytes , err := blob . GetBlobContent ( setting . UI . MaxDisplayFileSize )
2023-05-09 08:57:24 +03:00
if err != nil {
ctx . ServerError ( "GetBlobContent" , err )
return
}
profileContent , err := markdown . RenderString ( & markup . RenderContext {
Ctx : ctx ,
GitRepo : gitRepo ,
} , bytes )
if err != nil {
ctx . ServerError ( "RenderString" , err )
return
}
ctx . Data [ "ProfileReadme" ] = profileContent
}
}
2022-03-26 12:04:22 +03:00
showPrivate := ctx . IsSigned && ( ctx . Doer . IsAdmin || ctx . Doer . ID == ctx . ContextUser . ID )
2016-01-31 00:51:11 +03:00
2022-03-29 09:29:02 +03:00
orgs , err := organization . FindOrgs ( organization . FindOrgOptions {
2022-03-26 12:04:22 +03:00
UserID : ctx . ContextUser . ID ,
2021-11-22 16:51:45 +03:00
IncludePrivate : showPrivate ,
} )
2016-01-12 05:09:59 +03:00
if err != nil {
2021-11-22 16:51:45 +03:00
ctx . ServerError ( "FindOrgs" , err )
2016-01-12 05:09:59 +03:00
return
}
2016-02-07 12:20:58 +03:00
2016-01-12 05:09:59 +03:00
ctx . Data [ "Orgs" ] = orgs
2022-03-29 09:29:02 +03:00
ctx . Data [ "HasOrgsVisible" ] = organization . HasOrgsVisible ( orgs , ctx . Doer )
2015-12-21 15:24:11 +03:00
2022-08-18 02:25:25 +03:00
badges , _ , err := user_model . GetUserBadges ( ctx , ctx . ContextUser )
if err != nil {
ctx . ServerError ( "GetUserBadges" , err )
return
}
ctx . Data [ "Badges" ] = badges
2021-08-11 03:31:13 +03:00
tab := ctx . FormString ( "tab" )
2015-12-21 15:24:11 +03:00
ctx . Data [ "TabName" ] = tab
2017-02-14 10:28:22 +03:00
2021-07-29 04:42:15 +03:00
page := ctx . FormInt ( "page" )
2017-02-14 10:28:22 +03:00
if page <= 0 {
page = 1
}
2023-02-25 00:15:10 +03:00
pagingNum := setting . UI . User . RepoPagingNum
if tab == "activity" {
pagingNum = setting . UI . FeedPagingNum
}
2021-07-29 04:42:15 +03:00
topicOnly := ctx . FormBool ( "topic" )
2018-09-13 05:33:48 +03:00
2017-02-14 10:28:22 +03:00
var (
2021-12-10 04:27:50 +03:00
repos [ ] * repo_model . Repository
2017-02-14 10:28:22 +03:00
count int64
2019-04-20 07:15:19 +03:00
total int
2021-11-24 12:49:20 +03:00
orderBy db . SearchOrderBy
2017-02-14 10:28:22 +03:00
)
2021-08-11 03:31:13 +03:00
ctx . Data [ "SortType" ] = ctx . FormString ( "sort" )
switch ctx . FormString ( "sort" ) {
2017-02-14 10:28:22 +03:00
case "newest" :
2021-11-24 12:49:20 +03:00
orderBy = db . SearchOrderByNewest
2017-02-14 10:28:22 +03:00
case "oldest" :
2021-11-24 12:49:20 +03:00
orderBy = db . SearchOrderByOldest
2017-02-14 10:28:22 +03:00
case "recentupdate" :
2021-11-24 12:49:20 +03:00
orderBy = db . SearchOrderByRecentUpdated
2017-02-14 10:28:22 +03:00
case "leastupdate" :
2021-11-24 12:49:20 +03:00
orderBy = db . SearchOrderByLeastUpdated
2017-02-14 10:28:22 +03:00
case "reversealphabetically" :
2021-11-24 12:49:20 +03:00
orderBy = db . SearchOrderByAlphabeticallyReverse
2017-02-14 10:28:22 +03:00
case "alphabetically" :
2021-11-24 12:49:20 +03:00
orderBy = db . SearchOrderByAlphabetically
2018-05-24 04:03:42 +03:00
case "moststars" :
2021-11-24 12:49:20 +03:00
orderBy = db . SearchOrderByStarsReverse
2018-05-24 04:03:42 +03:00
case "feweststars" :
2021-11-24 12:49:20 +03:00
orderBy = db . SearchOrderByStars
2018-05-24 04:03:42 +03:00
case "mostforks" :
2021-11-24 12:49:20 +03:00
orderBy = db . SearchOrderByForksReverse
2018-05-24 04:03:42 +03:00
case "fewestforks" :
2021-11-24 12:49:20 +03:00
orderBy = db . SearchOrderByForks
2017-02-14 10:28:22 +03:00
default :
ctx . Data [ "SortType" ] = "recentupdate"
2021-11-24 12:49:20 +03:00
orderBy = db . SearchOrderByRecentUpdated
2017-02-14 10:28:22 +03:00
}
2021-08-11 18:08:52 +03:00
keyword := ctx . FormTrim ( "q" )
2017-02-14 10:28:22 +03:00
ctx . Data [ "Keyword" ] = keyword
2022-01-28 14:29:04 +03:00
language := ctx . FormTrim ( "language" )
ctx . Data [ "Language" ] = language
2023-04-29 22:13:58 +03:00
followers , numFollowers , err := user_model . GetUserFollowers ( ctx , ctx . ContextUser , ctx . Doer , db . ListOptions {
PageSize : pagingNum ,
Page : page ,
} )
if err != nil {
ctx . ServerError ( "GetUserFollowers" , err )
return
}
ctx . Data [ "NumFollowers" ] = numFollowers
following , numFollowing , err := user_model . GetUserFollowing ( ctx , ctx . ContextUser , ctx . Doer , db . ListOptions {
PageSize : pagingNum ,
Page : page ,
} )
if err != nil {
ctx . ServerError ( "GetUserFollowing" , err )
return
}
ctx . Data [ "NumFollowing" ] = numFollowing
2015-12-21 15:24:11 +03:00
switch tab {
2020-02-09 23:18:01 +03:00
case "followers" :
2023-04-29 22:13:58 +03:00
ctx . Data [ "Cards" ] = followers
2022-07-05 18:47:45 +03:00
total = int ( count )
2020-02-09 23:18:01 +03:00
case "following" :
2023-04-29 22:13:58 +03:00
ctx . Data [ "Cards" ] = following
2022-07-05 18:47:45 +03:00
total = int ( count )
2015-12-21 15:24:11 +03:00
case "activity" :
2023-02-25 00:15:10 +03:00
date := ctx . FormString ( "date" )
items , count , err := activities_model . GetFeeds ( ctx , activities_model . GetFeedsOptions {
2022-03-26 12:04:22 +03:00
RequestedUser : ctx . ContextUser ,
2022-03-22 10:03:22 +03:00
Actor : ctx . Doer ,
2017-08-23 04:30:54 +03:00
IncludePrivate : showPrivate ,
OnlyPerformedBy : true ,
IncludeDeleted : false ,
2023-02-25 00:15:10 +03:00
Date : date ,
ListOptions : db . ListOptions {
PageSize : pagingNum ,
Page : page ,
} ,
2017-08-23 04:30:54 +03:00
} )
2022-03-13 19:40:47 +03:00
if err != nil {
ctx . ServerError ( "GetFeeds" , err )
2015-12-21 15:24:11 +03:00
return
}
2023-02-25 00:15:10 +03:00
ctx . Data [ "Feeds" ] = items
ctx . Data [ "Date" ] = date
total = int ( count )
2016-12-29 17:58:24 +03:00
case "stars" :
2017-02-14 10:28:22 +03:00
ctx . Data [ "PageIsProfileStarList" ] = true
2022-11-19 11:12:33 +03:00
repos , count , err = repo_model . SearchRepository ( ctx , & repo_model . SearchRepoOptions {
2021-09-24 14:32:56 +03:00
ListOptions : db . ListOptions {
2023-02-25 00:15:10 +03:00
PageSize : pagingNum ,
2020-01-24 22:00:29 +03:00
Page : page ,
} ,
2022-03-22 10:03:22 +03:00
Actor : ctx . Doer ,
2019-08-25 20:06:36 +03:00
Keyword : keyword ,
OrderBy : orderBy ,
Private : ctx . IsSigned ,
2022-03-26 12:04:22 +03:00
StarredByID : ctx . ContextUser . ID ,
2019-08-25 20:06:36 +03:00
Collaborate : util . OptionalBoolFalse ,
TopicOnly : topicOnly ,
2022-01-28 14:29:04 +03:00
Language : language ,
2019-08-25 20:06:36 +03:00
IncludeDescription : setting . UI . SearchRepoDescription ,
2019-05-15 18:24:39 +03:00
} )
if err != nil {
2019-08-25 20:06:36 +03:00
ctx . ServerError ( "SearchRepository" , err )
2019-05-15 18:24:39 +03:00
return
2017-02-07 14:54:16 +03:00
}
2019-04-20 07:15:19 +03:00
total = int ( count )
2020-08-17 06:07:38 +03:00
case "projects" :
2023-01-20 14:42:33 +03:00
ctx . Data [ "OpenProjects" ] , _ , err = project_model . FindProjects ( ctx , project_model . SearchOptions {
2020-08-17 06:07:38 +03:00
Page : - 1 ,
IsClosed : util . OptionalBoolFalse ,
2022-03-29 17:16:31 +03:00
Type : project_model . TypeIndividual ,
2020-08-17 06:07:38 +03:00
} )
if err != nil {
ctx . ServerError ( "GetProjects" , err )
return
}
2021-04-15 19:53:57 +03:00
case "watching" :
2022-11-19 11:12:33 +03:00
repos , count , err = repo_model . SearchRepository ( ctx , & repo_model . SearchRepoOptions {
2021-09-24 14:32:56 +03:00
ListOptions : db . ListOptions {
2023-02-25 00:15:10 +03:00
PageSize : pagingNum ,
2021-04-15 19:53:57 +03:00
Page : page ,
} ,
2022-03-22 10:03:22 +03:00
Actor : ctx . Doer ,
2021-04-15 19:53:57 +03:00
Keyword : keyword ,
OrderBy : orderBy ,
Private : ctx . IsSigned ,
2022-03-26 12:04:22 +03:00
WatchedByID : ctx . ContextUser . ID ,
2021-04-15 19:53:57 +03:00
Collaborate : util . OptionalBoolFalse ,
TopicOnly : topicOnly ,
2022-01-28 14:29:04 +03:00
Language : language ,
2021-04-15 19:53:57 +03:00
IncludeDescription : setting . UI . SearchRepoDescription ,
} )
if err != nil {
ctx . ServerError ( "SearchRepository" , err )
return
}
total = int ( count )
2015-12-21 15:24:11 +03:00
default :
2022-11-19 11:12:33 +03:00
repos , count , err = repo_model . SearchRepository ( ctx , & repo_model . SearchRepoOptions {
2021-09-24 14:32:56 +03:00
ListOptions : db . ListOptions {
2023-02-25 00:15:10 +03:00
PageSize : pagingNum ,
2020-01-24 22:00:29 +03:00
Page : page ,
} ,
2022-03-22 10:03:22 +03:00
Actor : ctx . Doer ,
2019-08-25 20:06:36 +03:00
Keyword : keyword ,
2022-03-26 12:04:22 +03:00
OwnerID : ctx . ContextUser . ID ,
2019-08-25 20:06:36 +03:00
OrderBy : orderBy ,
Private : ctx . IsSigned ,
Collaborate : util . OptionalBoolFalse ,
TopicOnly : topicOnly ,
2022-01-28 14:29:04 +03:00
Language : language ,
2019-08-25 20:06:36 +03:00
IncludeDescription : setting . UI . SearchRepoDescription ,
2019-05-15 18:24:39 +03:00
} )
if err != nil {
2019-08-25 20:06:36 +03:00
ctx . ServerError ( "SearchRepository" , err )
2019-05-15 18:24:39 +03:00
return
2015-12-21 15:24:11 +03:00
}
2019-05-15 18:24:39 +03:00
total = int ( count )
2015-12-21 15:24:11 +03:00
}
2019-04-20 07:15:19 +03:00
ctx . Data [ "Repos" ] = repos
ctx . Data [ "Total" ] = total
2023-02-25 00:15:10 +03:00
pager := context . NewPagination ( total , pagingNum , page , 5 )
2019-04-20 07:15:19 +03:00
pager . SetDefaultParams ( ctx )
2022-06-15 18:05:32 +03:00
pager . AddParam ( ctx , "tab" , "TabName" )
2022-01-28 14:29:04 +03:00
if tab != "followers" && tab != "following" && tab != "activity" && tab != "projects" {
pager . AddParam ( ctx , "language" , "Language" )
}
2023-02-25 00:15:10 +03:00
if tab == "activity" {
pager . AddParam ( ctx , "date" , "Date" )
}
2019-04-20 07:15:19 +03:00
ctx . Data [ "Page" ] = pager
2023-03-10 18:18:20 +03:00
ctx . Data [ "IsProjectEnabled" ] = true
2022-03-31 20:31:53 +03:00
ctx . Data [ "IsPackageEnabled" ] = setting . Packages . Enabled
2022-10-11 02:12:03 +03:00
ctx . Data [ "IsRepoIndexerEnabled" ] = setting . Indexer . RepoIndexerEnabled
2015-12-21 15:24:11 +03:00
2023-03-28 00:27:32 +03:00
ctx . Data [ "ShowUserEmail" ] = setting . UI . ShowUserEmail && ctx . ContextUser . Email != "" && ctx . IsSigned && ! ctx . ContextUser . KeepEmailPrivate
2017-08-17 12:08:03 +03:00
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplProfile )
2015-12-21 15:24:11 +03:00
}
2016-11-18 06:03:03 +03:00
// Action response for follow/unfollow user request
2016-03-11 19:56:52 +03:00
func Action ( ctx * context . Context ) {
2015-12-21 15:24:11 +03:00
var err error
2021-12-20 20:18:26 +03:00
switch ctx . FormString ( "action" ) {
2015-12-21 15:24:11 +03:00
case "follow" :
2022-03-26 12:04:22 +03:00
err = user_model . FollowUser ( ctx . Doer . ID , ctx . ContextUser . ID )
2015-12-21 15:24:11 +03:00
case "unfollow" :
2022-03-26 12:04:22 +03:00
err = user_model . UnfollowUser ( ctx . Doer . ID , ctx . ContextUser . ID )
2015-12-21 15:24:11 +03:00
}
if err != nil {
2021-12-20 20:18:26 +03:00
ctx . ServerError ( fmt . Sprintf ( "Action (%s)" , ctx . FormString ( "action" ) ) , err )
2015-12-21 15:24:11 +03:00
return
}
2021-11-16 21:18:25 +03:00
// FIXME: We should check this URL and make sure that it's a valid Gitea URL
2022-03-26 12:04:22 +03:00
ctx . RedirectToFirst ( ctx . FormString ( "redirect_to" ) , ctx . ContextUser . HomeLink ( ) )
2015-12-21 15:24:11 +03:00
}