2018-12-24 00:18:04 +00:00
|
|
|
package questions
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"html/template"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2018-12-24 19:47:25 +00:00
|
|
|
"strconv"
|
2018-12-24 00:18:04 +00:00
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/gorilla/mux"
|
2018-12-24 19:47:25 +00:00
|
|
|
"github.com/kirsle/blog/models/posts"
|
|
|
|
"github.com/kirsle/blog/models/settings"
|
2018-12-24 00:21:50 +00:00
|
|
|
"github.com/kirsle/blog/src/log"
|
|
|
|
"github.com/kirsle/blog/src/mail"
|
|
|
|
"github.com/kirsle/blog/src/markdown"
|
2018-12-24 19:47:25 +00:00
|
|
|
"github.com/kirsle/blog/src/middleware/auth"
|
|
|
|
"github.com/kirsle/blog/src/models"
|
2018-12-24 00:21:50 +00:00
|
|
|
"github.com/kirsle/blog/src/render"
|
|
|
|
"github.com/kirsle/blog/src/responses"
|
|
|
|
"github.com/kirsle/blog/src/sessions"
|
2018-12-24 19:47:25 +00:00
|
|
|
"github.com/urfave/negroni"
|
2018-12-24 00:18:04 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var badRequest func(http.ResponseWriter, *http.Request, string)
|
|
|
|
|
|
|
|
// Register the comment routes to the app.
|
2018-12-24 19:47:25 +00:00
|
|
|
func Register(r *mux.Router, loginError http.HandlerFunc) {
|
2018-12-24 00:18:04 +00:00
|
|
|
badRequest = responses.BadRequest
|
|
|
|
|
|
|
|
r.HandleFunc("/ask", questionsHandler)
|
2018-12-24 19:47:25 +00:00
|
|
|
r.Handle("/ask/answer",
|
|
|
|
negroni.New(
|
|
|
|
negroni.HandlerFunc(auth.LoginRequired(loginError)),
|
|
|
|
negroni.WrapFunc(answerHandler),
|
|
|
|
),
|
|
|
|
).Methods(http.MethodPost)
|
2018-12-24 00:18:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func questionsHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
// Share their name and email with the commenting system.
|
|
|
|
session := sessions.Get(r)
|
|
|
|
name, _ := session.Values["c.name"].(string)
|
|
|
|
email, _ := session.Values["c.email"].(string)
|
|
|
|
|
2018-12-24 19:47:25 +00:00
|
|
|
Q := models.NewQuestion()
|
2018-12-24 00:18:04 +00:00
|
|
|
Q.Name = name
|
|
|
|
Q.Email = email
|
|
|
|
|
|
|
|
cfg, err := settings.Load()
|
|
|
|
if err != nil {
|
|
|
|
responses.Error(w, r, "Error loading site configuration!")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
v := map[string]interface{}{}
|
|
|
|
|
|
|
|
// Previewing, deleting, or posting?
|
|
|
|
if r.Method == http.MethodPost {
|
|
|
|
Q.ParseForm(r)
|
|
|
|
log.Info("Q: %+v", Q)
|
|
|
|
|
2018-12-24 19:47:25 +00:00
|
|
|
if err := Q.Validate(); err != nil {
|
|
|
|
log.Debug("Validation error on question form: %s", err.Error())
|
|
|
|
v["Error"] = err
|
|
|
|
} else {
|
|
|
|
// Cache their name and email in their session.
|
|
|
|
session.Values["c.name"] = Q.Name
|
|
|
|
session.Values["c.email"] = Q.Email
|
|
|
|
session.Save(r, w)
|
|
|
|
|
|
|
|
// Append their comment.
|
|
|
|
err := Q.Save()
|
|
|
|
if err != nil {
|
|
|
|
log.Error("Error saving new question: %s", err.Error())
|
|
|
|
responses.FlashAndRedirect(w, r, "/ask", "Error saving question: %s", err)
|
2018-12-24 00:18:04 +00:00
|
|
|
return
|
|
|
|
}
|
2018-12-24 19:47:25 +00:00
|
|
|
|
|
|
|
// Email the site admin.
|
|
|
|
subject := fmt.Sprintf("Ask Me Anything (%s) from %s", cfg.Site.Title, Q.Name)
|
|
|
|
log.Info("Emailing site admin about this question")
|
|
|
|
go mail.SendEmail(mail.Email{
|
|
|
|
To: cfg.Site.AdminEmail,
|
|
|
|
Admin: true,
|
|
|
|
ReplyTo: Q.Email,
|
|
|
|
Subject: subject,
|
|
|
|
Template: ".email/generic.gohtml",
|
|
|
|
Data: map[string]interface{}{
|
|
|
|
"Subject": subject,
|
|
|
|
"Message": template.HTML(
|
|
|
|
markdown.RenderMarkdown(
|
|
|
|
Q.Question +
|
|
|
|
"\n\nAnswer this at " + strings.Trim(cfg.Site.URL, "/") + "/ask",
|
|
|
|
),
|
|
|
|
),
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
// Log it to disk, too.
|
|
|
|
fh, err := os.OpenFile(filepath.Join(*render.UserRoot, ".questions.log"), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
|
|
|
if err != nil {
|
|
|
|
responses.Flash(w, r, "Error logging the message to disk: %s", err)
|
|
|
|
} else {
|
|
|
|
fh.WriteString(fmt.Sprintf(
|
|
|
|
"Date: %s\nName: %s\nEmail: %s\n\n%s\n\n--------------------\n\n",
|
|
|
|
time.Now().Format(time.UnixDate),
|
|
|
|
Q.Name,
|
|
|
|
Q.Email,
|
|
|
|
Q.Question,
|
|
|
|
))
|
|
|
|
fh.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Info("Recorded question from %s: %s", Q.Name, Q.Question)
|
|
|
|
responses.FlashAndRedirect(w, r, "/ask", "Your question has been recorded!")
|
2018-12-24 00:18:04 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
v["Q"] = Q
|
|
|
|
|
2018-12-24 19:47:25 +00:00
|
|
|
// Load the pending questions.
|
|
|
|
pending, err := models.PendingQuestions(0, 20)
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err.Error())
|
|
|
|
}
|
|
|
|
v["Pending"] = pending
|
|
|
|
|
2018-12-24 00:18:04 +00:00
|
|
|
render.Template(w, r, "questions.gohtml", v)
|
|
|
|
}
|
2018-12-24 19:47:25 +00:00
|
|
|
|
|
|
|
func answerHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
submit := r.FormValue("submit")
|
|
|
|
|
|
|
|
cfg, err := settings.Load()
|
|
|
|
if err != nil {
|
|
|
|
responses.Error(w, r, "Error loading site configuration!")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
type answerForm struct {
|
|
|
|
ID int
|
|
|
|
Answer string
|
|
|
|
Submit string
|
|
|
|
}
|
|
|
|
id, _ := strconv.Atoi(r.FormValue("id"))
|
|
|
|
form := answerForm{
|
|
|
|
ID: id,
|
|
|
|
Answer: r.FormValue("answer"),
|
|
|
|
Submit: r.FormValue("submit"),
|
|
|
|
}
|
|
|
|
|
|
|
|
// Look up the question.
|
|
|
|
Q, err := models.GetQuestion(form.ID)
|
|
|
|
if err != nil {
|
|
|
|
responses.FlashAndRedirect(w, r, "/ask",
|
|
|
|
fmt.Sprintf("Did not find question ID %d", form.ID),
|
|
|
|
)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
switch submit {
|
|
|
|
case "answer":
|
|
|
|
// Prepare a Markdown-themed blog post and go to the Preview page for it.
|
|
|
|
blog := posts.New()
|
|
|
|
blog.Title = "Ask"
|
|
|
|
blog.Tags = []string{"ask"}
|
|
|
|
blog.Fragment = fmt.Sprintf("ask-%s",
|
|
|
|
time.Now().Format("20060102150405"),
|
|
|
|
)
|
|
|
|
blog.Body = fmt.Sprintf(
|
|
|
|
"> **%s** asks:\n>\n> %s\n\n"+
|
|
|
|
"%s\n",
|
|
|
|
Q.Name,
|
|
|
|
strings.Replace(Q.Question, "\n", "> \n", 0),
|
|
|
|
form.Answer,
|
|
|
|
)
|
|
|
|
|
|
|
|
Q.Status = models.Answered
|
|
|
|
Q.Save()
|
|
|
|
|
|
|
|
// TODO: email the person who asked about the new URL.
|
|
|
|
if Q.Email != "" {
|
|
|
|
log.Info("Notifying user %s by email that the question is answered", Q.Email)
|
|
|
|
go mail.SendEmail(mail.Email{
|
|
|
|
To: Q.Email,
|
|
|
|
Subject: "Your question has been answered",
|
|
|
|
Template: ".email/generic.gohtml",
|
|
|
|
Data: map[string]interface{}{
|
|
|
|
"Subject": "Your question has been answered",
|
|
|
|
"Message": template.HTML(
|
|
|
|
markdown.RenderMarkdown(
|
|
|
|
fmt.Sprintf(
|
|
|
|
"Hello, %s\n\n"+
|
|
|
|
"Your recent question on %s has been answered. To "+
|
|
|
|
"view the answer, please visit the following link:\n\n"+
|
|
|
|
"%s/%s",
|
|
|
|
Q.Name,
|
|
|
|
cfg.Site.Title,
|
|
|
|
cfg.Site.URL,
|
|
|
|
blog.Fragment,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
render.Template(w, r, "blog/edit", map[string]interface{}{
|
|
|
|
"preview": template.HTML(markdown.RenderTrustedMarkdown(blog.Body)),
|
|
|
|
"post": blog,
|
|
|
|
})
|
|
|
|
return
|
|
|
|
case "delete":
|
|
|
|
Q.Status = models.Deleted
|
|
|
|
Q.Save()
|
|
|
|
responses.FlashAndRedirect(w, r, "/ask", "Question deleted.")
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
responses.FlashAndRedirect(w, r, "/ask", "Unknown submit action.")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|