Add Gitea Profile Readmes (#23260)
Implements displaying a README.md file present in a users ```.profile``` repository on the users profile page. If no such repository/file is present, the user's profile page remains unchanged. Example of user with ```.profile/README.md``` ![image](https://user-images.githubusercontent.com/34464552/222757202-5d53ac62-60d9-432f-b9e3-2537ffa91041.png) Example of user without ```.profile/README.md``` ![image](https://user-images.githubusercontent.com/34464552/222759972-576e058b-acd4-47ac-be33-38a7cb58cc81.png) This pull request closes the feature request in #12233 Special thanks to @techknowlogick for the help in the Gitea discord! --------- Co-authored-by: techknowlogick <techknowlogick@gitea.io> Co-authored-by: Yarden Shoham <hrsi88@gmail.com> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: yp05327 <576951401@qq.com> Co-authored-by: Yarden Shoham <git@yardenshoham.com>
This commit is contained in:
parent
b6fc2cdf82
commit
c090f87a8d
7 changed files with 97 additions and 38 deletions
20
docs/content/doc/usage/profile-readme.en-us.md
Normal file
20
docs/content/doc/usage/profile-readme.en-us.md
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
---
|
||||||
|
date: "2023-03-02T21:00:00+05:00"
|
||||||
|
title: "Usage: Gitea Profile READMEs"
|
||||||
|
slug: "profile-readme"
|
||||||
|
weight: 12
|
||||||
|
toc: false
|
||||||
|
draft: false
|
||||||
|
menu:
|
||||||
|
sidebar:
|
||||||
|
parent: "usage"
|
||||||
|
name: "Gitea Profile READMEs"
|
||||||
|
weight: 12
|
||||||
|
identifier: "profile-readme"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Gitea Profile READMEs
|
||||||
|
|
||||||
|
To display a markdown file in your Gitea profile page, simply make a repository named ".profile" and edit the README.md file inside. Gitea will automatically pull this file in and display it above your repositories.
|
||||||
|
|
||||||
|
Note. You are welcome to make this repository private. Doing so will hide your source files from public viewing and allow you to privitize certain files. However, the README.md file will be the only file present on your profile. If you wish to have an entirely private .profile repository, remove or rename the README.md file.
|
|
@ -569,6 +569,7 @@ starred = Starred Repositories
|
||||||
watched = Watched Repositories
|
watched = Watched Repositories
|
||||||
code = Code
|
code = Code
|
||||||
projects = Projects
|
projects = Projects
|
||||||
|
overview = Overview
|
||||||
following = Following
|
following = Following
|
||||||
follow = Follow
|
follow = Follow
|
||||||
unfollow = Unfollow
|
unfollow = Unfollow
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
package user
|
package user
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -13,4 +15,24 @@ func RenderUserHeader(ctx *context.Context) {
|
||||||
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
|
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
|
||||||
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
|
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
|
||||||
ctx.Data["ContextUser"] = ctx.ContextUser
|
ctx.Data["ContextUser"] = ctx.ContextUser
|
||||||
|
tab := ctx.FormString("tab")
|
||||||
|
ctx.Data["TabName"] = tab
|
||||||
|
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 && blob != nil {
|
||||||
|
ctx.Data["ProfileReadme"] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/markup"
|
"code.gitea.io/gitea/modules/markup"
|
||||||
"code.gitea.io/gitea/modules/markup/markdown"
|
"code.gitea.io/gitea/modules/markup/markdown"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
@ -91,6 +92,38 @@ func Profile(ctx *context.Context) {
|
||||||
ctx.Data["RenderedDescription"] = content
|
ctx.Data["RenderedDescription"] = content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
bytes, err := blob.GetBlobContent()
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
|
showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
|
||||||
|
|
||||||
orgs, err := organization.FindOrgs(organization.FindOrgOptions{
|
orgs, err := organization.FindOrgs(organization.FindOrgOptions{
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<!-- TODO: make template org and user can share -->
|
<!-- TODO: make template org and user can share -->
|
||||||
|
{{if or (.IsPackagesPage) (.PageIsViewProjects)}}
|
||||||
{{with .ContextUser}}
|
{{with .ContextUser}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui vertically grid head">
|
<div class="ui vertically grid head">
|
||||||
|
@ -15,10 +16,16 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
<div class="ui tabs container">
|
<div class="ui tabs container">
|
||||||
<div class="ui secondary stackable pointing menu">
|
<div class="ui secondary stackable pointing menu">
|
||||||
<a class="item" href="{{.ContextUser.HomeLink}}">
|
{{if .ProfileReadme}}
|
||||||
|
<a class='{{if or (eq .TabName "overview") (and (eq .TabName "") (not .IsPackagesPage) (not .PageIsViewProjects))}}active {{end}}item' href="{{.ContextUser.HomeLink}}?tab=overview">
|
||||||
|
{{svg "octicon-info"}} {{.locale.Tr "user.overview"}}
|
||||||
|
</a>
|
||||||
|
{{end}}
|
||||||
|
<a class="{{if or (eq .TabName "repositories") (and (eq .TabName "") (not .IsPackagesPage) (not .PageIsViewProjects) (not .ProfileReadme))}}active {{end}} item" href="{{.ContextUser.HomeLink}}?tab=repositories">
|
||||||
{{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}}
|
{{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}}
|
||||||
{{if .ContextUser.NumRepos}}
|
{{if .ContextUser.NumRepos}}
|
||||||
<div class="ui small label">{{.ContextUser.NumRepos}}</div>
|
<div class="ui small label">{{.ContextUser.NumRepos}}</div>
|
||||||
|
|
|
@ -121,40 +121,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="ui eleven wide column">
|
<div class="ui eleven wide column">
|
||||||
<div class="ui secondary stackable pointing tight menu">
|
<div class="ui secondary stackable pointing tight menu">
|
||||||
<a class='{{if and (ne .TabName "activity") (ne .TabName "following") (ne .TabName "followers") (ne .TabName "stars") (ne .TabName "watching") (ne .TabName "projects") (ne .TabName "code")}}active {{end}}item' href="{{.ContextUser.HomeLink}}">
|
{{template "user/overview/header" .}}
|
||||||
{{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}}
|
|
||||||
{{if .ContextUser.NumRepos}}
|
|
||||||
<div class="ui small label">{{.ContextUser.NumRepos}}</div>
|
|
||||||
{{end}}
|
|
||||||
</a>
|
|
||||||
<a href="{{.ContextUser.HomeLink}}/-/projects" class="{{if eq .TabName "projects"}}active {{end}}item">
|
|
||||||
{{svg "octicon-project-symlink"}} {{.locale.Tr "user.projects"}}
|
|
||||||
</a>
|
|
||||||
{{if .IsPackageEnabled}}
|
|
||||||
<a class='{{if eq .TabName "packages"}}active {{end}}item' href="{{.ContextUser.HomeLink}}/-/packages">
|
|
||||||
{{svg "octicon-package"}} {{.locale.Tr "packages.title"}}
|
|
||||||
</a>
|
|
||||||
{{end}}
|
|
||||||
{{if and (not $.UnitTypeCode.UnitGlobalDisabled) .IsRepoIndexerEnabled}}
|
|
||||||
<a class='{{if eq .TabName "code"}}active {{end}}item' href="{{.ContextUser.HomeLink}}/-/code">
|
|
||||||
{{svg "octicon-code"}} {{.locale.Tr "user.code"}}
|
|
||||||
</a>
|
|
||||||
{{end}}
|
|
||||||
<a class='{{if eq .TabName "activity"}}active {{end}}item' href="{{.ContextUser.HomeLink}}?tab=activity">
|
|
||||||
{{svg "octicon-rss"}} {{.locale.Tr "user.activity"}}
|
|
||||||
</a>
|
|
||||||
{{if not .DisableStars}}
|
|
||||||
<a class='{{if eq .TabName "stars"}}active {{end}}item' href="{{.ContextUser.HomeLink}}?tab=stars">
|
|
||||||
{{svg "octicon-star"}} {{.locale.Tr "user.starred"}}
|
|
||||||
{{if .ContextUser.NumStars}}
|
|
||||||
<div class="ui small label">{{.ContextUser.NumStars}}</div>
|
|
||||||
{{end}}
|
|
||||||
</a>
|
|
||||||
{{else}}
|
|
||||||
<a class='{{if eq .TabName "watching"}}active {{end}}item' href="{{.ContextUser.HomeLink}}?tab=watching">
|
|
||||||
{{svg "octicon-eye"}} {{.locale.Tr "user.watched"}}
|
|
||||||
</a>
|
|
||||||
{{end}}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{if eq .TabName "activity"}}
|
{{if eq .TabName "activity"}}
|
||||||
|
@ -177,10 +144,12 @@
|
||||||
{{template "repo/user_cards" .}}
|
{{template "repo/user_cards" .}}
|
||||||
{{else if eq .TabName "followers"}}
|
{{else if eq .TabName "followers"}}
|
||||||
{{template "repo/user_cards" .}}
|
{{template "repo/user_cards" .}}
|
||||||
{{else}}
|
{{else if or (eq .TabName "repositories") (not .ProfileReadme)}}
|
||||||
{{template "explore/repo_search" .}}
|
{{template "explore/repo_search" .}}
|
||||||
{{template "explore/repo_list" .}}
|
{{template "explore/repo_list" .}}
|
||||||
{{template "base/paginate" .}}
|
{{template "base/paginate" .}}
|
||||||
|
{{else if .ProfileReadme}}
|
||||||
|
<div id="readme_profile" class="render-content markup"> {{$.ProfileReadme|Str2html}} </div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -140,6 +140,13 @@
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#readme_profile {
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 0.28571429rem;
|
||||||
|
background: var(--color-card);
|
||||||
|
border: 1px solid var(--color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
#notification_table tr {
|
#notification_table tr {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue