openidec

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 932c4886e05817a40275010313761fd4edf07e39
parent 6e340fd233f7b413e34d480c19f74c6c6b8967ea
Author: Peter Kosyh <p.kosyh@gmail.com>
Date:   Mon,  7 Sep 2020 19:37:31 +0300

avatars ready

Diffstat:
Mii-node/lib/style.css | 19++++++++++++++++---
Mii-node/tpl/profile.tpl | 10++++++++++
Mii-node/tpl/query.tpl | 3+++
Mii-node/tpl/topic.tpl | 3+++
Mii-node/web.go | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Mii/db.go | 45+++++++++++++++++++++++++++++++++++++++++++++
Mii/msg.go | 3+++
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}}) &mdash; {{.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}}) &mdash; {{.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])