Allow articles to be independent of an issue and allow a title for an issue

This commit is contained in:
Jason Streifling 2024-09-01 18:18:07 +02:00
parent e05521591b
commit 79ee20a50e
9 changed files with 116 additions and 89 deletions

View File

@ -21,6 +21,8 @@ type Article struct {
ID int64
AuthorID int64
IssueID int64
IsInIssue bool
AutoGenerated bool
}
func (db *DB) AddArticle(a *Article) (int64, error) {
@ -29,8 +31,8 @@ func (db *DB) AddArticle(a *Article) (int64, error) {
selectQuery := "SELECT id FROM issues WHERE published = false"
insertQuery := `
INSERT INTO articles
(title, description, link, enc_url, enc_length, enc_type, published, rejected, author_id, issue_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
(title, description, link, enc_url, enc_length, enc_type, published, rejected, author_id, issue_id, is_in_issue, auto_generated)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`
for i := 0; i < TxMaxRetries; i++ {
@ -47,8 +49,10 @@ func (db *DB) AddArticle(a *Article) (int64, error) {
return 0, fmt.Errorf("error getting issue ID when adding article to DB: %v", err)
}
fmt.Println(a)
result, err := tx.Exec(insertQuery, a.Title, a.Description, a.Link,
a.EncURL, a.EncLength, a.EncType, a.Published, a.Rejected, a.AuthorID, id)
a.EncURL, a.EncLength, a.EncType, a.Published, a.Rejected, a.AuthorID, id,
a.IsInIssue, a.AutoGenerated)
if err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
@ -82,7 +86,7 @@ func (db *DB) AddArticle(a *Article) (int64, error) {
func (db *DB) GetArticle(id int64) (*Article, error) {
query := `
SELECT title, created, description, link, enc_url, enc_length, enc_type, published, author_id
SELECT title, created, description, link, enc_url, enc_length, enc_type, published, author_id, issue_id, is_in_issue, auto_generated
FROM articles
WHERE id = ?
`
@ -94,7 +98,8 @@ func (db *DB) GetArticle(id int64) (*Article, error) {
if err := row.Scan(&article.Title, &created, &article.Description,
&article.Link, &article.EncURL, &article.EncLength, &article.EncType,
&article.Published, &article.AuthorID); err != nil {
&article.Published, &article.AuthorID, &article.IssueID,
&article.IsInIssue, &article.AutoGenerated); err != nil {
return nil, fmt.Errorf("error scanning article row: %v", err)
}
@ -109,7 +114,7 @@ func (db *DB) GetArticle(id int64) (*Article, error) {
func (db *DB) GetCertainArticles(published, rejected bool) ([]*Article, error) {
query := `
SELECT id, title, created, description, link, enc_url, enc_length, enc_type, author_id, issue_id
SELECT id, title, created, description, link, enc_url, enc_length, enc_type, author_id, issue_id, is_in_issue, auto_generated
FROM articles
WHERE published = ?
AND rejected = ?
@ -126,7 +131,8 @@ func (db *DB) GetCertainArticles(published, rejected bool) ([]*Article, error) {
if err = rows.Scan(&article.ID, &article.Title, &created,
&article.Description, &article.Link, &article.EncURL, &article.EncLength,
&article.EncType, &article.AuthorID, &article.IssueID); err != nil {
&article.EncType, &article.AuthorID, &article.IssueID,
&article.IsInIssue, &article.AutoGenerated); err != nil {
return nil, fmt.Errorf("error scanning article row: %v", err)
}
@ -147,9 +153,9 @@ func (db *DB) GetCurrentIssueArticles() ([]*Article, error) {
txOptions := &sql.TxOptions{Isolation: sql.LevelSerializable}
issueQuery := "SELECT id FROM issues WHERE published = false"
articlesQuery := `
SELECT id, title, created, description, link, enc_url, enc_length, enc_type, author_id
SELECT id, title, created, description, link, enc_url, enc_length, enc_type, author_id, auto_generated
FROM articles
WHERE issue_id = ? AND published = true
WHERE issue_id = ? AND published = true AND is_in_issue = true
`
for i := 0; i < TxMaxRetries; i++ {
@ -182,7 +188,7 @@ func (db *DB) GetCurrentIssueArticles() ([]*Article, error) {
if err = rows.Scan(&article.ID, &article.Title, &created,
&article.Description, &article.Link, &article.EncURL, &article.EncLength,
&article.EncType, &article.AuthorID); err != nil {
&article.EncType, &article.AuthorID, &article.AutoGenerated); err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
}

View File

@ -31,7 +31,10 @@ func GenerateRSS(c *Config, db *DB) (*string, error) {
for _, tag := range tags {
tagNames = append(tagNames, tag.Name)
}
if article.IsInIssue {
tagNames = append(tagNames, fmt.Sprint("Orient Express ", article.IssueID))
}
user, err := db.GetUser(article.AuthorID)
if err != nil {
@ -57,9 +60,8 @@ func GenerateRSS(c *Config, db *DB) (*string, error) {
PubDate: article.Created.Format(time.RFC1123Z),
Title: articleTitle,
}
fmt.Println(article.Link, ": ", len(article.Link))
if article.Title == "Autogenerated cpolis Issue Article" {
if article.AutoGenerated {
item.Enclosure = &rss.Enclosure{
Url: article.EncURL,
Lenght: article.EncLength,

View File

@ -78,6 +78,8 @@ func SubmitArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
Published: false,
Rejected: false,
AuthorID: session.Values["id"].(int64),
IsInIssue: r.PostFormValue("issue") == "on",
AutoGenerated: false,
}
article.ID, err = db.AddArticle(article)
@ -153,6 +155,7 @@ func ResubmitArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
&b.Attribute{Table: "articles", ID: id, AttName: "title", Value: title},
&b.Attribute{Table: "articles", ID: id, AttName: "description", Value: description},
&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"},
); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
@ -240,46 +243,27 @@ func ReviewUnpublishedArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.Hand
return
}
type htmlData struct {
Title string
Description string
data := new(struct {
Article *b.Article
Content template.HTML
Tags []*b.Tag
ID int64
}
})
var err error
data := new(htmlData)
data.ID, err = strconv.ParseInt(r.PathValue("id"), 10, 64)
id, err := strconv.ParseInt(r.PathValue("id"), 10, 64)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
article, err := db.GetArticle(data.ID)
data.Article, err = db.GetArticle(id)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
data.Title, err = b.ConvertToPlain(article.Title)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
data.Description, err = b.ConvertToPlain(article.Description)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
articleAbsName := fmt.Sprint(c.ArticleDir, "/", article.ID, ".md")
articleAbsName := fmt.Sprint(c.ArticleDir, "/", data.Article.ID, ".md")
contentBytes, err := os.ReadFile(articleAbsName)
if err != nil {
log.Println(err)
@ -295,7 +279,7 @@ func ReviewUnpublishedArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.Hand
}
data.Content = template.HTML(content)
data.Tags, err = db.GetArticleTags(data.ID)
data.Tags, err = db.GetArticleTags(data.Article.ID)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
@ -314,13 +298,12 @@ func ReviewRejectedArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.Handler
return
}
type htmlData struct {
data := new(struct {
Selected map[int64]bool
Article *b.Article
Content string
Tags []*b.Tag
}
data := new(htmlData)
})
id, err := strconv.ParseInt(r.PathValue("id"), 10, 64)
if err != nil {
@ -378,13 +361,20 @@ func PublishArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
}
id, err := strconv.ParseInt(r.PathValue("id"), 10, 64)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
article, err := db.GetArticle(id)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err = db.AddArticleToCurrentIssue(id); err != nil {
if err = db.AddArticleToCurrentIssue(article.ID); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
@ -523,7 +513,7 @@ func ShowPublishedArticles(c *b.Config, db *b.DB, s *b.CookieStore) http.Handler
filteredArticles := make([]*b.Article, 0)
for _, article := range publishedArticles {
if article.Title != "Autogenerated cpolis Issue Article" {
if !article.AutoGenerated {
filteredArticles = append(filteredArticles, article)
}
}

View File

@ -59,7 +59,7 @@ func PublishLatestIssue(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFun
mimeType := mime.TypeByExtension(filepath.Ext(imgAbsName))
article := &b.Article{
Title: "Autogenerated cpolis Issue Article",
Title: r.PostFormValue("issue-title"),
EncURL: fmt.Sprint(c.Domain, "/image/serve/", imgFileName),
EncLength: int(imgSize),
EncType: mimeType,
@ -67,6 +67,7 @@ func PublishLatestIssue(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFun
Rejected: false,
Created: time.Now(),
AuthorID: session.Values["id"].(int64),
AutoGenerated: true,
}
fmt.Println(article.Link)

View File

@ -33,6 +33,8 @@ CREATE TABLE articles (
rejected BOOL NOT NULL,
author_id INT NOT NULL,
issue_id INT NOT NULL,
is_in_issue BOOL NOT NULL,
auto_generated BOOL NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (author_id) REFERENCES users (id),
FOREIGN KEY (issue_id) REFERENCES issues (id)

View File

@ -17,7 +17,18 @@
<div>
<h3>Cover</h3>
<input id="image-upload" name="issue-image" type="file" required hx-post="/issue/upload-image" />
<div class="flex">
<label class="btn text-center" for="image-upload">Bild hochladen</label>
<input class="hidden" id="image-upload" name="issue-image" type="file" required
hx-post="/issue/upload-image" />
</div>
</div>
<div>
<h3>Titel</h3>
<div class="flex flex-col gap-y-1">
<input name="issue-title" type="text" />
</div>
</div>
<div>

View File

@ -21,6 +21,11 @@
<div>
<span>Tags</span>
<div class="flex flex-wrap gap-x-4">
<div>
<input id="issue" name="issue" type="checkbox" />
<label for="issue">Orient Express</label>
</div>
{{range .Tags}}
<div>
<input id="{{.Name}}" name="tags" type="checkbox" value="{{.ID}}" />

View File

@ -21,6 +21,11 @@
<div>
<span>Tags</span>
<div class="flex flex-wrap gap-x-4">
<div>
<input id="issue" name="issue" type="checkbox" {{if .Article.IsInIssue}}checked{{end}} />
<label for="issue">Orient Express</label>
</div>
{{range .Tags}}
<div>
<input id="tag-{{.Name}}" name="tags" type="checkbox" value="{{.ID}}" {{if index $.Selected

View File

@ -4,12 +4,12 @@
<div>
<span>Titel</span>
<div class="bg-white border mb-3 px-2 py-2 rounded-md w-full">
{{.Title}}
{{.Article.Title}}
</div>
<span>Beschreibung</span>
<div class="bg-white border mb-3 px-2 py-2 rounded-md w-full">
{{.Description}}
{{.Article.Description}}
</div>
<span>Artikel</span>
@ -21,16 +21,21 @@
<span>Tags</span>
<div class="bg-white border mb-3 px-2 py-2 rounded-md w-full">
{{if .Article.IsInIssue}}
<span>Orient Express</span>
<br>
{{end}}
{{range .Tags}}
{{.Name}}
<span>{{.Name}}</span>
<br>
{{end}}
</div>
<div class="btn-area">
<input class="action-btn" type="submit" value="Veröffentlichen" hx-get="/article/publish/{{.ID}}"
<input class="action-btn" type="submit" value="Veröffentlichen" hx-get="/article/publish/{{.Article.ID}}"
hx-target="#page-content" />
<input class="btn" type="submit" value="Ablehnen" hx-get="/article/reject/{{.Article.ID}}"
hx-target="#page-content" />
<input class="btn" type="submit" value="Ablehnen" hx-get="/article/reject/{{.ID}}" hx-target="#page-content" />
<button class="btn" hx-get="/hub" hx-target="#page-content">Zurück</button>
</div>
</div>