func HandleCreateMessage(ctx context.Context, w http.ResponseWriter, r *http.Request) { uid, ok := CurrentUserID(r) if !ok { // TODO - redirect to authentication page, but remember form content tmpl.Render500(w, errors.New("not implemented")) return } tid, err := strconv.Atoi(param(ctx, "topicid")) if err != nil || tid < 0 { tmpl.Render404(w, "Topic does not exist") return } content := strings.TrimSpace(r.FormValue("content")) if len(content) < 3 { tmpl.Render400(w, "Message too short") return } if len(content) > 20000 { tmpl.Render400(w, "Message too long") return } tx, err := DB(ctx).Beginx() if err != nil { tmpl.Render500(w, err) return } defer tx.Rollback() store := NewStore(tx) t, err := store.TopicByID(uint(tid)) if err != nil { if err == ErrNotFound { tmpl.Render404(w, "Topic does not exist") } else { tmpl.Render500(w, err) } return } m, err := store.CreateMessage(t.TopicID, uid, content, time.Now()) if err != nil { tmpl.Render500(w, err) return } if err := tx.Commit(); err != nil { tmpl.Render500(w, err) return } murl := fmt.Sprintf( "/t/%d/%s?page=%d#m%d", t.TopicID, t.Topic.Slug(), t.Pages(), m.MessageID) http.Redirect(w, r, murl, http.StatusFound) }
func HandleListTopics(ctx context.Context, w http.ResponseWriter, r *http.Request) { store := NewStore(DB(ctx)) p := NewSimplePaginator(time.Now()) if sec, err := strconv.Atoi(r.URL.Query().Get("page")); err == nil { p.Current = int(sec) } if t, err := store.LastTopicUpdated(time.Unix(int64(p.Current), 0)); err != nil { tmpl.Render500(w, err) return } else if checkLastModified(w, r, t) { return } var categories []int for _, raw := range r.URL.Query()["category"] { if id, err := strconv.Atoi(raw); err == nil { categories = append(categories, id) } } topics, err := store.Topics(categories, time.Unix(int64(p.Current), 0), p.Limit()) if err != nil { tmpl.Render500(w, err) return } // if there are less topics than the page size, then this is the last page if len(topics) == PageSize { p.Next = int(topics[len(topics)-1].Updated.Unix()) } c := struct { Topics []*TopicWithUserCategory Pagination *SimplePaginator URLQuery URLQueryBuilder }{ Topics: topics, Pagination: p, URLQuery: URLQueryBuilder{r}, } tmpl.Render(w, http.StatusOK, "page_topic_list", c) }
func HandleCreateTopic(ctx context.Context, w http.ResponseWriter, r *http.Request) { uid, ok := CurrentUserID(r) if !ok { // TODO - redirect to authentication page, but remember form content tmpl.Render500(w, errors.New("not implemented")) return } var c struct { Title string TitleErr string Category uint CategoryErr string Categories []*Category Content string ContentErr string } if r.Method == "GET" { if cats, err := NewStore(DB(ctx)).Categories(); err != nil { tmpl.Render500(w, err) } else { c.Categories = cats tmpl.Render(w, http.StatusOK, "page_create_topic", c) } return } if err := r.ParseMultipartForm(2 << 20); err != nil { tmpl.Render400(w, err.Error()) return } c.Content = strings.TrimSpace(r.FormValue("content")) c.Title = strings.TrimSpace(r.FormValue("title")) if len(c.Title) < 3 { c.TitleErr = "Title must be at least 3 characters long" } if len(c.Title) > 200 { c.TitleErr = "Title must not be longer than 200 characters" } if len(c.Content) < 3 { c.ContentErr = "Content must be at least 3 characters long" } if len(c.Content) > 10000 { c.ContentErr = "Content must be shorter than 10000 characters" } if raw := r.FormValue("category"); raw == "" { c.CategoryErr = "Category is required" } else { if cat, err := strconv.Atoi(r.FormValue("category")); err == nil { c.Category = uint(cat) } else { c.CategoryErr = "Invalid category" } } if c.TitleErr != "" || c.ContentErr != "" || c.CategoryErr != "" { if cats, err := NewStore(DB(ctx)).Categories(); err != nil { tmpl.Render500(w, err) } else { c.Categories = cats tmpl.Render(w, http.StatusBadRequest, "page_create_topic", c) } return } tx, err := DB(ctx).Beginx() if err != nil { tmpl.Render500(w, err) return } defer tx.Rollback() store := NewStore(tx) now := time.Now() topic, err := store.CreateTopic(c.Title, uid, c.Category, now) if err != nil { tmpl.Render500(w, err) return } if _, err := store.CreateMessage(topic.TopicID, uid, c.Content, now); err != nil { tmpl.Render500(w, err) return } if err := tx.Commit(); err != nil { tmpl.Render500(w, err) return } turl := fmt.Sprintf("/t/%d/%s", topic.TopicID, topic.Slug()) http.Redirect(w, r, turl, http.StatusFound) }
func HandleListTopicMessages(ctx context.Context, w http.ResponseWriter, r *http.Request) { tx, err := DB(ctx).Beginx() if err != nil { panic(err) } defer tx.Rollback() store := NewStore(tx) topicID, err := strconv.Atoi(param(ctx, "topicid")) if err != nil || topicID < 0 { tmpl.Render404(w, "Topic does not exist") return } topic, err := store.TopicByID(uint(topicID)) if err == ErrNotFound { tmpl.Render404(w, "Topic does not exist") return } if err != nil { tmpl.Render500(w, err) return } if checkLastModified(w, r, topic.Updated) { return } p := NewPaginator(r.URL.Query(), int(topic.Replies+1)) messages, err := store.TopicMessages(topic.TopicID, p.Offset(), p.Limit()) if err != nil { tmpl.Render500(w, err) return } type MessageWithUserPos struct { *Message *User CollectionPos int // position number in messages collection } emsgs := make([]*MessageWithUserPos, 0, len(messages)) for i, m := range messages { emsgs = append(emsgs, &MessageWithUserPos{ CollectionPos: i + (p.CurrentPage()-1)*int(p.PageSize()) + 1, Message: &m.Message, User: &m.User, }) } c := struct { Topic *TopicWithUserCategory Messages []*MessageWithUserPos Paginator *Paginator }{ Topic: topic, Messages: emsgs, Paginator: p, } tmpl.Render(w, http.StatusOK, "page_message_list", c) }