commit 932c4886e05817a40275010313761fd4edf07e39
parent 6e340fd233f7b413e34d480c19f74c6c6b8967ea
Author: Peter Kosyh <p.kosyh@gmail.com>
Date: Mon, 7 Sep 2020 19:37:31 +0300
avatars ready
Diffstat:
7 files changed, 153 insertions(+), 9 deletions(-)
diff --git a/ii-node/lib/style.css b/ii-node/lib/style.css
@@ -149,7 +149,7 @@ body {
border: 1px solid #55aaaa;
}
-#edit textarea, #edit input {
+#edit textarea, #edit input, #profile textarea {
font-size: larger;
margin: 10px;
padding: 10px;
@@ -158,8 +158,10 @@ body {
width: 90%;
border: 1px solid #55aaaa;
}
-
-#edit textarea {
+#profile textarea {
+ width: auto;
+}
+#edit textarea, #profile textarea {
background-color: #ffffea;
min-height: 20em;
font-size: x-large;
@@ -244,6 +246,17 @@ body {
image-rendering: pixelated;
}
+.avatar {
+ width: 4em;
+ float: left;
+ height: auto;
+ margin: 0.5em;
+ image-rendering: auto;
+ image-rendering: crisp-edges;
+ image-rendering: pixelated;
+ border: solid 1px #555555;
+}
+
.comment {
color: #555555;
font-style: italic;
diff --git a/ii-node/tpl/profile.tpl b/ii-node/tpl/profile.tpl
@@ -7,5 +7,15 @@
<tr class="even"><td>Addr:</td><td>{{.Selected}}</td></tr>
<tr class="odd"><td class="links" colspan="2"><a href="/from/{{.User.Name}}">/from/{{.User.Name}}</a> :: <a href="/to/{{.User.Name}}">/to/{{.User.Name}}</a>
</td></tr>
+
+<tr><td class="even" colspan="2">
+<form method="post" enctype="application/x-www-form-urlencoded" action="/avatar/{{.User.Name}}">
+<textarea type="text" name="avatar" class="message" cols=60 row=16 placeholder="Add XPM avatar here">{{.Info}}</textarea>
+</td></tr>
+<tr><td class="odd center" colspan="2">
+<button class="form-button" type="submit" name="action" value="Submit">Submit</button>
+</td></tr>
+</form>
+
</table>
{{template "footer.tpl"}}
diff --git a/ii-node/tpl/query.tpl b/ii-node/tpl/query.tpl
@@ -4,6 +4,9 @@
<div id="topic">
{{ range .Msg }}
<div class="msg">
+{{ if has_avatar .From }}
+<img class="avatar" src="/avatar/{{.From}}">
+{{ end }}
<a class="msgid" href="/{{.MsgId}}#{{.MsgId}}">#</a><span class="subj"> <a href="/{{. | repto}}#{{. | repto}}">{{with .Subj}}{{.}}{{else}}No subject{{end}}</a></span><br>
<span class="echo"><a href="/{{.Echo}}">{{.Echo}}</a></span><br>
<span class="info">{{.From}}({{.Addr}}) — {{.To}}<br>{{.Date | fdate}}</span><br>
diff --git a/ii-node/tpl/topic.tpl b/ii-node/tpl/topic.tpl
@@ -8,6 +8,9 @@
{{else}}
<div class="msg">
{{end}}
+{{ if has_avatar .From }}
+<img class="avatar" src="/avatar/{{.From}}">
+{{ end }}
<a class="msgid" href="/{{.MsgId}}#{{.MsgId}}">#</a><span class="subj"> <a href="/{{. | repto}}#{{. | repto}}">{{with .Subj}}{{.}}{{else}}No subject{{end}}</a></span><br>
<span class="info"><a href="/from/{{.From}}">{{.From}}</a>({{.Addr}}) — {{.To}}<br>{{.Date | fdate}}</span><br>
<div class="text">
diff --git a/ii-node/web.go b/ii-node/web.go
@@ -33,6 +33,7 @@ type WebContext struct {
Echolist *ii.EDB
Selected string
Ref string
+ Info string
www *WWW
}
@@ -100,6 +101,12 @@ func www_profile(ctx *WebContext, w http.ResponseWriter, r *http.Request) error
return errors.New("Access denied")
}
ctx.Selected = fmt.Sprintf("%s,%d", ctx.www.db.Name, ctx.User.Id)
+ ava, _ := ctx.User.Tags.Get("avatar")
+ if ava != "" {
+ if data, err := base64.URLEncoding.DecodeString(ava); err == nil {
+ ctx.Info = string(data)
+ }
+ }
err := ctx.www.tpl.ExecuteTemplate(w, "profile.tpl", ctx)
return err
}
@@ -123,16 +130,65 @@ func www_index(ctx *WebContext, w http.ResponseWriter, r *http.Request) error {
return err
}
+func parse_ava(txt string) *image.RGBA {
+ txt = msg_clean(txt)
+ lines := strings.Split(txt, "\n")
+ img, _ := ParseXpm(lines)
+ return img
+}
+
func www_avatar(ctx *WebContext, w http.ResponseWriter, r *http.Request, user string) error {
- ava, _ := ctx.User.Tags.Get("avatar")
+ if r.Method == "POST" { /* upload avatar */
+ if ctx.User.Name == "" || ctx.User.Name != user {
+ ii.Error.Printf("Access denied")
+ return errors.New("Access denied")
+ }
+ if err := r.ParseForm(); err != nil {
+ ii.Error.Printf("Error in POST request: %s", err)
+ return err
+ }
+ ava := r.FormValue("avatar")
+ if len(ava) > 2048 {
+ ii.Error.Printf("Avatar is too big.")
+ return errors.New("Avatar is too big")
+ }
+ img := parse_ava(ava)
+ if img == nil {
+ ii.Error.Printf("Wrong xpm format for avatar: " + user)
+ return errors.New("Wrong xpm format")
+ }
+ b64 := base64.URLEncoding.EncodeToString([]byte(ava))
+ ii.Trace.Printf("New avatar for %s: %s", ctx.User.Name, b64)
+ ctx.User.Tags.Add("avatar/" + b64)
+ if err := ctx.www.udb.Edit(ctx.User); err != nil {
+ ii.Error.Printf("Error saving avatar: " + user)
+ return errors.New("Error saving avatar")
+ }
+ http.Redirect(w, r, "/profile", http.StatusSeeOther)
+ return nil
+ }
+ // var id int32
+ // if !strings.HasPrefix(user, ctx.www.db.Name) {
+ // return nil
+ // }
+ // user = strings.TrimPrefix(user, ctx.www.db.Name)
+ // user = strings.TrimPrefix(user, ",")
+ // if _, err := fmt.Sscanf(user, "%d", &id); err != nil {
+ // return nil
+ // }
+ // u := ctx.www.udb.UserInfoId(id)
+ u := ctx.www.udb.UserInfoName(user)
+ if u == nil {
+ return nil
+ }
+ ava, _ := u.Tags.Get("avatar")
if ava == "" {
return nil
}
- if data, err := base64.StdEncoding.DecodeString(ava); err != nil {
- txt := msg_clean(string(data))
- lines := strings.Split(txt, "\n")
- img, _ := ParseXpm(lines)
+ if data, err := base64.URLEncoding.DecodeString(ava); err == nil {
+ img := parse_ava(string(data))
if img == nil {
+ ii.Error.Printf("Wrong xpm in avatar: %s\n", u.Name)
return nil
}
b := new(bytes.Buffer)
@@ -144,6 +200,9 @@ func www_avatar(ctx *WebContext, w http.ResponseWriter, r *http.Request, user st
}
return nil
}
+ ii.Error.Printf("Can't encode avatar in png: %s\n", u.Name)
+ } else {
+ ii.Error.Printf("Can't decode avatar: %s\n", u.Name)
}
return nil
}
@@ -650,6 +709,14 @@ func WebInit(www *WWW) {
"unescape": func(s string) template.HTML {
return template.HTML(s)
},
+ "has_avatar": func(user string) bool {
+ ui := www.udb.UserInfoName(user)
+ if ui != nil {
+ _, ok := ui.Tags.Get("avatar")
+ return ok
+ }
+ return false
+ },
}
www.tpl = template.Must(template.New("main").Funcs(funcMap).ParseGlob("tpl/*.tpl"))
}
@@ -701,7 +768,7 @@ func _handleWWW(ctx *WebContext, w http.ResponseWriter, r *http.Request) error {
} else if path == "register" {
ctx.BasePath = "register"
return www_register(ctx, w, r)
- } else if path == "avatar" {
+ } else if args[0] == "avatar" {
ctx.BasePath = "avatar"
if len(args) < 2 {
return errors.New("Wrong request")
diff --git a/ii/db.go b/ii/db.go
@@ -655,6 +655,7 @@ type User struct {
type UDB struct {
Path string
Names map[string]User
+ ById map[int32]string
Secrets map[string]string
List []string
Sync sync.RWMutex
@@ -727,6 +728,27 @@ func (db *UDB) UserInfo(Secret string) *User {
Error.Printf("No user for secret: %s", Secret)
return nil
}
+func (db *UDB) UserInfoId(id int32) *User {
+ db.Sync.RLock()
+ defer db.Sync.RUnlock()
+ name, ok := db.ById[id]
+ if ok {
+ v := db.Names[name]
+ return &v
+ }
+ Error.Printf("No user for Id: %d", id)
+ return nil
+}
+func (db *UDB) UserInfoName(name string) *User {
+ db.Sync.RLock()
+ defer db.Sync.RUnlock()
+ v, ok := db.Names[name]
+ if ok {
+ return &v
+ }
+ Error.Printf("No user: %s", name)
+ return nil
+}
func (db *UDB) Id(Secret string) int32 {
db.Sync.RLock()
@@ -786,6 +808,27 @@ func OpenUsers(path string) *UDB {
db.Path = path
return &db
}
+func (db *UDB) Edit(u *User) error {
+ db.Sync.Lock()
+ defer db.Sync.Unlock()
+ if _, ok := db.Names[u.Name]; !ok {
+ return errors.New("No such user")
+ }
+ db.Names[u.Name] = *u // new version
+ os.Remove(db.Path + ".tmp")
+ for _, Name := range db.List {
+ ui := db.Names[Name]
+ if err := append_file(db.Path + ".tmp", fmt.Sprintf("%d:%s:%s:%s:%s",
+ ui.Id, Name, ui.Mail, ui.Secret, ui.Tags.String())); err != nil {
+ return err
+ }
+ }
+ if err := os.Rename(db.Path + ".tmp", db.Path); err != nil {
+ return err
+ }
+ db.FileSize = 0 // force to reload
+ return nil
+}
func (db *UDB) LoadUsers() error {
db.Sync.Lock()
@@ -811,6 +854,7 @@ func (db *UDB) LoadUsers() error {
}
db.Names = make(map[string]User)
db.Secrets = make(map[string]string)
+ db.ById = make(map[int32]string)
err = file_lines(db.Path, func(line string) bool {
a := strings.Split(line, ":")
if len(a) < 4 {
@@ -828,6 +872,7 @@ func (db *UDB) LoadUsers() error {
u.Mail = a[2]
u.Secret = a[3]
u.Tags = NewTags(a[4])
+ db.ById[u.Id] = u.Name
db.Names[u.Name] = u
db.Secrets[u.Secret] = u.Name
db.List = append(db.List, u.Name)
diff --git a/ii/msg.go b/ii/msg.go
@@ -193,6 +193,9 @@ func (t *Tags) Add(str string) error {
if len(tags)%2 != 0 {
return errors.New("Wrong tags")
}
+ if t.Hash == nil {
+ t.Hash = make(map[string]string)
+ }
for i := 0; i < len(tags); i += 2 {
t.Hash[tags[i]] = tags[i+1]
t.List = append(t.List, tags[i])