Skip to content
Snippets Groups Projects
Commit b2db89fc authored by gbe's avatar gbe
Browse files

Add comments

parent 3d8d3f61
No related branches found
No related tags found
No related merge requests found
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"log" "log"
"net/http" "net/http"
"strconv" "strconv"
"strings"
) )
var detailsTemplate = template.Must(template.ParseFS(templateFS, "templates/base.tpl", "templates/details.tpl")) var detailsTemplate = template.Must(template.ParseFS(templateFS, "templates/base.tpl", "templates/details.tpl"))
...@@ -102,6 +103,19 @@ func (h Handler) detailsPost(w http.ResponseWriter, r *http.Request) { ...@@ -102,6 +103,19 @@ func (h Handler) detailsPost(w http.ResponseWriter, r *http.Request) {
v.Name = name v.Name = name
v.Rating = rating v.Rating = rating
comment := strings.TrimSpace(r.FormValue("comment"))
if comment != "" {
if len(comment) > 1024 {
comment = comment[:1024] + "..."
}
err = v.StoreComment(r.Context(), h.db, comment)
if err != nil {
httpError(w, "can't add comment", err, http.StatusInternalServerError)
return
}
}
err = r.ParseMultipartForm(16 * 1024 * 1024) err = r.ParseMultipartForm(16 * 1024 * 1024)
if err != nil { if err != nil {
httpError(w, "can't parse multipart form data", err, http.StatusInternalServerError) httpError(w, "can't parse multipart form data", err, http.StatusInternalServerError)
......
CREATE TABLE comments (
content TEXT,
wine INTEGER,
FOREIGN KEY (wine) REFERENCES wines(rowid)
);
\ No newline at end of file
...@@ -35,14 +35,27 @@ ...@@ -35,14 +35,27 @@
<input id="form-new-picture" type="file" name="picture" accept="image/png, image/jpeg"> <input id="form-new-picture" type="file" name="picture" accept="image/png, image/jpeg">
<!-- TODO: add a button to remove the image --> <!-- TODO: add a button to remove the image -->
</div> </div>
<div class="pure-control-group">
<label for="form-new-comment">Add comment</label>
<textarea id="form-new-comment" name="comment" placeholder="Etaoin Shrdlu">
</textarea>
</div>
</fieldset> </fieldset>
<details> {{ if .Wine.Comments }}
Etaoin Shrdlu, here be metadata <details open>
<summary>Comments</summary>
<ul>
{{ range .Wine.Comments }}
<li>{{ .Content }}</li>
{{ end }}
</ul>
</details> </details>
{{ end }}
{{ if .Wine.HasPicture }} {{ if .Wine.HasPicture }}
<p>And here's a picture:<br/> <p>Here's a picture:<br/>
<img class="foto pure-img" src="/details/img?id={{ .Wine.ID }}"/> <img class="foto pure-img" src="/details/img?id={{ .Wine.ID }}"/>
{{ end }} {{ end }}
......
...@@ -26,12 +26,17 @@ const ( ...@@ -26,12 +26,17 @@ const (
Update Update
) )
type Comment struct {
Content string `db:"content"`
}
type Vino struct { type Vino struct {
ID int `db:"rowid"` ID int `db:"rowid"`
Name string `db:"name"` Name string `db:"name"`
Rating int `db:"rating"` Rating int `db:"rating"`
Picture image.Image `db:"-"` Picture image.Image `db:"-"`
HasPicture bool `db:"has_picture"` // Set to true if there's picture data for this vino HasPicture bool `db:"has_picture"` // Set to true if there's picture data for this vino
Comments []Comment `db:"-"`
} }
func DeleteVino(ctx context.Context, db *sqlx.DB, id int) error { func DeleteVino(ctx context.Context, db *sqlx.DB, id int) error {
...@@ -51,7 +56,10 @@ func DeleteVino(ctx context.Context, db *sqlx.DB, id int) error { ...@@ -51,7 +56,10 @@ func DeleteVino(ctx context.Context, db *sqlx.DB, id int) error {
} }
func LoadVino(ctx context.Context, db *sqlx.DB, id int) (Vino, error) { func LoadVino(ctx context.Context, db *sqlx.DB, id int) (Vino, error) {
cols := []string{"rowid", "name", "rating", "case when picture is not null then true else false end as has_picture"} cols := []string{
"rowid", "name", "rating",
"CASE WHEN picture IS NOT NULL THEN true ELSE false END AS has_picture",
}
query, args, err := squirrel.Select(cols...). query, args, err := squirrel.Select(cols...).
From("wines"). From("wines").
...@@ -61,8 +69,28 @@ func LoadVino(ctx context.Context, db *sqlx.DB, id int) (Vino, error) { ...@@ -61,8 +69,28 @@ func LoadVino(ctx context.Context, db *sqlx.DB, id int) (Vino, error) {
return Vino{}, err return Vino{}, err
} }
tx, err := db.Beginx()
if err != nil {
return Vino{}, err
}
defer tx.Rollback() // The tx is readonly anyway, no need to commit anything
var v Vino var v Vino
err = db.GetContext(ctx, &v, query, args...) err = tx.GetContext(ctx, &v, query, args...)
if err != nil {
return v, err
}
query, args, err = squirrel.Select("content").
From("comments").
Where(squirrel.Eq{"wine": id}).
OrderBy("rowid ASC").
ToSql()
if err != nil {
return v, err
}
err = tx.SelectContext(ctx, &v.Comments, query, args...)
if err != nil { if err != nil {
return v, err return v, err
} }
...@@ -85,7 +113,7 @@ func LoadPictureData(ctx context.Context, db *sqlx.DB, id int) ([]byte, error) { ...@@ -85,7 +113,7 @@ func LoadPictureData(ctx context.Context, db *sqlx.DB, id int) ([]byte, error) {
// AddPicture loads picture data (PNG or JPEG) from fh and sets v's picture to it. // AddPicture loads picture data (PNG or JPEG) from fh and sets v's picture to it.
// If something goes wrong during loading, or the image is neither PNG nor JPEG, an error // If something goes wrong during loading, or the image is neither PNG nor JPEG, an error
// is returned. If contentType is not the empty string, it is validated to be either // is returned. If contentType is not the empty string, it is validated to be either
// image/png or image/jpeg // image/png or image/jpeg.
func (v *Vino) AddPicture(fh io.Reader, contentType string) error { func (v *Vino) AddPicture(fh io.Reader, contentType string) error {
switch contentType { switch contentType {
case "", "image/jpeg", "image/png": case "", "image/jpeg", "image/png":
...@@ -196,6 +224,17 @@ func (v *Vino) Store(ctx context.Context, db *sqlx.DB, op storeOperation) (err e ...@@ -196,6 +224,17 @@ func (v *Vino) Store(ctx context.Context, db *sqlx.DB, op storeOperation) (err e
return nil return nil
} }
func (v *Vino) StoreComment(ctx context.Context, db *sqlx.DB, comment string) error {
query := `INSERT INTO comments (content, wine) VALUES (?, ?)`
_, err := db.ExecContext(ctx, query, comment, v.ID)
if err != nil {
return err
}
return nil
}
func ListWines(ctx context.Context, db *sqlx.DB) ([]Vino, error) { func ListWines(ctx context.Context, db *sqlx.DB) ([]Vino, error) {
query := `SELECT rowid, name, rating FROM wines ORDER BY rating DESC;` query := `SELECT rowid, name, rating FROM wines ORDER BY rating DESC;`
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment