Compare commits
No commits in common. "364112a0a406bfba0f46541f9ec9e7e1cd72215d" and "200672dae2f84e7b9b4fac4d8f3c88660b7f24ee" have entirely different histories.
364112a0a4
...
200672dae2
@ -8,6 +8,8 @@ import (
|
|||||||
"git.streifling.com/jason/atom"
|
"git.streifling.com/jason/atom"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Feed struct{ *atom.Feed }
|
||||||
|
|
||||||
func GenerateAtomFeed(c *Config, db *DB) (*string, error) {
|
func GenerateAtomFeed(c *Config, db *DB) (*string, error) {
|
||||||
feed := atom.NewFeed(c.Title)
|
feed := atom.NewFeed(c.Title)
|
||||||
feed.ID = atom.NewID("urn:feed:1")
|
feed.ID = atom.NewID("urn:feed:1")
|
||||||
@ -30,25 +32,12 @@ func GenerateAtomFeed(c *Config, db *DB) (*string, error) {
|
|||||||
}
|
}
|
||||||
entry := atom.NewEntry(articleTitle)
|
entry := atom.NewEntry(articleTitle)
|
||||||
entry.ID = atom.NewID(fmt.Sprint("urn:entry:", article.ID))
|
entry.ID = atom.NewID(fmt.Sprint("urn:entry:", article.ID))
|
||||||
|
entry.Content = atom.NewContent(atom.OutOfLine, "text/hmtl", article.ContentLink)
|
||||||
entry.Published = atom.NewDate(article.Created)
|
entry.Published = atom.NewDate(article.Created)
|
||||||
|
|
||||||
if article.AutoGenerated {
|
|
||||||
entry.Content = atom.NewContent(atom.InlineText, "text", "")
|
|
||||||
} else {
|
|
||||||
entry.Content = atom.NewContent(atom.OutOfLine, "text/hmtl", article.ContentLink)
|
|
||||||
|
|
||||||
articleSummary, err := ConvertToPlain(article.Summary)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error converting description to plain text for Atom feed: %v", err)
|
|
||||||
}
|
|
||||||
entry.Summary = atom.NewText("text", articleSummary)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(article.BannerLink) > 0 {
|
|
||||||
linkID := entry.AddLink(atom.NewLink(article.BannerLink))
|
linkID := entry.AddLink(atom.NewLink(article.BannerLink))
|
||||||
entry.Links[linkID].Rel = "enclosure"
|
entry.Links[linkID].Rel = "enclosure"
|
||||||
entry.Links[linkID].Type = "image/webp"
|
entry.Links[linkID].Type = "image/webp"
|
||||||
}
|
|
||||||
|
|
||||||
user, err := db.GetUser(c, article.AuthorID)
|
user, err := db.GetUser(c, article.AuthorID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -56,6 +45,15 @@ func GenerateAtomFeed(c *Config, db *DB) (*string, error) {
|
|||||||
}
|
}
|
||||||
entry.AddAuthor(atom.NewPerson(user.FirstName + " " + user.LastName))
|
entry.AddAuthor(atom.NewPerson(user.FirstName + " " + user.LastName))
|
||||||
|
|
||||||
|
articleSummary, err := ConvertToPlain(article.Summary)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error converting description to plain text for Atom feed: %v", err)
|
||||||
|
}
|
||||||
|
if article.AutoGenerated {
|
||||||
|
articleSummary = "auto generated"
|
||||||
|
}
|
||||||
|
entry.Summary = atom.NewText("text", articleSummary)
|
||||||
|
|
||||||
tags, err := db.GetArticleTags(article.ID)
|
tags, err := db.GetArticleTags(article.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error getting tags for articles for Atom feed: %v", err)
|
return nil, fmt.Errorf("error getting tags for articles for Atom feed: %v", err)
|
||||||
|
@ -52,7 +52,7 @@ func newConfig() *Config {
|
|||||||
PDFDir: "/var/www/cpolis/pdfs",
|
PDFDir: "/var/www/cpolis/pdfs",
|
||||||
PicsDir: "/var/www/cpolis/pics",
|
PicsDir: "/var/www/cpolis/pics",
|
||||||
Port: ":8080",
|
Port: ":8080",
|
||||||
Version: "v0.13.3",
|
Version: "v0.13.2",
|
||||||
WebDir: "/var/www/cpolis/web",
|
WebDir: "/var/www/cpolis/web",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package backend
|
package backend
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"io"
|
"io"
|
||||||
@ -42,3 +43,20 @@ func SaveImage(src io.Reader, maxHeight, maxWidth int, path string) (string, err
|
|||||||
|
|
||||||
return filename, nil
|
return filename, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ServeBase64Image(c *Config, filename string) (string, error) {
|
||||||
|
file := c.PicsDir + "/" + filename
|
||||||
|
|
||||||
|
img, err := os.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("error opening file %v: %v", file, err)
|
||||||
|
}
|
||||||
|
defer img.Close()
|
||||||
|
|
||||||
|
imgBytes, err := io.ReadAll(img)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("error turning %v into bytes: %v", file, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return base64.StdEncoding.EncodeToString(imgBytes), nil
|
||||||
|
}
|
||||||
|
@ -80,7 +80,7 @@ func SubmitArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
|||||||
|
|
||||||
article := &b.Article{
|
article := &b.Article{
|
||||||
Title: r.PostFormValue("article-title"),
|
Title: r.PostFormValue("article-title"),
|
||||||
BannerLink: c.Domain + "/image/serve/" + r.PostFormValue("article-banner-url"),
|
BannerLink: r.PostFormValue("article-banner-url"),
|
||||||
Summary: r.PostFormValue("article-summary"),
|
Summary: r.PostFormValue("article-summary"),
|
||||||
Published: false,
|
Published: false,
|
||||||
Rejected: false,
|
Rejected: false,
|
||||||
@ -93,6 +93,14 @@ func SubmitArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
|||||||
http.Error(w, "Bitte den Titel eingeben.", http.StatusBadRequest)
|
http.Error(w, "Bitte den Titel eingeben.", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if len(article.BannerLink) == 0 {
|
||||||
|
http.Error(w, "Bitte ein Titelbild einfügen.", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(article.Summary) == 0 {
|
||||||
|
http.Error(w, "Bitte die Beschreibung eingeben.", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
article.ID, err = db.AddArticle(article)
|
article.ID, err = db.AddArticle(article)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -173,12 +181,11 @@ func ResubmitArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
bannerLink := r.PostFormValue("article-banner-url")
|
|
||||||
if len(bannerLink) != 0 {
|
|
||||||
bannerLink = c.Domain + "/image/serve/" + bannerLink
|
|
||||||
}
|
|
||||||
|
|
||||||
summary := r.PostFormValue("article-summary")
|
summary := r.PostFormValue("article-summary")
|
||||||
|
if len(summary) == 0 {
|
||||||
|
http.Error(w, "Bitte die Beschreibung eingeben.", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
content := r.PostFormValue("article-content")
|
content := r.PostFormValue("article-content")
|
||||||
if len(content) == 0 {
|
if len(content) == 0 {
|
||||||
@ -195,7 +202,6 @@ func ResubmitArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
|||||||
|
|
||||||
if err = db.UpdateAttributes(
|
if err = db.UpdateAttributes(
|
||||||
&b.Attribute{Table: "articles", ID: id, AttName: "title", Value: title},
|
&b.Attribute{Table: "articles", ID: id, AttName: "title", Value: title},
|
||||||
&b.Attribute{Table: "articles", ID: id, AttName: "banner_link", Value: bannerLink},
|
|
||||||
&b.Attribute{Table: "articles", ID: id, AttName: "summary", Value: summary},
|
&b.Attribute{Table: "articles", ID: id, AttName: "summary", Value: summary},
|
||||||
&b.Attribute{Table: "articles", ID: id, AttName: "rejected", Value: false},
|
&b.Attribute{Table: "articles", ID: id, AttName: "rejected", Value: false},
|
||||||
&b.Attribute{Table: "articles", ID: id, AttName: "is_in_issue", Value: r.PostFormValue("issue") == "on"},
|
&b.Attribute{Table: "articles", ID: id, AttName: "is_in_issue", Value: r.PostFormValue("issue") == "on"},
|
||||||
@ -341,7 +347,13 @@ func ReviewRejectedArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.Handler
|
|||||||
}
|
}
|
||||||
|
|
||||||
imgURL := strings.Split(data.Article.BannerLink, "/")
|
imgURL := strings.Split(data.Article.BannerLink, "/")
|
||||||
data.BannerImage = imgURL[len(imgURL)-1]
|
imgFileName := imgURL[len(imgURL)-1]
|
||||||
|
data.BannerImage, err = b.ServeBase64Image(c, imgFileName)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
articleAbsName := fmt.Sprint(c.ArticleDir, "/", data.Article.ID, ".md")
|
articleAbsName := fmt.Sprint(c.ArticleDir, "/", data.Article.ID, ".md")
|
||||||
content, err := os.ReadFile(articleAbsName)
|
content, err := os.ReadFile(articleAbsName)
|
||||||
@ -612,7 +624,13 @@ func ReviewArticle(c *b.Config, db *b.DB, s *b.CookieStore, action, title, butto
|
|||||||
}
|
}
|
||||||
|
|
||||||
imgURL := strings.Split(article.BannerLink, "/")
|
imgURL := strings.Split(article.BannerLink, "/")
|
||||||
data.BannerImage = imgURL[len(imgURL)-1]
|
imgFileName := imgURL[len(imgURL)-1]
|
||||||
|
data.BannerImage, err = b.ServeBase64Image(c, imgFileName)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
data.Article.Summary, err = b.ConvertToPlain(article.Summary)
|
data.Article.Summary, err = b.ConvertToPlain(article.Summary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -789,7 +807,13 @@ func EditArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
imgURL := strings.Split(data.Article.BannerLink, "/")
|
imgURL := strings.Split(data.Article.BannerLink, "/")
|
||||||
data.BannerImage = imgURL[len(imgURL)-1]
|
imgFileName := imgURL[len(imgURL)-1]
|
||||||
|
data.BannerImage, err = b.ServeBase64Image(c, imgFileName)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
content, err := os.ReadFile(fmt.Sprint(c.ArticleDir, "/", data.Article.ID, ".md"))
|
content, err := os.ReadFile(fmt.Sprint(c.ArticleDir, "/", data.Article.ID, ".md"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"html/template"
|
"html/template"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
b "streifling.com/jason/cpolis/cmd/backend"
|
b "streifling.com/jason/cpolis/cmd/backend"
|
||||||
)
|
)
|
||||||
@ -70,8 +69,20 @@ func UploadBanner(c *b.Config, s *b.CookieStore, fileKey, htmlFile, htmlTemplate
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
data := new(struct{ BannerImage string })
|
base64Img, err := b.ServeBase64Image(c, filename)
|
||||||
data.BannerImage = filename
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data := new(struct {
|
||||||
|
BannerImage string
|
||||||
|
URL string
|
||||||
|
})
|
||||||
|
|
||||||
|
data.BannerImage = base64Img
|
||||||
|
data.URL = c.Domain + "/image/serve/" + filename
|
||||||
|
|
||||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/" + htmlFile)
|
tmpl, err := template.ParseFiles(c.WebDir + "/templates/" + htmlFile)
|
||||||
if err = template.Must(tmpl, err).ExecuteTemplate(w, htmlTemplate, data); err != nil {
|
if err = template.Must(tmpl, err).ExecuteTemplate(w, htmlTemplate, data); err != nil {
|
||||||
@ -81,22 +92,3 @@ func UploadBanner(c *b.Config, s *b.CookieStore, fileKey, htmlFile, htmlTemplate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ServeImage(c *b.Config, s *b.CookieStore) http.HandlerFunc {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if _, err := getSession(w, r, c, s); err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
absFilepath, err := filepath.Abs(c.PicsDir)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
http.ServeFile(w, r, absFilepath+"/"+r.PathValue("img"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -41,6 +41,10 @@ func PublishLatestIssue(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFun
|
|||||||
http.Error(w, "Bitte den Titel eingeben.", http.StatusBadRequest)
|
http.Error(w, "Bitte den Titel eingeben.", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if len(article.BannerLink) == 0 {
|
||||||
|
http.Error(w, "Bitte ein Titelbild einfügen.", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
article.ID, err = db.AddArticle(article)
|
article.ID, err = db.AddArticle(article)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -68,7 +68,6 @@ func main() {
|
|||||||
mux.HandleFunc("GET /article/write", f.WriteArticle(config, db, store))
|
mux.HandleFunc("GET /article/write", f.WriteArticle(config, db, store))
|
||||||
mux.HandleFunc("GET /atom/serve", c.ServeAtomFeed(config))
|
mux.HandleFunc("GET /atom/serve", c.ServeAtomFeed(config))
|
||||||
mux.HandleFunc("GET /hub", f.ShowHub(config, db, store))
|
mux.HandleFunc("GET /hub", f.ShowHub(config, db, store))
|
||||||
mux.HandleFunc("GET /image/{img}", f.ServeImage(config, store))
|
|
||||||
mux.HandleFunc("GET /image/serve/{pic}", c.ServeImage(config))
|
mux.HandleFunc("GET /image/serve/{pic}", c.ServeImage(config))
|
||||||
mux.HandleFunc("GET /issue/this", f.ShowCurrentIssue(config, db, store))
|
mux.HandleFunc("GET /issue/this", f.ShowCurrentIssue(config, db, store))
|
||||||
mux.HandleFunc("GET /logout", f.Logout(config, store))
|
mux.HandleFunc("GET /logout", f.Logout(config, store))
|
||||||
|
@ -3,7 +3,10 @@
|
|||||||
|
|
||||||
<form id="edit-area" hx-encoding="multipart/form-data">
|
<form id="edit-area" hx-encoding="multipart/form-data">
|
||||||
<div class="flex flex-col gap-y-1">
|
<div class="flex flex-col gap-y-1">
|
||||||
{{template "article-banner-template" .}}
|
<div class="w-full" id="article-banner-container">
|
||||||
|
<img src="data:image/webp;base64,{{.BannerImage}}" alt="Banner Image">
|
||||||
|
<input id="article-banner-url" name="article-banner-url" type="hidden" value="{{.Article.BannerLink}}" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-2 gap-4 items-center">
|
<div class="grid grid-cols-2 gap-4 items-center">
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
@ -14,7 +17,7 @@
|
|||||||
<div class="grid grid-cols-1 items-center">
|
<div class="grid grid-cols-1 items-center">
|
||||||
<label class="btn text-center" for="article-banner">Titelbild</label>
|
<label class="btn text-center" for="article-banner">Titelbild</label>
|
||||||
<input class="hidden" id="article-banner" name="article-banner" type="file" required
|
<input class="hidden" id="article-banner" name="article-banner" type="file" required
|
||||||
hx-post="/article/upload-banner" hx-swap="outerHTML" hx-target="#article-banner-container" />
|
hx-post="/article/upload-banner" hx-target="#article-banner-container" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -89,8 +92,8 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{define "article-banner-template"}}
|
{{define "article-banner-template"}}
|
||||||
<div id="article-banner-container">
|
<div class="w-full" id="article-banner-container">
|
||||||
<img src="/image/{{.BannerImage}}" alt="">
|
<img src="data:image/webp;base64,{{.BannerImage}}" alt="Banner Image">
|
||||||
<input id="article-banner-url" name="article-banner-url" type="hidden" value="{{.BannerImage}}" />
|
<input id="article-banner-url" name="article-banner-url" type="hidden" value="{{.URL}}" />
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="w-full" id="article-banner-container">
|
<div class="w-full" id="article-banner-container">
|
||||||
<img src="/image/{{.BannerImage}}" alt="Banner Image">
|
<img src="data:image/webp;base64,{{.BannerImage}}" alt="Banner Image">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span>Titel</span>
|
<span>Titel</span>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user