194 lines
4.8 KiB
Go
194 lines
4.8 KiB
Go
|
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.")
|
||
|
}
|
||
|
}
|