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>
pull/24592/head^2
Nicholas Pease 2 years ago committed by GitHub
parent b6fc2cdf82
commit c090f87a8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 20
      docs/content/doc/usage/profile-readme.en-us.md
  2. 1
      options/locale/locale_en-US.ini
  3. 22
      routers/web/shared/user/header.go
  4. 33
      routers/web/user/profile.go
  5. 11
      templates/user/overview/header.tmpl
  6. 39
      templates/user/profile.tmpl
  7. 7
      web_src/css/user.css

@ -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
code = Code
projects = Projects
overview = Overview
following = Following
follow = Follow
unfollow = Unfollow

@ -4,7 +4,9 @@
package user
import (
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
)
@ -13,4 +15,24 @@ func RenderUserHeader(ctx *context.Context) {
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
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"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
@ -91,6 +92,38 @@ func Profile(ctx *context.Context) {
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)
orgs, err := organization.FindOrgs(organization.FindOrgOptions{

@ -1,5 +1,6 @@
<!-- TODO: make template org and user can share -->
{{with .ContextUser}}
{{if or (.IsPackagesPage) (.PageIsViewProjects)}}
{{with .ContextUser}}
<div class="ui container">
<div class="ui vertically grid head">
<div class="column">
@ -14,11 +15,17 @@
</div>
</div>
</div>
{{end}}
{{end}}
<div class="ui tabs container">
<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"}}
{{if .ContextUser.NumRepos}}
<div class="ui small label">{{.ContextUser.NumRepos}}</div>

@ -121,40 +121,7 @@
</div>
<div class="ui eleven wide column">
<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}}">
{{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}}
{{template "user/overview/header" .}}
</div>
{{if eq .TabName "activity"}}
@ -177,10 +144,12 @@
{{template "repo/user_cards" .}}
{{else if eq .TabName "followers"}}
{{template "repo/user_cards" .}}
{{else}}
{{else if or (eq .TabName "repositories") (not .ProfileReadme)}}
{{template "explore/repo_search" .}}
{{template "explore/repo_list" .}}
{{template "base/paginate" .}}
{{else if .ProfileReadme}}
<div id="readme_profile" class="render-content markup"> {{$.ProfileReadme|Str2html}} </div>
{{end}}
</div>
</div>

@ -140,6 +140,13 @@
border: none;
}
#readme_profile {
padding: 10px;
border-radius: 0.28571429rem;
background: var(--color-card);
border: 1px solid var(--color-secondary);
}
#notification_table tr {
cursor: default;
}

Loading…
Cancel
Save