hmn/src/website/education.go

249 lines
6.2 KiB
Go

package website
import (
"errors"
"fmt"
"html/template"
"net/http"
"time"
"git.handmade.network/hmn/hmn/src/db"
"git.handmade.network/hmn/hmn/src/hmndata"
"git.handmade.network/hmn/hmn/src/hmnurl"
"git.handmade.network/hmn/hmn/src/models"
"git.handmade.network/hmn/hmn/src/parsing"
"git.handmade.network/hmn/hmn/src/templates"
)
func EducationIndex(c *RequestContext) ResponseData {
type indexData struct {
templates.BaseData
}
tmpl := indexData{
BaseData: getBaseData(c, "Handmade Education", nil),
}
var res ResponseData
res.MustWriteTemplate("education_index.html", tmpl, c.Perf)
return res
}
func EducationGlossary(c *RequestContext) ResponseData {
type glossaryData struct {
templates.BaseData
}
tmpl := glossaryData{
BaseData: getBaseData(c, "Handmade Education", nil),
}
var res ResponseData
res.MustWriteTemplate("education_glossary.html", tmpl, c.Perf)
return res
}
func EducationArticle(c *RequestContext) ResponseData {
type articleData struct {
templates.BaseData
Content template.HTML
}
type articleResult struct {
Article models.EduArticle `db:"a"`
CurrentVersion models.EduArticleVersion `db:"v"`
}
article, err := db.QueryOne[articleResult](c, c.Conn,
`
SELECT $columns
FROM
education_article AS a
JOIN education_article_version AS v ON a.current_version = v.id
WHERE
slug = $1
AND type = $2
`,
c.PathParams["slug"],
models.EduArticleTypeArticle,
)
if errors.Is(err, db.NotFound) {
return FourOhFour(c)
} else if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, err)
}
tmpl := articleData{
BaseData: getBaseData(c, "Handmade Education", nil),
Content: template.HTML(article.CurrentVersion.ContentHTML),
}
var res ResponseData
res.MustWriteTemplate("education_article.html", tmpl, c.Perf)
return res
}
func EducationAdmin(c *RequestContext) ResponseData {
articles, err := hmndata.FetchEduArticles(c, c.Conn, c.CurrentUser, hmndata.EduArticleQuery{})
if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, err)
}
var tmplArticles []templates.EduArticle
var tmplGlossaryTerms []templates.EduArticle
for _, a := range articles {
tmpl := templates.EducationArticleToTemplate(&a.Article, &a.CurrentVersion)
switch a.Article.Type {
case models.EduArticleTypeArticle:
tmplArticles = append(tmplArticles, tmpl)
case models.EduArticleTypeGlossary:
tmplGlossaryTerms = append(tmplGlossaryTerms, tmpl)
}
}
type adminData struct {
templates.BaseData
Articles []templates.EduArticle
GlossaryTerms []templates.EduArticle
}
tmpl := adminData{
BaseData: getBaseData(c, "Education Admin", nil),
Articles: tmplArticles,
GlossaryTerms: tmplGlossaryTerms,
}
var res ResponseData
res.MustWriteTemplate("education_admin.html", tmpl, c.Perf)
return res
}
func EducationArticleNew(c *RequestContext) ResponseData {
type adminData struct {
editorData
}
tmpl := adminData{
editorData: getEditorDataForEduArticle(c.UrlContext, c.CurrentUser, getBaseData(c, "New Education Article", nil), nil),
}
tmpl.editorData.SubmitUrl = hmnurl.BuildEducationArticleNew()
var res ResponseData
res.MustWriteTemplate("editor.html", tmpl, c.Perf)
return res
}
func EducationArticleNewSubmit(c *RequestContext) ResponseData {
form, err := c.GetFormValues()
if err != nil {
return c.ErrorResponse(http.StatusBadRequest, err)
}
var redirectUrl string
title := form.Get("title")
slug := form.Get("slug")
description := form.Get("description")
var eduType models.EduArticleType
switch form.Get("type") {
case "article":
eduType = models.EduArticleTypeArticle
redirectUrl = hmnurl.BuildEducationArticle(slug)
case "glossary":
eduType = models.EduArticleTypeGlossary
redirectUrl = hmnurl.BuildEducationGlossary(slug)
default:
panic(fmt.Errorf("unknown education article type: %s", form.Get("type")))
}
published := form.Get("published") != ""
body := form.Get("body")
// TODO: Education-specific Markdown
bodyRendered := parsing.ParseMarkdown(body, parsing.ForumRealMarkdown)
tx, err := c.Conn.Begin(c)
if err != nil {
panic(err)
}
defer tx.Rollback(c)
{
dupe := 0 < db.MustQueryOneScalar[int](c, tx,
`
SELECT COUNT(*) FROM education_article
WHERE slug = $1 AND type = $2
`,
slug, eduType,
)
if dupe {
return c.RejectRequest("A resource already exists with that slug and type.")
}
articleID := db.MustQueryOneScalar[int](c, tx,
`
INSERT INTO education_article (title, slug, description, published, type, current_version)
VALUES ($1, $2, $3, $4, $5, -1)
RETURNING id
`,
title, slug, description, published, eduType,
)
versionID := db.MustQueryOneScalar[int](c, tx,
`
INSERT INTO education_article_version (article_id, date, editor_id, content_raw, content_html)
VALUES ($1, $2, $3, $4, $5 )
RETURNING id
`,
articleID, time.Now(), c.CurrentUser.ID, body, bodyRendered,
)
tx.Exec(c,
`UPDATE education_article SET current_version = $1 WHERE id = $2`,
versionID, articleID,
)
}
err = tx.Commit(c)
if err != nil {
panic(err)
}
return c.Redirect(redirectUrl, http.StatusSeeOther)
}
func EducationArticleEdit(c *RequestContext) ResponseData {
// TODO
panic("not implemented yet")
}
func EducationArticleEditSubmit(c *RequestContext) ResponseData {
// TODO
panic("not implemented yet")
}
func EducationArticleDelete(c *RequestContext) ResponseData {
// TODO
panic("not implemented yet")
}
func EducationArticleDeleteSubmit(c *RequestContext) ResponseData {
// TODO
panic("not implemented yet")
}
func getEditorDataForEduArticle(
urlContext *hmnurl.UrlContext,
currentUser *models.User,
baseData templates.BaseData,
article *models.EduArticle,
) editorData {
result := editorData{
BaseData: baseData,
SubmitLabel: "Submit",
CanEditPostTitle: true,
MaxFileSize: AssetMaxSize(currentUser),
UploadUrl: urlContext.BuildAssetUpload(),
ShowEduOptions: true,
}
return result
}