From 2af30f715e64dbb0a3900168e3768ffb36c06392 Mon Sep 17 00:00:00 2001
From: puni9869 <80308335+puni9869@users.noreply.github.com>
Date: Fri, 7 Jul 2023 00:29:24 +0530
Subject: [PATCH] Fix inconsistent user profile layout across tabs (#25625)

Fix ::User Profile Page  Project Tab Have Inconsistent Layout and Style
Added the big_avator for consistency in the all header_items tabs.
Fixes: #24871
> ### Description
> in the user profile page the `Packages` and `Projects` tab have small
icons for user but other tabs have bigger profile picture with user
info:
>
> ### Screenshots
> ### **For Packages And Projects:**
>
![image](https://user-images.githubusercontent.com/25511175/240148601-2420d77b-ba25-4718-9ccb-c5d0d95e3079.png)
>
> ### **For Other Tabs:**
>
![image](https://user-images.githubusercontent.com/25511175/240148461-ce9636b3-fe11-4c46-a230-30d83eee5947.png)
>

## Before

![image](https://github.com/go-gitea/gitea/assets/80308335/975ad038-07ca-4b10-b75d-ccf259be7b9d)


 ## After changes
Project View
<img width="1394" alt="image"
src="https://github.com/go-gitea/gitea/assets/80308335/95d181d7-8e61-496d-9899-7b825c91ad56">


Packages View
<img width="1378" alt="image"
src="https://github.com/go-gitea/gitea/assets/80308335/7f5fd60f-6b18-4fa8-8c56-7b0d45d1a610">


## Org view for projects page
<img width="1385" alt="image"
src="https://github.com/go-gitea/gitea/assets/80308335/6400dc89-a5ae-4f0a-831b-5b6efa020d89">

## Org view for packages page
<img width="1387" alt="image"
src="https://github.com/go-gitea/gitea/assets/80308335/4e1e9ffe-1e4b-4334-8657-de11b5fd31d0">

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Giteabot <teabot@gitea.io>
Co-authored-by: silverwind <me@silverwind.io>
---
 models/user/user.go                           |   2 +-
 modules/context/org.go                        |   1 -
 routers/web/org/projects.go                   |   1 +
 routers/web/shared/user/header.go             | 106 +++++++++++--
 routers/web/user/code.go                      |   4 +-
 routers/web/user/home.go                      |   2 +-
 routers/web/user/package.go                   |   2 +
 routers/web/user/profile.go                   | 148 +++++-------------
 templates/code/searchcombo.tmpl               |  17 ++
 templates/explore/code.tmpl                   |  19 +--
 templates/org/menu.tmpl                       |   4 +-
 templates/org/projects/list.tmpl              |  29 +++-
 templates/org/projects/new.tmpl               |   3 +
 templates/org/projects/view.tmpl              |   3 +
 templates/package/settings.tmpl               |   3 +-
 templates/package/shared/list.tmpl            |   4 +-
 templates/package/shared/versionlist.tmpl     |   2 -
 templates/package/view.tmpl                   |   3 +-
 templates/projects/list.tmpl                  |  10 +-
 templates/projects/new.tmpl                   |   4 -
 templates/repo/packages.tmpl                  |   2 +
 templates/repo/projects/new.tmpl              |   2 +
 templates/shared/user/org_profile_avatar.tmpl |  16 ++
 templates/shared/user/profile_big_avatar.tmpl | 116 ++++++++++++++
 templates/user/code.tmpl                      |  42 ++---
 templates/user/overview/header.tmpl           | 139 +++++++---------
 templates/user/overview/package_versions.tmpl |  27 +++-
 templates/user/overview/packages.tmpl         |  29 +++-
 templates/user/profile.tmpl                   | 125 +--------------
 web_src/css/helpers.css                       |   1 +
 30 files changed, 464 insertions(+), 402 deletions(-)
 create mode 100644 templates/code/searchcombo.tmpl
 create mode 100644 templates/shared/user/org_profile_avatar.tmpl
 create mode 100644 templates/shared/user/profile_big_avatar.tmpl

diff --git a/models/user/user.go b/models/user/user.go
index 6f9c2f5b35..4b19eda67b 100644
--- a/models/user/user.go
+++ b/models/user/user.go
@@ -336,7 +336,7 @@ func GetUserFollowers(ctx context.Context, u, viewer *User, listOptions db.ListO
 
 // GetUserFollowing returns range of user's following.
 func GetUserFollowing(ctx context.Context, u, viewer *User, listOptions db.ListOptions) ([]*User, int64, error) {
-	sess := db.GetEngine(db.DefaultContext).
+	sess := db.GetEngine(ctx).
 		Select("`user`.*").
 		Join("LEFT", "follow", "`user`.id=follow.follow_id").
 		Where("follow.user_id=?", u.ID).
diff --git a/modules/context/org.go b/modules/context/org.go
index 355ba0ebd0..835c761372 100644
--- a/modules/context/org.go
+++ b/modules/context/org.go
@@ -161,7 +161,6 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
 	}
 	ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner
 	ctx.Data["IsOrganizationMember"] = ctx.Org.IsMember
-	ctx.Data["IsProjectEnabled"] = true
 	ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
 	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
 	ctx.Data["IsPublicMember"] = func(uid int64) bool {
diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go
index 4b33d943b3..21cb23000d 100644
--- a/routers/web/org/projects.go
+++ b/routers/web/org/projects.go
@@ -41,6 +41,7 @@ func MustEnableProjects(ctx *context.Context) {
 
 // Projects renders the home page of projects
 func Projects(ctx *context.Context) {
+	shared_user.PrepareContextForProfileBigAvatar(ctx)
 	ctx.Data["Title"] = ctx.Tr("repo.project_board")
 
 	sortType := ctx.FormTrim("sort")
diff --git a/routers/web/shared/user/header.go b/routers/web/shared/user/header.go
index 9594e6975a..516c853b02 100644
--- a/routers/web/shared/user/header.go
+++ b/routers/web/shared/user/header.go
@@ -4,35 +4,109 @@
 package user
 
 import (
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	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/log"
+	"code.gitea.io/gitea/modules/markup"
+	"code.gitea.io/gitea/modules/markup/markdown"
 	"code.gitea.io/gitea/modules/setting"
 )
 
-func RenderUserHeader(ctx *context.Context) {
-	ctx.Data["IsProjectEnabled"] = true
+// prepareContextForCommonProfile store some common data into context data for user's profile related pages (including the nav menu)
+// It is designed to be fast and safe to be called multiple times in one request
+func prepareContextForCommonProfile(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())
+	ctx.Data["EnableFeed"] = setting.Other.EnableFeed
+	ctx.Data["FeedURL"] = ctx.ContextUser.HomeLink()
+}
+
+// PrepareContextForProfileBigAvatar set the context for big avatar view on the profile page
+func PrepareContextForProfileBigAvatar(ctx *context.Context) {
+	prepareContextForCommonProfile(ctx)
+
+	ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx.Doer.ID, ctx.ContextUser.ID)
+	ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail && ctx.ContextUser.Email != "" && ctx.IsSigned && !ctx.ContextUser.KeepEmailPrivate
+
+	// Show OpenID URIs
+	openIDs, err := user_model.GetUserOpenIDs(ctx.ContextUser.ID)
+	if err != nil {
+		ctx.ServerError("GetUserOpenIDs", err)
+		return
+	}
+	ctx.Data["OpenIDs"] = openIDs
+
+	if len(ctx.ContextUser.Description) != 0 {
+		content, err := markdown.RenderString(&markup.RenderContext{
+			URLPrefix: ctx.Repo.RepoLink,
+			Metas:     map[string]string{"mode": "document"},
+			GitRepo:   ctx.Repo.GitRepo,
+			Ctx:       ctx,
+		}, ctx.ContextUser.Description)
 		if err != nil {
-			ctx.ServerError("OpenRepository", err)
+			ctx.ServerError("RenderString", err)
 			return
 		}
-		defer gitRepo.Close()
-		commit, err := gitRepo.GetBranchCommit(repo.DefaultBranch)
-		if err != nil {
-			ctx.ServerError("GetBranchCommit", err)
-			return
+		ctx.Data["RenderedDescription"] = content
+	}
+
+	showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
+	orgs, err := organization.FindOrgs(organization.FindOrgOptions{
+		UserID:         ctx.ContextUser.ID,
+		IncludePrivate: showPrivate,
+	})
+	if err != nil {
+		ctx.ServerError("FindOrgs", err)
+		return
+	}
+	ctx.Data["Orgs"] = orgs
+	ctx.Data["HasOrgsVisible"] = organization.HasOrgsVisible(orgs, ctx.Doer)
+
+	badges, _, err := user_model.GetUserBadges(ctx, ctx.ContextUser)
+	if err != nil {
+		ctx.ServerError("GetUserBadges", err)
+		return
+	}
+	ctx.Data["Badges"] = badges
+
+	// in case the numbers are already provided by other functions, no need to query again (which is slow)
+	if _, ok := ctx.Data["NumFollowers"]; !ok {
+		_, ctx.Data["NumFollowers"], _ = user_model.GetUserFollowers(ctx, ctx.ContextUser, ctx.Doer, db.ListOptions{PageSize: 1, Page: 1})
+	}
+	if _, ok := ctx.Data["NumFollowing"]; !ok {
+		_, ctx.Data["NumFollowing"], _ = user_model.GetUserFollowing(ctx, ctx.ContextUser, ctx.Doer, db.ListOptions{PageSize: 1, Page: 1})
+	}
+}
+
+func FindUserProfileReadme(ctx *context.Context) (profileGitRepo *git.Repository, profileReadmeBlob *git.Blob, profileClose func()) {
+	profileDbRepo, err := repo_model.GetRepositoryByName(ctx.ContextUser.ID, ".profile")
+	if err == nil && !profileDbRepo.IsEmpty {
+		if profileGitRepo, err = git.OpenRepository(ctx, profileDbRepo.RepoPath()); err != nil {
+			log.Error("FindUserProfileReadme failed to OpenRepository: %v", err)
+		} else {
+			if commit, err := profileGitRepo.GetBranchCommit(profileDbRepo.DefaultBranch); err != nil {
+				log.Error("FindUserProfileReadme failed to GetBranchCommit: %v", err)
+			} else {
+				profileReadmeBlob, _ = commit.GetBlobByPath("README.md")
+			}
 		}
-		blob, err := commit.GetBlobByPath("README.md")
-		if err == nil && blob != nil {
-			ctx.Data["ProfileReadme"] = true
+	}
+	return profileGitRepo, profileReadmeBlob, func() {
+		if profileGitRepo != nil {
+			_ = profileGitRepo.Close()
 		}
 	}
 }
+
+func RenderUserHeader(ctx *context.Context) {
+	prepareContextForCommonProfile(ctx)
+
+	_, profileReadmeBlob, profileClose := FindUserProfileReadme(ctx)
+	defer profileClose()
+	ctx.Data["HasProfileReadme"] = profileReadmeBlob != nil
+}
diff --git a/routers/web/user/code.go b/routers/web/user/code.go
index 15524de7d6..033f65c9c0 100644
--- a/routers/web/user/code.go
+++ b/routers/web/user/code.go
@@ -11,6 +11,7 @@ import (
 	"code.gitea.io/gitea/modules/context"
 	code_indexer "code.gitea.io/gitea/modules/indexer/code"
 	"code.gitea.io/gitea/modules/setting"
+	shared_user "code.gitea.io/gitea/routers/web/shared/user"
 )
 
 const (
@@ -23,8 +24,9 @@ func CodeSearch(ctx *context.Context) {
 		ctx.Redirect(ctx.ContextUser.HomeLink())
 		return
 	}
+	shared_user.PrepareContextForProfileBigAvatar(ctx)
+	shared_user.RenderUserHeader(ctx)
 
-	ctx.Data["IsProjectEnabled"] = true
 	ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
 	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
 	ctx.Data["Title"] = ctx.Tr("explore.code")
diff --git a/routers/web/user/home.go b/routers/web/user/home.go
index 1b0f651b07..6a89c507a9 100644
--- a/routers/web/user/home.go
+++ b/routers/web/user/home.go
@@ -857,7 +857,7 @@ func UsernameSubRoute(ctx *context.Context) {
 		context_service.UserAssignmentWeb()(ctx)
 		if !ctx.Written() {
 			ctx.Data["EnableFeed"] = setting.Other.EnableFeed
-			Profile(ctx)
+			OwnerProfile(ctx)
 		}
 	}
 }
diff --git a/routers/web/user/package.go b/routers/web/user/package.go
index 551e7f54c8..2e2c2a6e1f 100644
--- a/routers/web/user/package.go
+++ b/routers/web/user/package.go
@@ -37,6 +37,7 @@ const (
 
 // ListPackages displays a list of all packages of the context user
 func ListPackages(ctx *context.Context) {
+	shared_user.PrepareContextForProfileBigAvatar(ctx)
 	page := ctx.FormInt("page")
 	if page <= 1 {
 		page = 1
@@ -259,6 +260,7 @@ func ViewPackageVersion(ctx *context.Context) {
 
 // ListPackageVersions lists all versions of a package
 func ListPackageVersions(ctx *context.Context) {
+	shared_user.PrepareContextForProfileBigAvatar(ctx)
 	p, err := packages_model.GetPackageByName(ctx, ctx.Package.Owner.ID, packages_model.Type(ctx.Params("type")), ctx.Params("name"))
 	if err != nil {
 		if err == packages_model.ErrPackageNotExist {
diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go
index 6f9f84d60d..442fd0433a 100644
--- a/routers/web/user/profile.go
+++ b/routers/web/user/profile.go
@@ -11,22 +11,22 @@ import (
 
 	activities_model "code.gitea.io/gitea/models/activities"
 	"code.gitea.io/gitea/models/db"
-	"code.gitea.io/gitea/models/organization"
-	project_model "code.gitea.io/gitea/models/project"
 	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/log"
 	"code.gitea.io/gitea/modules/markup"
 	"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"
 	"code.gitea.io/gitea/routers/web/org"
+	shared_user "code.gitea.io/gitea/routers/web/shared/user"
 )
 
-// Profile render user's profile page
-func Profile(ctx *context.Context) {
+// OwnerProfile render profile page for a user or a organization (aka, repo owner)
+func OwnerProfile(ctx *context.Context) {
 	if strings.Contains(ctx.Req.Header.Get("Accept"), "application/rss+xml") {
 		feed.ShowUserFeedRSS(ctx)
 		return
@@ -38,36 +38,22 @@ func Profile(ctx *context.Context) {
 
 	if ctx.ContextUser.IsOrganization() {
 		org.Home(ctx)
-		return
+	} else {
+		userProfile(ctx)
 	}
+}
 
+func userProfile(ctx *context.Context) {
 	// check view permissions
 	if !user_model.IsUserVisibleToViewer(ctx, ctx.ContextUser, ctx.Doer) {
 		ctx.NotFound("user", fmt.Errorf(ctx.ContextUser.Name))
 		return
 	}
 
-	// advertise feed via meta tag
-	ctx.Data["FeedURL"] = ctx.ContextUser.HomeLink()
-
-	// Show OpenID URIs
-	openIDs, err := user_model.GetUserOpenIDs(ctx.ContextUser.ID)
-	if err != nil {
-		ctx.ServerError("GetUserOpenIDs", err)
-		return
-	}
-
-	var isFollowing bool
-	if ctx.Doer != nil {
-		isFollowing = user_model.IsFollowing(ctx.Doer.ID, ctx.ContextUser.ID)
-	}
-
 	ctx.Data["Title"] = ctx.ContextUser.DisplayName()
 	ctx.Data["PageIsUserProfile"] = true
-	ctx.Data["ContextUser"] = ctx.ContextUser
-	ctx.Data["OpenIDs"] = openIDs
-	ctx.Data["IsFollowing"] = isFollowing
 
+	// prepare heatmap data
 	if setting.Service.EnableUserHeatmap {
 		data, err := activities_model.GetUserHeatmapDataByUser(ctx.ContextUser, ctx.Doer)
 		if err != nil {
@@ -78,75 +64,28 @@ func Profile(ctx *context.Context) {
 		ctx.Data["HeatmapTotalContributions"] = activities_model.GetTotalContributionsInHeatmap(data)
 	}
 
-	if len(ctx.ContextUser.Description) != 0 {
-		content, err := markdown.RenderString(&markup.RenderContext{
-			URLPrefix: ctx.Repo.RepoLink,
-			Metas:     map[string]string{"mode": "document"},
-			GitRepo:   ctx.Repo.GitRepo,
-			Ctx:       ctx,
-		}, ctx.ContextUser.Description)
-		if err != nil {
-			ctx.ServerError("RenderString", err)
-			return
-		}
-		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(setting.UI.MaxDisplayFileSize)
-			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
-		}
-	}
+	profileGitRepo, profileReadmeBlob, profileClose := shared_user.FindUserProfileReadme(ctx)
+	defer profileClose()
 
 	showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
+	prepareUserProfileTabData(ctx, showPrivate, profileGitRepo, profileReadmeBlob)
+	// call PrepareContextForProfileBigAvatar later to avoid re-querying the NumFollowers & NumFollowing
+	shared_user.PrepareContextForProfileBigAvatar(ctx)
+	ctx.HTML(http.StatusOK, tplProfile)
+}
 
-	orgs, err := organization.FindOrgs(organization.FindOrgOptions{
-		UserID:         ctx.ContextUser.ID,
-		IncludePrivate: showPrivate,
-	})
-	if err != nil {
-		ctx.ServerError("FindOrgs", err)
-		return
-	}
-
-	ctx.Data["Orgs"] = orgs
-	ctx.Data["HasOrgsVisible"] = organization.HasOrgsVisible(orgs, ctx.Doer)
-
-	badges, _, err := user_model.GetUserBadges(ctx, ctx.ContextUser)
-	if err != nil {
-		ctx.ServerError("GetUserBadges", err)
-		return
-	}
-	ctx.Data["Badges"] = badges
-
+func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileGitRepo *git.Repository, profileReadme *git.Blob) {
+	// if there is a profile readme, default to "overview" page, otherwise, default to "repositories" page
 	tab := ctx.FormString("tab")
+	if tab == "" {
+		if profileReadme != nil {
+			tab = "overview"
+		} else {
+			tab = "repositories"
+		}
+	}
 	ctx.Data["TabName"] = tab
+	ctx.Data["HasProfileReadme"] = profileReadme != nil
 
 	page := ctx.FormInt("page")
 	if page <= 0 {
@@ -154,12 +93,7 @@ func Profile(ctx *context.Context) {
 	}
 
 	pagingNum := setting.UI.User.RepoPagingNum
-	if tab == "activity" {
-		pagingNum = setting.UI.FeedPagingNum
-	}
-
 	topicOnly := ctx.FormBool("topic")
-
 	var (
 		repos   []*repo_model.Repository
 		count   int64
@@ -228,6 +162,7 @@ func Profile(ctx *context.Context) {
 		total = int(count)
 	case "activity":
 		date := ctx.FormString("date")
+		pagingNum = setting.UI.FeedPagingNum
 		items, count, err := activities_model.GetFeeds(ctx, activities_model.GetFeedsOptions{
 			RequestedUser:   ctx.ContextUser,
 			Actor:           ctx.Doer,
@@ -271,16 +206,6 @@ func Profile(ctx *context.Context) {
 		}
 
 		total = int(count)
-	case "projects":
-		ctx.Data["OpenProjects"], _, err = project_model.FindProjects(ctx, project_model.SearchOptions{
-			Page:     -1,
-			IsClosed: util.OptionalBoolFalse,
-			Type:     project_model.TypeIndividual,
-		})
-		if err != nil {
-			ctx.ServerError("GetProjects", err)
-			return
-		}
 	case "watching":
 		repos, count, err = repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{
 			ListOptions: db.ListOptions{
@@ -303,7 +228,17 @@ func Profile(ctx *context.Context) {
 		}
 
 		total = int(count)
-	default:
+	case "overview":
+		if bytes, err := profileReadme.GetBlobContent(setting.UI.MaxDisplayFileSize); err != nil {
+			log.Error("failed to GetBlobContent: %v", err)
+		} else {
+			if profileContent, err := markdown.RenderString(&markup.RenderContext{Ctx: ctx, GitRepo: profileGitRepo}, bytes); err != nil {
+				log.Error("failed to RenderString: %v", err)
+			} else {
+				ctx.Data["ProfileReadme"] = profileContent
+			}
+		}
+	default: // default to "repositories"
 		repos, count, err = repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{
 			ListOptions: db.ListOptions{
 				PageSize: pagingNum,
@@ -339,13 +274,6 @@ func Profile(ctx *context.Context) {
 		pager.AddParam(ctx, "date", "Date")
 	}
 	ctx.Data["Page"] = pager
-	ctx.Data["IsProjectEnabled"] = true
-	ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
-	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
-
-	ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail && ctx.ContextUser.Email != "" && ctx.IsSigned && !ctx.ContextUser.KeepEmailPrivate
-
-	ctx.HTML(http.StatusOK, tplProfile)
 }
 
 // Action response for follow/unfollow user request
diff --git a/templates/code/searchcombo.tmpl b/templates/code/searchcombo.tmpl
new file mode 100644
index 0000000000..e495b3b454
--- /dev/null
+++ b/templates/code/searchcombo.tmpl
@@ -0,0 +1,17 @@
+{{template "code/searchform" .}}
+<div class="divider"></div>
+<div class="ui user list">
+	{{if .CodeIndexerUnavailable}}
+		<div class="ui error message">
+			<p>{{$.locale.Tr "explore.code_search_unavailable"}}</p>
+		</div>
+	{{else if .SearchResults}}
+		<h3>
+			{{.locale.Tr "explore.code_search_results" (.Keyword|Escape) | Str2html}}
+		</h3>
+		{{template "code/searchresults" .}}
+	{{else if .Keyword}}
+		<div>{{$.locale.Tr "explore.code_no_results"}}</div>
+	{{end}}
+</div>
+{{template "base/paginate" .}}
diff --git a/templates/explore/code.tmpl b/templates/explore/code.tmpl
index c537cca05e..2298575887 100644
--- a/templates/explore/code.tmpl
+++ b/templates/explore/code.tmpl
@@ -2,24 +2,7 @@
 <div role="main" aria-label="{{.Title}}" class="page-content explore users">
 	{{template "explore/navbar" .}}
 	<div class="ui container">
-		{{template "code/searchform" .}}
-		<div class="divider"></div>
-		<div class="ui user list">
-			{{if .CodeIndexerUnavailable}}
-				<div class="ui error message">
-					<p>{{$.locale.Tr "explore.code_search_unavailable"}}</p>
-				</div>
-			{{else if .SearchResults}}
-				<h3>
-					{{.locale.Tr "explore.code_search_results" (.Keyword|Escape) | Str2html}}
-				</h3>
-				{{template "code/searchresults" .}}
-			{{else if .Keyword}}
-				<div>{{$.locale.Tr "explore.code_no_results"}}</div>
-			{{end}}
-		</div>
-
-		{{template "base/paginate" .}}
+		{{template "code/searchcombo" .}}
 	</div>
 </div>
 {{template "base/footer" .}}
diff --git a/templates/org/menu.tmpl b/templates/org/menu.tmpl
index 1bb19a0673..6492e5e668 100644
--- a/templates/org/menu.tmpl
+++ b/templates/org/menu.tmpl
@@ -1,4 +1,4 @@
-<div class="ui tabs container">
+<div class="ui container">
 	<div class="ui secondary stackable pointing menu">
 		<a class="{{if .PageIsViewRepositories}}active {{end}}item" href="{{$.Org.HomeLink}}">
 			{{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}}
@@ -6,7 +6,7 @@
 				<div class="ui small label">{{.ContextUser.NumRepos}}</div>
 			{{end}}
 		</a>
-		{{if and .IsProjectEnabled .CanReadProjects}}
+		{{if .CanReadProjects}}
 		<a class="{{if .PageIsViewProjects}}active {{end}}item" href="{{$.Org.HomeLink}}/-/projects">
 			{{svg "octicon-project-symlink"}} {{.locale.Tr "user.projects"}}
 		</a>
diff --git a/templates/org/projects/list.tmpl b/templates/org/projects/list.tmpl
index 1f113b28c8..689091e5e0 100644
--- a/templates/org/projects/list.tmpl
+++ b/templates/org/projects/list.tmpl
@@ -1,6 +1,27 @@
 {{template "base/head" .}}
-<div role="main" aria-label="{{.Title}}" class="page-content repository packages">
-	{{template "user/overview/header" .}}
-	{{template "projects/list" .}}
-</div>
+{{if .ContextUser.IsOrganization}}
+	<div role="main" aria-label="{{.Title}}" class="page-content repository packages">
+		{{template "shared/user/org_profile_avatar" .}}
+		<div class="ui container">
+		{{template "user/overview/header" .}}
+		{{template "projects/list" .}}
+		</div>
+	</div>
+{{else}}
+	<div role="main" aria-label="{{.Title}}" class="page-content user profile">
+		<div class="ui container">
+			<div class="ui stackable grid">
+				<div class="ui four wide column">
+					{{template "shared/user/profile_big_avatar" .}}
+				</div>
+				<div class="ui twelve wide column">
+				<div class="gt-mb-4">
+					{{template "user/overview/header" .}}
+				</div>
+				{{template "projects/list" .}}
+				</div>
+			</div>
+		</div>
+	</div>
+{{end}}
 {{template "base/footer" .}}
diff --git a/templates/org/projects/new.tmpl b/templates/org/projects/new.tmpl
index 7011eb56d7..fc52130f68 100644
--- a/templates/org/projects/new.tmpl
+++ b/templates/org/projects/new.tmpl
@@ -1,6 +1,9 @@
 {{template "base/head" .}}
 <div role="main" aria-label="{{.Title}}" class="page-content organization projects edit-project new">
+	{{template "shared/user/org_profile_avatar" .}}
+	<div class="ui container">
 	{{template "user/overview/header" .}}
 	{{template "projects/new" .}}
+	</div>
 </div>
 {{template "base/footer" .}}
diff --git a/templates/org/projects/view.tmpl b/templates/org/projects/view.tmpl
index c2d8f015f1..ef8ec24225 100644
--- a/templates/org/projects/view.tmpl
+++ b/templates/org/projects/view.tmpl
@@ -1,6 +1,9 @@
 {{template "base/head" .}}
 <div role="main" aria-label="{{.Title}}" class="page-content repository packages">
+	{{template "shared/user/org_profile_avatar" .}}
+	<div class="ui container">
 	{{template "user/overview/header" .}}
 	{{template "projects/view" .}}
+	</div>
 </div>
 {{template "base/footer" .}}
diff --git a/templates/package/settings.tmpl b/templates/package/settings.tmpl
index 205d875c3e..abcdd7ec1c 100644
--- a/templates/package/settings.tmpl
+++ b/templates/package/settings.tmpl
@@ -1,7 +1,8 @@
 {{template "base/head" .}}
 <div role="main" aria-label="{{.Title}}" class="page-content repository settings options">
-	{{template "user/overview/header" .}}
+	{{template "shared/user/org_profile_avatar" .}}
 	<div class="ui container">
+		{{template "user/overview/header" .}}
 		{{template "base/alert" .}}
 		<p><a href="{{.PackageDescriptor.FullWebLink}}">{{.PackageDescriptor.Package.Name}} ({{.PackageDescriptor.Version.Version}})</a> / <strong>{{.locale.Tr "repo.settings"}}</strong></p>
 		<h4 class="ui top attached header">
diff --git a/templates/package/shared/list.tmpl b/templates/package/shared/list.tmpl
index 6ac930e5c7..1acafff41a 100644
--- a/templates/package/shared/list.tmpl
+++ b/templates/package/shared/list.tmpl
@@ -1,4 +1,3 @@
-<div class="ui container">
 	{{template "base/alert" .}}
 	<form class="ui form ignore-dirty">
 		<div class="ui fluid action input">
@@ -37,7 +36,7 @@
 			</li>
 		{{else}}
 			{{if not .HasPackages}}
-				<div class="empty center">
+				<div class="gt-pt-5 empty center">
 					{{svg "octicon-package" 48}}
 					<h2>{{.locale.Tr "packages.empty"}}</h2>
 					{{if and .Repository .CanWritePackages}}
@@ -52,4 +51,3 @@
 		{{end}}
 		{{template "base/paginate" .}}
 	</div>
-</div>
diff --git a/templates/package/shared/versionlist.tmpl b/templates/package/shared/versionlist.tmpl
index 9fc53d806c..be5c2a16de 100644
--- a/templates/package/shared/versionlist.tmpl
+++ b/templates/package/shared/versionlist.tmpl
@@ -1,4 +1,3 @@
-<div class="ui container">
 	<p><a href="{{.PackageDescriptor.PackageWebLink}}">{{.PackageDescriptor.Package.Name}}</a> / <strong>{{.locale.Tr "packages.versions"}}</strong></p>
 	<form class="ui form ignore-dirty">
 		<div class="ui fluid action input">
@@ -36,4 +35,3 @@
 		{{end}}
 		{{template "base/paginate" .}}
 	</div>
-</div>
diff --git a/templates/package/view.tmpl b/templates/package/view.tmpl
index 6064db1b4c..fe4214cbd2 100644
--- a/templates/package/view.tmpl
+++ b/templates/package/view.tmpl
@@ -1,7 +1,8 @@
 {{template "base/head" .}}
 <div role="main" aria-label="{{.Title}}" class="page-content repository view issue packages">
-	{{template "user/overview/header" .}}
+	{{template "shared/user/org_profile_avatar" .}}
 	<div class="ui container">
+		{{template "user/overview/header" .}}
 		<div class="issue-title-header">
 			<div class="issue-title">
 				<h1>{{.PackageDescriptor.Package.Name}} ({{.PackageDescriptor.Version.Version}})</h1>
diff --git a/templates/projects/list.tmpl b/templates/projects/list.tmpl
index 9725c1e63b..ebce0ea48f 100644
--- a/templates/projects/list.tmpl
+++ b/templates/projects/list.tmpl
@@ -1,10 +1,6 @@
-<div role="main" aria-label="{{.Title}}" class="page-content repository projects">
-	<div class="ui container">
 		{{if .CanWriteProjects}}
-			<div class="navbar">
-				<div class="ui right">
-					<a class="ui small green button" href="{{$.Link}}/new">{{.locale.Tr "repo.projects.new"}}</a>
-				</div>
+			<div class="gt-tr">
+				<a class="ui small green button" href="{{$.Link}}/new">{{.locale.Tr "repo.projects.new"}}</a>
 			</div>
 			<div class="divider"></div>
 		{{end}}
@@ -75,8 +71,6 @@
 
 			{{template "base/paginate" .}}
 		</div>
-	</div>
-</div>
 
 {{if $.CanWriteProjects}}
 <div class="ui g-modal-confirm delete modal">
diff --git a/templates/projects/new.tmpl b/templates/projects/new.tmpl
index bc3b19e915..e110e880cf 100644
--- a/templates/projects/new.tmpl
+++ b/templates/projects/new.tmpl
@@ -1,4 +1,3 @@
-<div class="ui container">
 	<h2 class="ui dividing header">
 		{{if .PageIsEditProjects}}
 		{{.locale.Tr "repo.projects.edit"}}
@@ -55,7 +54,6 @@
 				</div>
 			</div>
 		</div>
-		<div class="ui container">
 			<div class="divider"></div>
 			<div class="ui left">
 				<a class="ui cancel button" href="{{$.CancelLink}}">
@@ -65,6 +63,4 @@
 					{{if .PageIsEditProjects}}{{.locale.Tr "repo.projects.modify"}}{{else}}{{.locale.Tr "repo.projects.create"}}{{end}}
 				</button>
 			</div>
-		</div>
 	</form>
-</div>
diff --git a/templates/repo/packages.tmpl b/templates/repo/packages.tmpl
index 47fa338a44..14e32c125b 100644
--- a/templates/repo/packages.tmpl
+++ b/templates/repo/packages.tmpl
@@ -1,6 +1,8 @@
 {{template "base/head" .}}
 <div role="main" aria-label="{{.Title}}" class="page-content repository packages">
 	{{template "repo/header" .}}
+	<div class="ui container">
 	{{template "package/shared/list" .}}
+	</div>
 </div>
 {{template "base/footer" .}}
diff --git a/templates/repo/projects/new.tmpl b/templates/repo/projects/new.tmpl
index 531cae8d11..e70f3bca87 100644
--- a/templates/repo/projects/new.tmpl
+++ b/templates/repo/projects/new.tmpl
@@ -1,6 +1,8 @@
 {{template "base/head" .}}
 <div role="main" aria-label="{{.Title}}" class="page-content repository projects edit-project new milestone">
 	{{template "repo/header" .}}
+	<div class="ui container">
 	{{template "projects/new" .}}
+	</div>
 </div>
 {{template "base/footer" .}}
diff --git a/templates/shared/user/org_profile_avatar.tmpl b/templates/shared/user/org_profile_avatar.tmpl
new file mode 100644
index 0000000000..a56763e288
--- /dev/null
+++ b/templates/shared/user/org_profile_avatar.tmpl
@@ -0,0 +1,16 @@
+{{with .ContextUser}}
+	<div class="ui container">
+		<div class="ui vertically grid head">
+			<div class="column">
+				<div class="ui header">
+					{{avatar $.Context . 100}}
+					<span class="text thin grey"><a href="{{.HomeLink}}">{{.DisplayName}}</a></span>
+					<span class="org-visibility">
+						{{if .Visibility.IsLimited}}<div class="ui medium basic horizontal label">{{$.locale.Tr "org.settings.visibility.limited_shortname"}}</div>{{end}}
+						{{if .Visibility.IsPrivate}}<div class="ui medium basic horizontal label">{{$.locale.Tr "org.settings.visibility.private_shortname"}}</div>{{end}}
+					</span>
+				</div>
+			</div>
+		</div>
+	</div>
+{{end}}
diff --git a/templates/shared/user/profile_big_avatar.tmpl b/templates/shared/user/profile_big_avatar.tmpl
new file mode 100644
index 0000000000..408877e481
--- /dev/null
+++ b/templates/shared/user/profile_big_avatar.tmpl
@@ -0,0 +1,116 @@
+<div class="ui card">
+	<div id="profile-avatar" class="content gt-df">
+	{{if eq .SignedUserID .ContextUser.ID}}
+		<a class="image" href="{{AppSubUrl}}/user/settings" data-tooltip-content="{{.locale.Tr "user.change_avatar"}}">
+			{{/* the size doesn't take affect (and no need to take affect), image size(width) should be controlled by the parent container since this is not a flex layout*/}}
+			{{avatar $.Context .ContextUser 256}}
+		</a>
+	{{else}}
+		<span class="image">
+			{{avatar $.Context .ContextUser 256}}
+		</span>
+	{{end}}
+	</div>
+	<div class="content gt-word-break profile-avatar-name">
+		{{if .ContextUser.FullName}}<span class="header text center">{{.ContextUser.FullName}}</span>{{end}}
+		<span class="username text center">{{.ContextUser.Name}}</span>
+		{{if .EnableFeed}}
+			<a href="{{.ContextUser.HomeLink}}.rss"><i class="ui text grey gt-ml-3" data-tooltip-content="{{.locale.Tr "rss_feed"}}">{{svg "octicon-rss" 18}}</i></a>
+		{{end}}
+		<div class="gt-mt-3">
+			<a class="muted" href="{{.ContextUser.HomeLink}}?tab=followers">{{svg "octicon-person" 18 "gt-mr-2"}}{{.NumFollowers}} {{.locale.Tr "user.followers"}}</a> · <a class="muted" href="{{.ContextUser.HomeLink}}?tab=following">{{.NumFollowing}} {{.locale.Tr "user.following"}}</a>
+		</div>
+	</div>
+	<div class="extra content gt-word-break">
+		<ul>
+			{{if .ContextUser.Location}}
+				<li>{{svg "octicon-location"}} {{.ContextUser.Location}}</li>
+			{{end}}
+			{{if (eq .SignedUserID .ContextUser.ID)}}
+				<li>
+					{{svg "octicon-mail"}}
+					<a href="mailto:{{.ContextUser.Email}}" rel="nofollow">{{.ContextUser.Email}}</a>
+					<a href="{{AppSubUrl}}/user/settings#keep-email-private">
+						{{if .ShowUserEmail}}
+							<i class="ui right" data-tooltip-content="{{.locale.Tr "user.email_visibility.limited"}}">
+								{{svg "octicon-unlock"}}
+							</i>
+						{{else}}
+							<i class="ui right" data-tooltip-content="{{.locale.Tr "user.email_visibility.private"}}">
+								{{svg "octicon-lock"}}
+							</i>
+						{{end}}
+					</a>
+				</li>
+			{{else}}
+				{{if .ShowUserEmail}}
+					<li>
+						{{svg "octicon-mail"}}
+						<a href="mailto:{{.ContextUser.Email}}" rel="nofollow">{{.ContextUser.Email}}</a>
+					</li>
+				{{end}}
+			{{end}}
+			{{if .ContextUser.Website}}
+				<li>
+					{{svg "octicon-link"}}
+					<a target="_blank" rel="noopener noreferrer me" href="{{.ContextUser.Website}}">{{.ContextUser.Website}}</a>
+				</li>
+			{{end}}
+			{{if $.RenderedDescription}}
+				<li>
+					<div class="render-content markup">{{$.RenderedDescription|Str2html}}</div>
+				</li>
+			{{end}}
+			{{range .OpenIDs}}
+				{{if .Show}}
+					<li>
+						{{svg "fontawesome-openid"}}
+						<a target="_blank" rel="noopener noreferrer" href="{{.URI}}">{{.URI}}</a>
+					</li>
+				{{end}}
+			{{end}}
+			<li>{{svg "octicon-clock"}} {{.locale.Tr "user.joined_on" (DateTime "short" .ContextUser.CreatedUnix) | Safe}}</li>
+			{{if and .Orgs .HasOrgsVisible}}
+			<li>
+				<ul class="user-orgs">
+				{{range .Orgs}}
+					{{if (or .Visibility.IsPublic (and ($.SignedUser) (or .Visibility.IsLimited (and (.HasMemberWithUserID $.SignedUserID) .Visibility.IsPrivate) ($.IsAdmin))))}}
+					<li>
+						<a href="{{.HomeLink}}" data-tooltip-content="{{.Name}}">
+							{{avatar $.Context .}}
+						</a>
+					</li>
+					{{end}}
+				{{end}}
+				</ul>
+			</li>
+			{{end}}
+			{{if .Badges}}
+			<li>
+				<ul class="user-badges">
+				{{range .Badges}}
+					<li>
+						<img width="64" height="64" src="{{.ImageURL}}" alt="{{.Description}}" data-tooltip-content="{{.Description}}">
+					</li>
+				{{end}}
+				</ul>
+			</li>
+			{{end}}
+			{{if and .IsSigned (ne .SignedUserID .ContextUser.ID)}}
+			<li class="follow">
+				{{if $.IsFollowing}}
+					<form method="post" action="{{.Link}}?action=unfollow&redirect_to={{$.Link}}">
+						{{$.CsrfTokenHtml}}
+						<button type="submit" class="ui basic red button">{{svg "octicon-person"}} {{.locale.Tr "user.unfollow"}}</button>
+					</form>
+				{{else}}
+					<form method="post" action="{{.Link}}?action=follow&redirect_to={{$.Link}}">
+						{{$.CsrfTokenHtml}}
+						<button type="submit" class="ui basic green button">{{svg "octicon-person"}} {{.locale.Tr "user.follow"}}</button>
+					</form>
+				{{end}}
+			</li>
+			{{end}}
+		</ul>
+	</div>
+</div>
diff --git a/templates/user/code.tmpl b/templates/user/code.tmpl
index cc02ef8314..da9a3c3a24 100644
--- a/templates/user/code.tmpl
+++ b/templates/user/code.tmpl
@@ -1,25 +1,25 @@
 {{template "base/head" .}}
-<div role="main" aria-label="{{.Title}}" class="page-content repository code-search">
-	{{template "user/overview/header" .}}
-	<div class="ui container">
-		{{template "code/searchform" .}}
-		<div class="divider"></div>
-		<div class="ui user list">
-			{{if .CodeIndexerUnavailable}}
-				<div class="ui error message">
-					<p>{{$.locale.Tr "explore.code_search_unavailable"}}</p>
-				</div>
-			{{else if .SearchResults}}
-				<h3>
-					{{.locale.Tr "explore.code_search_results" (.Keyword|Escape) | Str2html}}
-				</h3>
-				{{template "code/searchresults" .}}
-			{{else if .Keyword}}
-				<div>{{$.locale.Tr "explore.code_no_results"}}</div>
-			{{end}}
+{{if .ContextUser.IsOrganization}}
+	<div role="main" aria-label="{{.Title}}" class="page-content repository">
+		{{template "shared/user/org_profile_avatar" .}}
+		<div class="ui container">
+			{{template "user/overview/header" .}}
+			{{template "code/searchcombo" .}}
 		</div>
-
-		{{template "base/paginate" .}}
 	</div>
-</div>
+{{else}}
+	<div role="main" aria-label="{{.Title}}" class="page-content user profile">
+		<div class="ui container">
+			<div class="ui stackable grid">
+				<div class="ui four wide column">
+					{{template "shared/user/profile_big_avatar" .}}
+				</div>
+				<div class="ui twelve wide column">
+					{{template "user/overview/header" .}}
+					{{template "code/searchcombo" .}}
+				</div>
+			</div>
+		</div>
+	</div>
+{{end}}
 {{template "base/footer" .}}
diff --git a/templates/user/overview/header.tmpl b/templates/user/overview/header.tmpl
index 6016aca447..0491020959 100644
--- a/templates/user/overview/header.tmpl
+++ b/templates/user/overview/header.tmpl
@@ -1,92 +1,69 @@
-<!-- TODO: make template org and user can share -->
-{{if or (.IsPackagesPage) (.PageIsViewProjects)}}
-	{{with .ContextUser}}
-		<div class="ui container">
-		<div class="ui vertically grid head">
-			<div class="column">
-				<div class="ui header">
-					{{avatar $.Context . 100}}
-					<span class="text thin grey"><a href="{{.HomeLink}}">{{.DisplayName}}</a></span>
-					<span class="org-visibility">
-						{{if .Visibility.IsLimited}}<div class="ui medium basic horizontal label">{{$.locale.Tr "org.settings.visibility.limited_shortname"}}</div>{{end}}
-						{{if .Visibility.IsPrivate}}<div class="ui medium basic horizontal label">{{$.locale.Tr "org.settings.visibility.private_shortname"}}</div>{{end}}
-					</span>
-				</div>
-			</div>
-		</div>
-	</div>
+<div class="ui secondary stackable pointing menu">
+	{{if .HasProfileReadme}}
+	<a class='{{if eq .TabName "overview"}}active {{end}}item' href="{{.ContextUser.HomeLink}}?tab=overview">
+		{{svg "octicon-info"}} {{.locale.Tr "user.overview"}}
+	</a>
+	{{end}}
+	<a class="{{if eq .TabName "repositories"}}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>
+		{{end}}
+	</a>
+	{{if or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadProjects)}}
+	<a href="{{.ContextUser.HomeLink}}/-/projects" class="{{if .PageIsViewProjects}}active {{end}}item">
+		{{svg "octicon-project-symlink"}} {{.locale.Tr "user.projects"}}
+	</a>
+	{{end}}
+	{{if and .IsPackageEnabled (or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadPackages))}}
+		<a href="{{.ContextUser.HomeLink}}/-/packages" class="{{if .IsPackagesPage}}active {{end}}item">
+			{{svg "octicon-package"}} {{.locale.Tr "packages.title"}}
+		</a>
+	{{end}}
+	{{if and .IsRepoIndexerEnabled (or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadCode))}}
+		<a href="{{.ContextUser.HomeLink}}/-/code" class="{{if .IsCodePage}}active {{end}}item">
+			{{svg "octicon-code"}} {{.locale.Tr "user.code"}}
+		</a>
 	{{end}}
-{{end}}
 
-<div class="ui tabs container">
-	<div class="ui secondary stackable pointing menu">
-		{{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>
-			{{end}}
-		</a>
-		{{if and .IsProjectEnabled (or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadProjects))}}
-		<a href="{{.ContextUser.HomeLink}}/-/projects" class="{{if .PageIsViewProjects}}active {{end}}item">
-			{{svg "octicon-project-symlink"}} {{.locale.Tr "user.projects"}}
-		</a>
-		{{end}}
-		{{if and .IsPackageEnabled (or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadPackages))}}
-			<a href="{{.ContextUser.HomeLink}}/-/packages" class="{{if .IsPackagesPage}}active {{end}}item">
-				{{svg "octicon-package"}} {{.locale.Tr "packages.title"}}
+	{{if .ContextUser.IsOrganization}}
+		{{if .IsOrganizationMember}}
+			<a class="item" href="{{$.OrgLink}}/members">
+				{{svg "octicon-person"}}&nbsp;{{$.locale.Tr "org.members"}}
+				{{if .NumMembers}}
+					<div class="ui small label">{{.NumMembers}}</div>
+				{{end}}
 			</a>
-		{{end}}
-		{{if and .IsRepoIndexerEnabled (or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadCode))}}
-			<a href="{{.ContextUser.HomeLink}}/-/code" class="{{if .IsCodePage}}active {{end}}item">
-				{{svg "octicon-code"}} {{.locale.Tr "user.code"}}
+			<a class="item" href="{{$.OrgLink}}/teams">
+				{{svg "octicon-people"}}&nbsp;{{$.locale.Tr "org.teams"}}
+				{{if .NumTeams}}
+					<div class="ui small label">{{.NumTeams}}</div>
+				{{end}}
 			</a>
 		{{end}}
 
-		{{if .ContextUser.IsOrganization}}
-			{{if .IsOrganizationMember}}
-				<a class="item" href="{{$.OrgLink}}/members">
-					{{svg "octicon-person"}}&nbsp;{{$.locale.Tr "org.members"}}
-					{{if .NumMembers}}
-						<div class="ui small label">{{.NumMembers}}</div>
-					{{end}}
+		{{if .IsOrganizationOwner}}
+			<div class="right menu">
+				<a class="item" href="{{.OrgLink}}/settings">
+				{{svg "octicon-tools"}} {{.locale.Tr "repo.settings"}}
 				</a>
-				<a class="item" href="{{$.OrgLink}}/teams">
-					{{svg "octicon-people"}}&nbsp;{{$.locale.Tr "org.teams"}}
-					{{if .NumTeams}}
-						<div class="ui small label">{{.NumTeams}}</div>
-					{{end}}
-				</a>
-			{{end}}
-
-			{{if .IsOrganizationOwner}}
-				<div class="right menu">
-					<a class="item" href="{{.OrgLink}}/settings">
-					{{svg "octicon-tools"}} {{.locale.Tr "repo.settings"}}
-					</a>
-				</div>
-			{{end}}
+			</div>
+		{{end}}
+	{{else}}
+		<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 "activity"}}active {{end}}item' href="{{.ContextUser.HomeLink}}?tab=activity">
-				{{svg "octicon-rss"}} {{.locale.Tr "user.activity"}}
+			<a class='{{if eq .TabName "watching"}}active {{end}}item' href="{{.ContextUser.HomeLink}}?tab=watching">
+				{{svg "octicon-eye"}} {{.locale.Tr "user.watched"}}
 			</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}}
 		{{end}}
-
-	</div>
+	{{end}}
 </div>
diff --git a/templates/user/overview/package_versions.tmpl b/templates/user/overview/package_versions.tmpl
index 0bab740f5c..3757e922e3 100644
--- a/templates/user/overview/package_versions.tmpl
+++ b/templates/user/overview/package_versions.tmpl
@@ -1,6 +1,27 @@
 {{template "base/head" .}}
-<div role="main" aria-label="{{.Title}}" class="page-content repository packages">
-	{{template "user/overview/header" .}}
-	{{template "package/shared/versionlist" .}}
+{{if .ContextUser.IsOrganization}}
+	<div role="main" aria-label="{{.Title}}" class="page-content repository packages">
+		{{template "shared/user/org_profile_avatar" .}}
+		<div class="ui container">
+		{{template "user/overview/header" .}}
+		{{template "package/shared/versionlist" .}}
+		</div>
+	</div>
+{{else}}
+<div role="main" aria-label="{{.Title}}" class="page-content user profile packages">
+	<div class="ui container">
+		<div class="ui stackable grid">
+			<div class="ui four wide column">
+				{{template "shared/user/profile_big_avatar" .}}
+			</div>
+			<div class="ui twelve wide column">
+				<div class="gt-mb-4">
+					{{template "user/overview/header" .}}
+				</div>
+				{{template "package/shared/versionlist" .}}
+			</div>
+		</div>
+	</div>
 </div>
+{{end}}
 {{template "base/footer" .}}
diff --git a/templates/user/overview/packages.tmpl b/templates/user/overview/packages.tmpl
index 8f8597c42d..4fd17696d1 100644
--- a/templates/user/overview/packages.tmpl
+++ b/templates/user/overview/packages.tmpl
@@ -1,6 +1,27 @@
 {{template "base/head" .}}
-<div role="main" aria-label="{{.Title}}" class="page-content repository packages">
-	{{template "user/overview/header" .}}
-	{{template "package/shared/list" .}}
-</div>
+{{if .ContextUser.IsOrganization}}
+	<div role="main" aria-label="{{.Title}}" class="page-content repository packages">
+		{{template "shared/user/org_profile_avatar" .}}
+		<div class="ui container">
+		{{template "user/overview/header" .}}
+		{{template "package/shared/list" .}}
+		</div>
+	</div>
+{{else}}
+	<div role="main" aria-label="{{.Title}}" class="page-content user profile packages">
+		<div class="ui container">
+			<div class="ui stackable grid">
+				<div class="ui four wide column">
+					{{template "shared/user/profile_big_avatar" .}}
+				</div>
+				<div class="ui twelve wide column">
+					<div class="gt-mb-4">
+						{{template "user/overview/header" .}}
+					</div>
+					{{template "package/shared/list" .}}
+				</div>
+			</div>
+		</div>
+	</div>
+{{end}}
 {{template "base/footer" .}}
diff --git a/templates/user/profile.tmpl b/templates/user/profile.tmpl
index 7c37de2bc9..396d9ebcd4 100644
--- a/templates/user/profile.tmpl
+++ b/templates/user/profile.tmpl
@@ -3,125 +3,10 @@
 	<div class="ui container">
 		<div class="ui stackable grid">
 			<div class="ui four wide column">
-				<div class="ui card">
-					<div id="profile-avatar" class="content gt-df">
-					{{if eq .SignedUserID .ContextUser.ID}}
-						<a class="image" href="{{AppSubUrl}}/user/settings" data-tooltip-content="{{.locale.Tr "user.change_avatar"}}">
-							{{/* the size doesn't take affect (and no need to take affect), image size(width) should be controlled by the parent container since this is not a flex layout*/}}
-							{{avatar $.Context .ContextUser 256}}
-						</a>
-					{{else}}
-						<span class="image">
-							{{avatar $.Context .ContextUser 256}}
-						</span>
-					{{end}}
-					</div>
-					<div class="content gt-word-break profile-avatar-name">
-						{{if .ContextUser.FullName}}<span class="header text center">{{.ContextUser.FullName}}</span>{{end}}
-						<span class="username text center">{{.ContextUser.Name}}</span>
-						{{if .EnableFeed}}
-							<a href="{{.ContextUser.HomeLink}}.rss"><i class="ui text grey gt-ml-3" data-tooltip-content="{{.locale.Tr "rss_feed"}}">{{svg "octicon-rss" 18}}</i></a>
-						{{end}}
-						<div class="gt-mt-3">
-							<a class="muted" href="{{.ContextUser.HomeLink}}?tab=followers">{{svg "octicon-person" 18 "gt-mr-2"}}{{.NumFollowers}} {{.locale.Tr "user.followers"}}</a> · <a class="muted" href="{{.ContextUser.HomeLink}}?tab=following">{{.NumFollowing}} {{.locale.Tr "user.following"}}</a>
-						</div>
-					</div>
-					<div class="extra content gt-word-break">
-						<ul>
-							{{if .ContextUser.Location}}
-								<li>{{svg "octicon-location"}} {{.ContextUser.Location}}</li>
-							{{end}}
-							{{if (eq .SignedUserID .ContextUser.ID)}}
-								<li>
-									{{svg "octicon-mail"}}
-									<a href="mailto:{{.ContextUser.Email}}" rel="nofollow">{{.ContextUser.Email}}</a>
-									<a href="{{AppSubUrl}}/user/settings#privacy-user-settings">
-										{{if .ShowUserEmail}}
-											<i class="ui right" data-tooltip-content="{{.locale.Tr "user.email_visibility.limited"}}">
-												{{svg "octicon-unlock"}}
-											</i>
-										{{else}}
-											<i class="ui right" data-tooltip-content="{{.locale.Tr "user.email_visibility.private"}}">
-												{{svg "octicon-lock"}}
-											</i>
-										{{end}}
-									</a>
-								</li>
-							{{else}}
-								{{if .ShowUserEmail}}
-									<li>
-										{{svg "octicon-mail"}}
-										<a href="mailto:{{.ContextUser.Email}}" rel="nofollow">{{.ContextUser.Email}}</a>
-									</li>
-								{{end}}
-							{{end}}
-							{{if .ContextUser.Website}}
-								<li>
-									{{svg "octicon-link"}}
-									<a target="_blank" rel="noopener noreferrer me" href="{{.ContextUser.Website}}">{{.ContextUser.Website}}</a>
-								</li>
-							{{end}}
-							{{if $.RenderedDescription}}
-								<li>
-									<div class="render-content markup">{{$.RenderedDescription|Str2html}}</div>
-								</li>
-							{{end}}
-							{{range .OpenIDs}}
-								{{if .Show}}
-									<li>
-										{{svg "fontawesome-openid"}}
-										<a target="_blank" rel="noopener noreferrer" href="{{.URI}}">{{.URI}}</a>
-									</li>
-								{{end}}
-							{{end}}
-							<li>{{svg "octicon-clock"}} {{.locale.Tr "user.joined_on" (DateTime "short" .ContextUser.CreatedUnix) | Safe}}</li>
-							{{if and .Orgs .HasOrgsVisible}}
-							<li>
-								<ul class="user-orgs">
-								{{range .Orgs}}
-									{{if (or .Visibility.IsPublic (and ($.SignedUser) (or .Visibility.IsLimited (and (.HasMemberWithUserID $.SignedUserID) .Visibility.IsPrivate) ($.IsAdmin))))}}
-									<li>
-										<a href="{{.HomeLink}}" data-tooltip-content="{{.Name}}">
-											{{avatar $.Context .}}
-										</a>
-									</li>
-									{{end}}
-								{{end}}
-								</ul>
-							</li>
-							{{end}}
-							{{if .Badges}}
-							<li>
-								<ul class="user-badges">
-								{{range .Badges}}
-									<li>
-										<img width="64" height="64" src="{{.ImageURL}}" alt="{{.Description}}" data-tooltip-content="{{.Description}}">
-									</li>
-								{{end}}
-								</ul>
-							</li>
-							{{end}}
-							{{if and .IsSigned (ne .SignedUserID .ContextUser.ID)}}
-							<li class="follow">
-								{{if $.IsFollowing}}
-									<form method="post" action="{{.Link}}?action=unfollow&redirect_to={{$.Link}}">
-										{{$.CsrfTokenHtml}}
-										<button type="submit" class="ui basic red button">{{svg "octicon-person"}} {{.locale.Tr "user.unfollow"}}</button>
-									</form>
-								{{else}}
-									<form method="post" action="{{.Link}}?action=follow&redirect_to={{$.Link}}">
-										{{$.CsrfTokenHtml}}
-										<button type="submit" class="ui basic green button">{{svg "octicon-person"}} {{.locale.Tr "user.follow"}}</button>
-									</form>
-								{{end}}
-							</li>
-							{{end}}
-						</ul>
-					</div>
-				</div>
+				{{template "shared/user/profile_big_avatar" .}}
 			</div>
 			<div class="ui twelve wide column">
-				<div class="gt-mb-4 gt-df">
+				<div class="gt-mb-4">
 					{{template "user/overview/header" .}}
 				</div>
 
@@ -145,12 +30,12 @@
 					{{template "repo/user_cards" .}}
 				{{else if eq .TabName "followers"}}
 					{{template "repo/user_cards" .}}
-				{{else if or (eq .TabName "repositories") (not .ProfileReadme)}}
+				{{else if eq .TabName "overview"}}
+					<div id="readme_profile" class="markup">{{.ProfileReadme | Str2html}}</div>
+				{{else}}
 					{{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>
diff --git a/web_src/css/helpers.css b/web_src/css/helpers.css
index 68d4a1b021..a24d35e979 100644
--- a/web_src/css/helpers.css
+++ b/web_src/css/helpers.css
@@ -11,6 +11,7 @@ Gitea's private styles use `g-` prefix.
 .gt-ab { align-items: baseline !important; }
 .gt-tc { text-align: center !important; }
 .gt-tl { text-align: left !important; }
+.gt-tr { text-align: right !important; } /* TODO: rename these to "gt-text-right", etc. there are only a few */
 .gt-jc { justify-content: center !important; }
 .gt-js { justify-content: flex-start !important; }
 .gt-je { justify-content: flex-end !important; }