github.com/Azareal/Gosora@v0.0.0-20210729070923-553e66b59003/routes/profile.go (about) 1 package routes 2 3 import ( 4 "database/sql" 5 "math" 6 "net/http" 7 "time" 8 9 c "github.com/Azareal/Gosora/common" 10 "github.com/Azareal/Gosora/common/phrases" 11 qgen "github.com/Azareal/Gosora/query_gen" 12 ) 13 14 type ProfileStmts struct { 15 getReplies *sql.Stmt 16 } 17 18 var profileStmts ProfileStmts 19 20 // TODO: Move these DbInits into some sort of abstraction 21 func init() { 22 c.DbInits.Add(func(acc *qgen.Accumulator) error { 23 profileStmts = ProfileStmts{ 24 getReplies: acc.SimpleLeftJoin("users_replies", "users", "users_replies.rid, users_replies.content, users_replies.createdBy, users_replies.createdAt, users_replies.lastEdit, users_replies.lastEditBy, users.avatar, users.name, users.group", "users_replies.createdBy=users.uid", "users_replies.uid=?", "", ""), 25 } 26 return acc.FirstError() 27 }) 28 } 29 30 // TODO: Remove the View part of the name? 31 func ViewProfile(w http.ResponseWriter, r *http.Request, user *c.User, h *c.Header) c.RouteError { 32 var reCreatedAt time.Time 33 var reContent, reCreatedByName, reAvatar string 34 var rid, reCreatedBy, reLastEdit, reLastEditBy, reGroup int 35 var reList []*c.ReplyUser 36 37 // TODO: Do a 301 if it's the wrong username? Do a canonical too? 38 _, pid, err := ParseSEOURL(r.URL.Path[len("/user/"):]) 39 if err != nil { 40 return c.SimpleError(phrases.GetErrorPhrase("url_id_must_be_integer"), w, r, h) 41 } 42 if pid == 0 { 43 return c.NotFound(w, r, h) 44 } 45 46 var puser *c.User 47 if pid == user.ID { 48 user.IsMod = true 49 puser = user 50 } else { 51 // Fetch the user data 52 // TODO: Add a shared function for checking for ErrNoRows and internal erroring if it's not that case? 53 puser, err = c.Users.Get(pid) 54 if err == sql.ErrNoRows { 55 return c.NotFound(w, r, h) 56 } else if err != nil { 57 return c.InternalError(err, w, r) 58 } 59 } 60 h.Title = phrases.GetTitlePhrasef("profile", puser.Name) 61 h.Path = c.BuildProfileURL(c.NameToSlug(puser.Name), puser.ID) 62 // TODO: Preload this? 63 h.AddSheet(h.Theme.Name + "/profile.css") 64 if user.Loggedin { 65 h.AddScriptAsync("profile_member.js") 66 } 67 68 // Get the replies.. 69 rows, err := profileStmts.getReplies.Query(puser.ID) 70 if err != nil { 71 return c.InternalError(err, w, r) 72 } 73 defer rows.Close() 74 75 for rows.Next() { 76 err := rows.Scan(&rid, &reContent, &reCreatedBy, &reCreatedAt, &reLastEdit, &reLastEditBy, &reAvatar, &reCreatedByName, &reGroup) 77 if err != nil { 78 return c.InternalError(err, w, r) 79 } 80 81 reLiked := false 82 reLikeCount := 0 83 ru := &c.ReplyUser{Reply: c.Reply{rid, puser.ID, reContent, reCreatedBy /*, reGroup*/, reCreatedAt, reLastEdit, reLastEditBy, 0, "", reLiked, reLikeCount, 0, ""}, ContentHtml: c.ParseMessage(reContent, 0, "", user.ParseSettings, user), CreatedByName: reCreatedByName, Avatar: reAvatar, Group: reGroup, Level: 0} 84 _, err = ru.Init(user) 85 if err != nil { 86 return c.InternalError(err, w, r) 87 } 88 if puser.ID == ru.CreatedBy { 89 ru.Tag = phrases.GetTmplPhrase("profile.owner_tag") 90 } 91 92 // TODO: Add a hook here 93 reList = append(reList, ru) 94 } 95 if err := rows.Err(); err != nil { 96 return c.InternalError(err, w, r) 97 } 98 99 // Normalise the score so that the user sees their relative progress to the next level rather than showing them their total score 100 prevScore := c.GetLevelScore(puser.Level) 101 score := puser.Score 102 //score = 23 103 currentScore := score - prevScore 104 nextScore := c.GetLevelScore(puser.Level+1) - prevScore 105 perc := int(math.Floor((float64(currentScore) / float64(nextScore)) * 100)) 106 var blocked, blockedInv bool 107 if user.Loggedin { 108 blocked, err = c.UserBlocks.IsBlockedBy(user.ID, puser.ID) 109 if err != nil { 110 return c.InternalError(err, w, r) 111 } 112 blockedInv, err = c.UserBlocks.IsBlockedBy(puser.ID, user.ID) 113 if err != nil { 114 return c.InternalError(err, w, r) 115 } 116 } 117 118 canMessage := (!blockedInv && user.Perms.UseConvos) || (!blockedInv && puser.IsSuperMod && user.Perms.UseConvosOnlyWithMod) || user.IsSuperMod 119 canComment := !blockedInv && user.Perms.CreateProfileReply 120 showComments := c.PrivacyCommentsShow(puser, user) 121 if !showComments { 122 canComment = false 123 } 124 if !c.PrivacyAllowMessage(puser, user) { 125 canMessage = false 126 } 127 128 ppage := c.ProfilePage{h, reList, *puser, currentScore, nextScore, perc, blocked, canMessage, canComment, showComments} 129 return renderTemplate("profile", w, r, h, ppage) 130 }