package controllers import ( "fmt" "html/template" "net/http" "strings" "time" "git.kirsle.net/apps/gophertype/pkg/authentication" "git.kirsle.net/apps/gophertype/pkg/console" "git.kirsle.net/apps/gophertype/pkg/glue" "git.kirsle.net/apps/gophertype/pkg/mail" "git.kirsle.net/apps/gophertype/pkg/markdown" "git.kirsle.net/apps/gophertype/pkg/models" "git.kirsle.net/apps/gophertype/pkg/responses" "git.kirsle.net/apps/gophertype/pkg/session" "git.kirsle.net/apps/gophertype/pkg/settings" "github.com/albrow/forms" "github.com/gorilla/mux" "github.com/kirsle/blog/src/log" ) func init() { glue.Register(glue.Endpoint{ Path: "/ask", Methods: []string{"GET", "POST"}, Handler: QuestionHandler, }) glue.Register(glue.Endpoint{ Path: "/ask/answer", Methods: []string{"POST"}, Middleware: []mux.MiddlewareFunc{ authentication.LoginRequired, }, Handler: AnswerHandler, }) } // QuestionHandler implements the "Ask Me Anything" at the URL "/ask" func QuestionHandler(w http.ResponseWriter, r *http.Request) { var ( v = responses.NewTemplateVars(w, r) ses = session.Get(r) ) // Load their cached name from any previous comments they may have posted. name, _ := ses.Values["c.name"].(string) q := models.Questions.New() q.Name = name for r.Method == http.MethodPost { form, _ := forms.Parse(r) q.ParseForm(r) // Validate form parameters. val := form.Validator() val.Require("question") if val.HasErrors() { v.ValidationError = val.ErrorMap() v.V["Error"] = "Missing required form fields." break } // Cache their name in their session for future comments/asks. ses.Values["c.name"] = q.Name ses.Save(r, w) // Save the question. err := q.Save() if err != nil { log.Error("Error saving neq eustion: %s", err) responses.Error(w, r, http.StatusInternalServerError, "Error saving question: "+err.Error()) return } // Email the site admin. if name == "" { name = "Anonymous" } subject := fmt.Sprintf("Ask Me Anything (%s) from %s", settings.Current.Title, name) go mail.EmailAdmins(mail.Email{ Subject: subject, Template: "_builtin/email/generic.gohtml", Data: map[string]interface{}{ "Subject": subject, "Message": template.HTML( markdown.RenderMarkdown(fmt.Sprintf( "%s\n\nAnswer this at %s", q.Question, strings.TrimSuffix(settings.Current.BaseURL, "/")+"/ask", )), ), }, }) session.Flash(w, r, "Your question has been recorded!") responses.Redirect(w, r, "/ask") return } // If logged in, load the pending questions. if authentication.LoggedIn(r) { pending, err := models.Questions.Pending() if err != nil { console.Error("Error loading pending questions: %s", err) } v.V["Pending"] = pending } // Load the recently answered questions for public users. recent, err := models.Questions.RecentlyAnswered(10) if err != nil { console.Error("Error loading recently answered questions: %s", err) } v.V["Q"] = q v.V["Recent"] = recent responses.RenderTemplate(w, r, "_builtin/questions.gohtml", v) } // AnswerHandler handles answering (and deleting) questions. func AnswerHandler(w http.ResponseWriter, r *http.Request) { v := responses.NewTemplateVars(w, r) CurrentUser, _ := authentication.CurrentUser(r) // Validate form parameters. form, _ := forms.Parse(r) val := form.Validator() val.Require("id") val.Require("answer") val.Require("submit") if val.HasErrors() { v.ValidationError = val.ErrorMap() v.V["Error"] = "Missing required form fields." responses.RenderTemplate(w, r, "_builtin/questions.gohtml", v) return } // Look up the question. q, err := models.Questions.Load(form.GetInt("id")) if err != nil { responses.Error(w, r, http.StatusInternalServerError, err.Error()) return } // Handle submit actions. switch form.Get("submit") { case "answer": // Prepare a Markdown themed blog post for this answer. name := q.Name if name == "" { name = "Anonymous" } post := models.Post{ Title: "Ask", ContentType: "markdown", Privacy: models.Public, EnableComments: true, AuthorID: CurrentUser.ID, Tags: []models.TaggedPost{ models.TaggedPost{Tag: "ask"}, }, Fragment: fmt.Sprintf("ask-%s", time.Now().Format("20060102150405"), ), Body: fmt.Sprintf( "> **%s** asks:\n\n> %s\n\n%s\n", name, strings.Replace(q.Question, "\n", "\n> ", 0), form.Get("answer"), ), } post.Save() // Associate the question to this post ID. q.PostID = post.ID q.Answered = true q.Save() // Send the admin to the post edit page. responses.Redirect(w, r, "/"+post.Fragment) case "delete": q.Delete() session.Flash(w, r, "Question deleted!") responses.Redirect(w, r, "/ask") default: responses.BadRequest(w, r, "Invalid submit method.") } }