From f49f4ca10dbe0daeb80f0cb0162b0aab9df65b5c Mon Sep 17 00:00:00 2001
From: Thomas Miceli <tho.miceli@gmail.com>
Date: Mon, 20 Mar 2023 13:30:25 +0100
Subject: [PATCH] Add avatars to HTML

---
 internal/git/commands.go        | 27 +++++++++++----------------
 internal/git/output_parser.go   | 16 ++++++++++------
 internal/models/gist.go         |  2 +-
 internal/web/config.go          |  2 --
 internal/web/gist.go            | 27 ++++++++++++++-------------
 internal/web/run.go             |  8 ++++++--
 templates/base/base_header.html |  4 ++--
 templates/pages/all.html        | 22 +++++++++++++++++-----
 templates/pages/forks.html      |  3 +++
 templates/pages/likes.html      |  5 +++--
 templates/pages/revisions.html  |  9 ++++++---
 11 files changed, 73 insertions(+), 52 deletions(-)

diff --git a/internal/git/commands.go b/internal/git/commands.go
index 4532ca6..bd4e1e0 100644
--- a/internal/git/commands.go
+++ b/internal/git/commands.go
@@ -1,6 +1,7 @@
 package git
 
 import (
+	"fmt"
 	"io"
 	"opengist/internal/config"
 	"os"
@@ -114,7 +115,7 @@ func GetLog(user string, gist string, skip string) ([]*Commit, error) {
 		"-p",
 		"--skip",
 		skip,
-		"--format=format:c %H%na %aN%nt %at",
+		"--format=format:c %H%na %aN%nm %ae%nt %at",
 		"--shortstat",
 		"HEAD",
 	)
@@ -146,12 +147,6 @@ func CloneTmp(user string, gist string, gistTmpId string) error {
 		return err
 	}
 
-	cmd = exec.Command("git", "config", "user.name", user)
-	cmd.Dir = tmpRepositoryPath
-	if err = cmd.Run(); err != nil {
-		return err
-	}
-
 	// remove every file (and not the .git directory!)
 	cmd = exec.Command("find", ".", "-maxdepth", "1", "-type", "f", "-delete")
 	cmd.Dir = tmpRepositoryPath
@@ -167,13 +162,6 @@ func ForkClone(userSrc string, gistSrc string, userDst string, gistDst string) e
 		return err
 	}
 
-	cmd = exec.Command("git", "config", "user.name", userDst)
-	cmd.Dir = repositoryPathDst
-	err := cmd.Run()
-	if err != nil {
-		return err
-	}
-
 	return copyFiles(repositoryPathDst)
 }
 
@@ -200,8 +188,15 @@ func AddAll(gistTmpId string) error {
 	return cmd.Run()
 }
 
-func CommitRepository(gistTmpId string) error {
-	cmd := exec.Command("git", "commit", "--allow-empty", "-m", `"Opengist commit"`)
+func CommitRepository(gistTmpId string, authorName string, authorEmail string) error {
+	cmd := exec.Command("git",
+		"commit",
+		"--allow-empty",
+		"-m",
+		"Opengist commit",
+		"--author",
+		fmt.Sprintf("%s <%s>", authorName, authorEmail),
+	)
 	tmpPath := TmpRepositoryPath(gistTmpId)
 	cmd.Dir = tmpPath
 
diff --git a/internal/git/output_parser.go b/internal/git/output_parser.go
index 39e467c..4075838 100644
--- a/internal/git/output_parser.go
+++ b/internal/git/output_parser.go
@@ -26,11 +26,12 @@ type CsvFile struct {
 }
 
 type Commit struct {
-	Hash      string
-	Author    string
-	Timestamp string
-	Changed   string
-	Files     []File
+	Hash        string
+	AuthorName  string
+	AuthorEmail string
+	Timestamp   string
+	Changed     string
+	Files       []File
 }
 
 func truncateCommandOutput(out io.Reader, maxBytes int64) (string, bool, error) {
@@ -75,7 +76,10 @@ func parseLog(out io.Reader) []*Commit {
 		currentCommit = &Commit{Hash: string(scanner.Bytes()[2:]), Files: []File{}}
 
 		scanner.Scan()
-		currentCommit.Author = string(scanner.Bytes()[2:])
+		currentCommit.AuthorName = string(scanner.Bytes()[2:])
+
+		scanner.Scan()
+		currentCommit.AuthorEmail = string(scanner.Bytes()[2:])
 
 		scanner.Scan()
 		currentCommit.Timestamp = string(scanner.Bytes()[2:])
diff --git a/internal/models/gist.go b/internal/models/gist.go
index 98cb27d..676a860 100644
--- a/internal/models/gist.go
+++ b/internal/models/gist.go
@@ -251,7 +251,7 @@ func (gist *Gist) AddAndCommitFiles(files *[]FileDTO) error {
 		return err
 	}
 
-	if err := git.CommitRepository(gist.Uuid); err != nil {
+	if err := git.CommitRepository(gist.Uuid, gist.User.Username, gist.User.Email); err != nil {
 		return err
 	}
 
diff --git a/internal/web/config.go b/internal/web/config.go
index 042d796..8f0879c 100644
--- a/internal/web/config.go
+++ b/internal/web/config.go
@@ -32,8 +32,6 @@ func emailProcess(ctx echo.Context) error {
 	email := ctx.FormValue("email")
 	var hash string
 
-	fmt.Println()
-
 	if email == "" {
 		// generate random md5 string
 		hash = fmt.Sprintf("%x", md5.Sum([]byte(time.Now().String())))
diff --git a/internal/web/gist.go b/internal/web/gist.go
index 58181a1..24364f6 100644
--- a/internal/web/gist.go
+++ b/internal/web/gist.go
@@ -71,7 +71,8 @@ func gistInit(next echo.HandlerFunc) echo.HandlerFunc {
 
 func allGists(ctx echo.Context) error {
 	var err error
-	fromUser := ctx.Param("user")
+	fromUserStr := ctx.Param("user")
+
 	userLogged := getUserLogged(ctx)
 
 	pageInt := getPage(ctx)
@@ -99,30 +100,30 @@ func allGists(ctx echo.Context) error {
 	} else {
 		currentUserId = 0
 	}
-	if fromUser == "" {
+	if fromUserStr == "" {
 		setData(ctx, "htmlTitle", "All gists")
-		fromUser = "all"
+		fromUserStr = "all"
 		gists, err = models.GetAllGistsForCurrentUser(currentUserId, pageInt-1, sort, order)
 	} else {
-		setData(ctx, "htmlTitle", "All gists from "+fromUser)
-		setData(ctx, "fromUser", fromUser)
+		setData(ctx, "htmlTitle", "All gists from "+fromUserStr)
 
-		var exists bool
-		if exists, err = models.UserExists(fromUser); err != nil {
+		fromUser, err := models.GetUserByUsername(fromUserStr)
+		if err != nil {
+			if errors.Is(err, gorm.ErrRecordNotFound) {
+				return notFound("User not found")
+			}
 			return errorRes(500, "Error fetching user", err)
 		}
+		setData(ctx, "fromUser", fromUser)
 
-		if !exists {
-			return notFound("User not found")
-		}
-
-		gists, err = models.GetAllGistsFromUser(fromUser, currentUserId, pageInt-1, sort, order)
+		gists, err = models.GetAllGistsFromUser(fromUserStr, currentUserId, pageInt-1, sort, order)
 	}
+
 	if err != nil {
 		return errorRes(500, "Error fetching gists", err)
 	}
 
-	if err = paginate(ctx, gists, pageInt, 10, "gists", fromUser, 2, "&sort="+sort+"&order="+order); err != nil {
+	if err = paginate(ctx, gists, pageInt, 10, "gists", fromUserStr, 2, "&sort="+sort+"&order="+order); err != nil {
 		return errorRes(404, "Page not found", nil)
 	}
 
diff --git a/internal/web/run.go b/internal/web/run.go
index 96e6c5e..edc4c14 100644
--- a/internal/web/run.go
+++ b/internal/web/run.go
@@ -2,6 +2,7 @@ package web
 
 import (
 	"context"
+	"crypto/md5"
 	"fmt"
 	"github.com/gorilla/sessions"
 	"github.com/labstack/echo/v4"
@@ -100,8 +101,11 @@ func Start() {
 				"slug": func(s string) string {
 					return strings.Trim(re.ReplaceAllString(strings.ToLower(s), "-"), "-")
 				},
-				"avatarUrl": func(user *models.User) string {
-					return "https://www.gravatar.com/avatar/" + user.MD5Hash + "?d=identicon&s=200"
+				"avatarUrl": func(userHash string) string {
+					return "https://www.gravatar.com/avatar/" + userHash + "?d=identicon&s=200"
+				},
+				"emailToMD5": func(email string) string {
+					return fmt.Sprintf("%x", md5.Sum([]byte(strings.ToLower(strings.TrimSpace(email)))))
 				},
 			}).ParseGlob("templates/*/*.html")),
 	}
diff --git a/templates/base/base_header.html b/templates/base/base_header.html
index f937a0e..855bdde 100644
--- a/templates/base/base_header.html
+++ b/templates/base/base_header.html
@@ -58,7 +58,7 @@
                         {{ if .userLogged.IsAdmin }}
                             <a href="/admin" class="hidden sm:block text-slate-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium" aria-current="page">Admin</a>
                         {{ end }}
-                        <a href="/settings" class="hidden sm:block text-slate-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium" aria-current="page">Config</a>
+                        <a href="/settings" class="hidden sm:block text-slate-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium" aria-current="page">Settings</a>
 
                         <a href="/logout" id="logged-button" class="inline-flex text-slate-300 hover:bg-rose-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium">
                                 <p class="text-slate-300 mr-1 username">{{ .userLogged.Username }}</p>
@@ -90,7 +90,7 @@
                     <a href="/all" class="bg-gray-900 text-white block px-3 py-2 rounded-md text-base font-medium" aria-current="page">All</a>
                     {{ if .userLogged }}
                         <a href="/{{ .userLogged.Username }}" class="text-slate-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium">My gists</a>
-                        <a href="/settings" class="text-slate-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium">Config</a>
+                        <a href="/settings" class="text-slate-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium">Settings</a>
 
                     {{ if .userLogged.IsAdmin }}
                             <a href="/admin" class="text-slate-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium">Admin</a>
diff --git a/templates/pages/all.html b/templates/pages/all.html
index 21b283b..1dd2be2 100644
--- a/templates/pages/all.html
+++ b/templates/pages/all.html
@@ -2,7 +2,19 @@
 <div class="py-10">
     <header class="pb-4 flex ">
         <div class="flex-auto">
-            <h1 class="text-2xl font-bold leading-tight">All gists {{if .fromUser}} from {{.fromUser}} {{end}}</h1>
+            {{if .fromUser}}
+            <div class="flex items-center">
+                <div class="flex-shrink-0">
+                    <img class="h-12 w-12 rounded-md mr-2 border border-gray-700" src="{{ avatarUrl .fromUser.MD5Hash }}" alt="">
+                </div>
+                <div>
+                    <h1 class="text-2xl font-bold leading-tight">{{.fromUser.Username}}</h1>
+                    <p class="text-sm text-slate-500">Joined <span class="moment-timestamp">{{.fromUser.CreatedAt}}</span></p>
+                </div>
+            </div>
+            {{ else }}
+            <h1 class="text-2xl font-bold leading-tight">All gists</h1>
+            {{ end }}
         </div>
         <div class="float-right">
             <div class="relative inline-block text-left">
@@ -16,21 +28,21 @@
                 </div>
                 <div id="sort-gists-dropdown" class="hidden absolute right-0 z-10 mt-2 w-56 origin-top-right divide-y divide-gray-700 rounded-md rounded border border-gray-600 bg-gray-800 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none" role="menu" aria-orientation="vertical" aria-labelledby="menu-button" tabindex="-1">
                     <div class="" role="none">
-                        <a href="/{{if .fromUser}}{{.fromUser}}{{else}}all{{end}}?sort=created&order=desc" class="text-slate-300 group flex items-center px-3 py-2 text-xs hover:bg-gray-700 hover:text-white hover:bg-primary-500 hover:rounded-t-md" role="menuitem">
+                        <a href="/{{if .fromUser}}{{.fromUser.Username}}{{else}}all{{end}}?sort=created&order=desc" class="text-slate-300 group flex items-center px-3 py-2 text-xs hover:bg-gray-700 hover:text-white hover:bg-primary-500 hover:rounded-t-md" role="menuitem">
                             Recently created
                         </a>
                     </div>
                     <div class="" role="none">
-                        <a href="/{{if .fromUser}}{{.fromUser}}{{else}}all{{end}}?sort=created&order=asc" class="text-slate-300 group flex items-center px-3 py-2 text-xs hover:bg-gray-700 hover:text-white hover:bg-primary-500" role="menuitem">
+                        <a href="/{{if .fromUser}}{{.fromUser.Username}}{{else}}all{{end}}?sort=created&order=asc" class="text-slate-300 group flex items-center px-3 py-2 text-xs hover:bg-gray-700 hover:text-white hover:bg-primary-500" role="menuitem">
                             Least recently created
                         </a>
                     </div><div class="" role="none">
-                        <a href="/{{if .fromUser}}{{.fromUser}}{{else}}all{{end}}?sort=updated&order=desc" class="text-slate-300 group flex items-center px-3 py-2 text-xs hover:bg-gray-700 hover:text-white hover:bg-primary-500" role="menuitem">
+                        <a href="/{{if .fromUser}}{{.fromUser.Username}}{{else}}all{{end}}?sort=updated&order=desc" class="text-slate-300 group flex items-center px-3 py-2 text-xs hover:bg-gray-700 hover:text-white hover:bg-primary-500" role="menuitem">
                             Recently updated
                         </a>
                     </div>
                     <div class="" role="none">
-                        <a href="/{{if .fromUser}}{{.fromUser}}{{else}}all{{end}}?sort=updated&order=asc" class="text-slate-300 group flex items-center px-3 py-2 text-xs hover:bg-gray-700 hover:text-white hover:bg-primary-500 hover:rounded-b-md" role="menuitem">
+                        <a href="/{{if .fromUser}}{{.fromUser.Username}}{{else}}all{{end}}?sort=updated&order=asc" class="text-slate-300 group flex items-center px-3 py-2 text-xs hover:bg-gray-700 hover:text-white hover:bg-primary-500 hover:rounded-b-md" role="menuitem">
                             Least recently updated
                         </a>
                     </div>
diff --git a/templates/pages/forks.html b/templates/pages/forks.html
index 0367632..417736e 100644
--- a/templates/pages/forks.html
+++ b/templates/pages/forks.html
@@ -7,6 +7,9 @@
             <ul role="list" class="divide-y divide-gray-700">
                 {{ range $gist := .forks }}
                 <li class="flex py-4">
+                    <a href="/{{ $gist.User.Username }}">
+                        <img class="h-12 w-12 rounded-md mr-2 border border-gray-700" src="{{ avatarUrl $gist.User.MD5Hash }}" alt="">
+                    </a>
                     <div>
                         <a href="/{{ $gist.User.Username }}" class="text-sm font-medium text-slate-300">{{ $gist.User.Username }}</a>
                         <p class="text-sm text-slate-500">Forked <span class="moment-timestamp">{{ $gist.CreatedAt }}</span></p>
diff --git a/templates/pages/likes.html b/templates/pages/likes.html
index 8fd674e..8269f14 100644
--- a/templates/pages/likes.html
+++ b/templates/pages/likes.html
@@ -5,10 +5,11 @@
         <div class="grid grid-cols-1 gap-4 sm:grid-cols-5">
             {{ range $user := .likers }}
                 <div class="relative flex items-center space-x-3 rounded-lg border border-gray-600 bg-gray-800 px-6 py-5 shadow-sm focus-within:ring-1 focus-within:border-primary-500 focus-within:ring-primary-500 hover:border-gray-400">
-                    <div class="min-w-0 flex-1">
+                    <div class="min-w-0 flex">
+                        <img class="h-12 w-12 rounded-md mr-2 border border-gray-700" src="{{ avatarUrl $user.MD5Hash }}" alt="">
                         <a href="/{{ $user.Username }}" class="focus:outline-none">
                             <span class="absolute inset-0" aria-hidden="true"></span>
-                            <p class="text-sm font-medium text-slate-300">{{ $user.Username }}</p>
+                            <p class="text-sm font-medium text-slate-300 align-middle">{{ $user.Username }}</p>
                         </a>
                     </div>
                 </div>
diff --git a/templates/pages/revisions.html b/templates/pages/revisions.html
index 40fd01d..1f01141 100644
--- a/templates/pages/revisions.html
+++ b/templates/pages/revisions.html
@@ -6,9 +6,12 @@
         {{ range $commit := .commits }}
         <div class="pb-8">
             <div class="flex">
-            <h3 class="text-sm py-2 flex-auto"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 mr-1 inline" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
-                <path stroke-linecap="round" stroke-linejoin="round" d="M13 5l7 7-7 7M5 5l7 7-7 7" />
-            </svg><span class="font-bold">{{ $commit.Author }}</span> revised this gist <span class="moment-timestamp font-bold">{{ $commit.Timestamp }}</span>. <a href="/{{ $.gist.User.Username }}/{{ $.gist.Uuid }}/rev/{{ $commit.Hash }}">Go to revision</a></h3>
+            <h3 class="text-sm py-2 flex-auto">
+                <svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 mr-1 inline" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
+                    <path stroke-linecap="round" stroke-linejoin="round" d="M13 5l7 7-7 7M5 5l7 7-7 7" />
+                </svg>
+                <img class="h-5 w-5 rounded-full inline" src="{{ avatarUrl (emailToMD5 $commit.AuthorEmail) }}" alt="" />
+                <span class="font-bold">{{ $commit.AuthorName }}</span> revised this gist <span class="moment-timestamp font-bold">{{ $commit.Timestamp }}</span>. <a href="/{{ $.gist.User.Username }}/{{ $.gist.Uuid }}/rev/{{ $commit.Hash }}">Go to revision</a></h3>
                 {{ if ne $commit.Changed "" }}
                     <p class="text-sm float-right py-2">
                     <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 inline-flex">