Add settings
This commit is contained in:
parent
8cd8720a7f
commit
e3238c54ad
15 changed files with 190 additions and 25 deletions
|
|
@ -33,4 +33,8 @@ section {
|
|||
grid-template-columns: repeat(2, 1fr);
|
||||
grid-gap: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.field { margin-bottom: 1rem; max-width: 500px; }
|
||||
.field > label { margin-bottom: .25rem; }
|
||||
.field > * { display: block; width: 100%; box-sizing: border-box; }
|
||||
|
|
@ -8,10 +8,12 @@ import (
|
|||
)
|
||||
|
||||
type User struct {
|
||||
Name string
|
||||
Password string
|
||||
Hash []byte
|
||||
CreatedAt time.Time
|
||||
Name string
|
||||
Password string
|
||||
Hash []byte
|
||||
Homepage string
|
||||
About string
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
func (u User) Validate() error {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import (
|
|||
"strconv"
|
||||
)
|
||||
|
||||
const schemaVersion = 2
|
||||
const schemaVersion = 3
|
||||
|
||||
func Migrate(db *sql.DB) {
|
||||
var currentVersion int
|
||||
|
|
|
|||
|
|
@ -27,5 +27,9 @@ create table statuses
|
|||
`,
|
||||
"schema_version_2": `alter table users
|
||||
add column status_id int references statuses(id);
|
||||
`,
|
||||
"schema_version_3": `alter table users
|
||||
add column homepage varchar(500) not null DEFAULT '',
|
||||
add column about varchar(500) not null DEFAULT '';
|
||||
`,
|
||||
}
|
||||
|
|
|
|||
3
storage/sql/schema_version_3.sql
Normal file
3
storage/sql/schema_version_3.sql
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
alter table users
|
||||
add column homepage varchar(500) not null DEFAULT '',
|
||||
add column about varchar(500) not null DEFAULT '';
|
||||
|
|
@ -4,11 +4,10 @@ import (
|
|||
"status/model"
|
||||
)
|
||||
|
||||
const queryFindName = `SELECT name, hash, created_at FROM users WHERE name=lower($1);`
|
||||
const queryFindDomain = `SELECT name, hash, created_at FROM users WHERE domain=$1;`
|
||||
const queryFindName = `SELECT name, hash, created_at, homepage, about FROM users WHERE name=lower($1);`
|
||||
|
||||
func (s *Storage) queryUser(q string, params ...interface{}) (user model.User, err error) {
|
||||
err = s.db.QueryRow(q, params...).Scan(&user.Name, &user.Hash, &user.CreatedAt)
|
||||
err = s.db.QueryRow(q, params...).Scan(&user.Name, &user.Hash, &user.CreatedAt, &user.Homepage, &user.About)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -77,3 +76,12 @@ func (s *Storage) DeleteUser(username string) error {
|
|||
_, err = stmt.Exec(username)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Storage) UpdateSettings(username, homepage, about string) error {
|
||||
stmt, err := s.db.Prepare(`UPDATE users SET homepage = $1, about = $2 WHERE name = $3;`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = stmt.Exec(homepage, about, username)
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
17
web/handler/form/settings.go
Normal file
17
web/handler/form/settings.go
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package form
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type SettingsForm struct {
|
||||
Homepage string
|
||||
About string
|
||||
}
|
||||
|
||||
func NewSettingsForm(r *http.Request) *SettingsForm {
|
||||
return &SettingsForm{
|
||||
Homepage: r.FormValue("homepage"),
|
||||
About: r.FormValue("about"),
|
||||
}
|
||||
}
|
||||
|
|
@ -70,6 +70,9 @@ func New(cfg *config.Config, sess *session.Session, data *storage.Storage) (http
|
|||
router.HandleFunc("/register", h.handleRegister)
|
||||
router.HandleFunc("/logout", h.logout).Methods(http.MethodGet)
|
||||
|
||||
router.HandleFunc("/settings", h.showSettingsView).Methods(http.MethodGet)
|
||||
router.HandleFunc("/settings-update", h.updateSettings).Methods(http.MethodPost)
|
||||
|
||||
router.HandleFunc("/status-new", h.showNewStatusView).Methods(http.MethodGet)
|
||||
router.HandleFunc("/status-save", h.saveStatus).Methods(http.MethodPost)
|
||||
router.HandleFunc("/statuses/{id}/edit", h.showEditStatusView).Methods(http.MethodGet)
|
||||
|
|
|
|||
|
|
@ -86,7 +86,6 @@ Are you sure you you want to delete the following status?
|
|||
<div class="field">
|
||||
<textarea name="content" placeholder="What's new?" required style="width: 100%; box-sizing: border-box; height: 100px;" autofocus></textarea>
|
||||
</div>
|
||||
<br>
|
||||
<input type="submit" value="Submit">
|
||||
<h3>Update status with bookmarklet</h3>
|
||||
<p>Drag to your bookmarks to update from anywhere: <a href="javascript:void(open('https://status.cafe/status-new','status.cafe','resizable,scrollbars,width=360,height=200'))">Set status</a></p>
|
||||
|
|
@ -142,12 +141,44 @@ Are you sure you you want to delete the following status?
|
|||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
{{ end }}`,
|
||||
"user": `{{ define "content" }}
|
||||
"settings": `{{ define "content" }}
|
||||
<section>
|
||||
<h2>{{ .user }}</h2>
|
||||
{{ range .statuses }}
|
||||
{{ template "status" . }}
|
||||
<h1>Settings</h1>
|
||||
{{ if .flash }}
|
||||
<p>{{ .flash }}</p>
|
||||
{{ end }}
|
||||
<form action="/settings-update" method="post">
|
||||
<div class="field">
|
||||
<label for="homepage">homepage</label>
|
||||
<input type="text" name="homepage" id="homepage" value="{{ .Homepage }}" autocomplete="off"/>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="about">about</label>
|
||||
<textarea name="about" id="about">{{ .About }}</textarea>
|
||||
</div>
|
||||
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</section>
|
||||
{{ end }}`,
|
||||
"user": `{{ define "content" }}
|
||||
<div class="cols">
|
||||
<section>
|
||||
<h2>{{ .user }}</h2>
|
||||
<dl>
|
||||
<dt>Homepage</dt>
|
||||
<dd><a href="{{ .homepage }}" target="_blank">{{ .homepage }}</a></dd>
|
||||
<dt>About</dt>
|
||||
<dd>{{ .about }}</dd>
|
||||
</dl>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Statuses</h2>
|
||||
{{ range .statuses }}
|
||||
{{ template "status" . }}
|
||||
{{ end }}
|
||||
</section>
|
||||
</div>
|
||||
{{ end }}`,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
<div class="field">
|
||||
<textarea name="content" placeholder="What's new?" required style="width: 100%; box-sizing: border-box; height: 100px;" autofocus></textarea>
|
||||
</div>
|
||||
<br>
|
||||
<input type="submit" value="Submit">
|
||||
<h3>Update status with bookmarklet</h3>
|
||||
<p>Drag to your bookmarks to update from anywhere: <a href="javascript:void(open('https://status.cafe/status-new','status.cafe','resizable,scrollbars,width=360,height=200'))">Set status</a></p>
|
||||
|
|
|
|||
21
web/handler/html/settings.html
Normal file
21
web/handler/html/settings.html
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{{ define "content" }}
|
||||
<section>
|
||||
<h1>Settings</h1>
|
||||
{{ if .flash }}
|
||||
<p>{{ .flash }}</p>
|
||||
{{ end }}
|
||||
<form action="/settings-update" method="post">
|
||||
<div class="field">
|
||||
<label for="homepage">homepage</label>
|
||||
<input type="text" name="homepage" id="homepage" value="{{ .Homepage }}" autocomplete="off"/>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="about">about</label>
|
||||
<textarea name="about" id="about">{{ .About }}</textarea>
|
||||
</div>
|
||||
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</section>
|
||||
{{ end }}
|
||||
|
|
@ -1,8 +1,19 @@
|
|||
{{ define "content" }}
|
||||
<section>
|
||||
<h2>{{ .user }}</h2>
|
||||
{{ range .statuses }}
|
||||
{{ template "status" . }}
|
||||
{{ end }}
|
||||
</section>
|
||||
<div class="cols">
|
||||
<section>
|
||||
<h2>{{ .user }}</h2>
|
||||
<dl>
|
||||
<dt>Homepage</dt>
|
||||
<dd><a href="{{ .homepage }}" target="_blank">{{ .homepage }}</a></dd>
|
||||
<dt>About</dt>
|
||||
<dd>{{ .about }}</dd>
|
||||
</dl>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Statuses</h2>
|
||||
{{ range .statuses }}
|
||||
{{ template "status" . }}
|
||||
{{ end }}
|
||||
</section>
|
||||
</div>
|
||||
{{ end }}
|
||||
28
web/handler/settings-update.go
Normal file
28
web/handler/settings-update.go
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"status/web/handler/form"
|
||||
)
|
||||
|
||||
func (h *Handler) updateSettings(w http.ResponseWriter, r *http.Request) {
|
||||
user, err := h.getUser(r)
|
||||
if err != nil {
|
||||
unauthorized(w)
|
||||
return
|
||||
}
|
||||
f := form.NewSettingsForm(r)
|
||||
if err := h.storage.UpdateSettings(user, f.Homepage, f.About); err != nil {
|
||||
serverError(w, err)
|
||||
return
|
||||
}
|
||||
session, err := h.sess.Store.Get(r, "ichi")
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
session.AddFlash("Status edited!")
|
||||
err = session.Save(r, w)
|
||||
http.Redirect(w, r, fmt.Sprintf("/settings"), http.StatusFound)
|
||||
}
|
||||
31
web/handler/settings_show.go
Normal file
31
web/handler/settings_show.go
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package handler
|
||||
|
||||
import "net/http"
|
||||
|
||||
func (h *Handler) showSettingsView(w http.ResponseWriter, r *http.Request) {
|
||||
username, err := h.getUser(r)
|
||||
if err != nil {
|
||||
unauthorized(w)
|
||||
return
|
||||
}
|
||||
user, err := h.storage.UserByName(username)
|
||||
if err != nil {
|
||||
unauthorized(w)
|
||||
return
|
||||
}
|
||||
session, err := h.sess.Store.Get(r, "ichi")
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
flash := ""
|
||||
if flashes := session.Flashes(); len(flashes) > 0 {
|
||||
flash = flashes[0].(string)
|
||||
}
|
||||
session.Save(r, w)
|
||||
h.renderLayout(w, "settings", map[string]interface{}{
|
||||
"flash": flash,
|
||||
"About": user.About,
|
||||
"Homepage": user.Homepage,
|
||||
}, username)
|
||||
}
|
||||
|
|
@ -17,8 +17,9 @@ import (
|
|||
)
|
||||
|
||||
func (h *Handler) showUserView(w http.ResponseWriter, r *http.Request) {
|
||||
user := mux.Vars(r)["user"]
|
||||
if !h.storage.UserExists(user) {
|
||||
username := mux.Vars(r)["user"]
|
||||
user, err := h.storage.UserByName(username)
|
||||
if err != nil {
|
||||
notFound(w)
|
||||
return
|
||||
}
|
||||
|
|
@ -28,9 +29,11 @@ func (h *Handler) showUserView(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
h.renderLayout(w, "user", map[string]interface{}{
|
||||
"user": user,
|
||||
"user": username,
|
||||
"statuses": statuses,
|
||||
}, "")
|
||||
"homepage": user.Homepage,
|
||||
"about": user.About,
|
||||
}, username)
|
||||
}
|
||||
|
||||
type statusjson struct {
|
||||
Loading…
Reference in a new issue