249 lines
6.2 KiB
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
|
|
}
|