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.")
}
}