From f10220f9360bd8c879c1eeb11b08d41f22d2f233 Mon Sep 17 00:00:00 2001 From: Jason Streifling Date: Sun, 10 Mar 2024 15:03:46 +0100 Subject: [PATCH] Added ability to reject and rework article --- cmd/control/rss.go | 8 +- cmd/main.go | 10 +- cmd/model/articles.go | 16 ++- cmd/model/db.go | 25 +++- cmd/model/structs.go | 33 +++-- cmd/view/articles.go | 164 ++++++++++++++++++------ cmd/view/rss.go | 30 ++++- web/templates/editor.html | 12 +- web/templates/hub.html | 1 + web/templates/rejected-articles.html | 10 ++ web/templates/rework-article.html | 10 ++ web/templates/to-be-published.html | 4 +- web/templates/unpublished-articles.html | 2 +- 13 files changed, 237 insertions(+), 88 deletions(-) create mode 100644 web/templates/rejected-articles.html create mode 100644 web/templates/rework-article.html diff --git a/cmd/control/rss.go b/cmd/control/rss.go index 7a61701..813cd8c 100644 --- a/cmd/control/rss.go +++ b/cmd/control/rss.go @@ -8,15 +8,15 @@ import ( "streifling.com/jason/cpolis/cmd/model" ) -func GetChannel(db *model.DB, title, link, desc string) (*rss.Channel, error) { +func GetChannel(db *model.DB, title, link, description string) (*rss.Channel, error) { channel := &rss.Channel{ Title: title, Link: link, - Description: desc, + Description: description, Items: make([]*rss.Item, 0), } - articles, err := db.GetCertainArticles(true) + articles, err := db.GetCertainArticles(true, false) if err != nil { return nil, fmt.Errorf("error fetching published articles: %v", err) } @@ -40,7 +40,7 @@ func GetChannel(db *model.DB, title, link, desc string) (*rss.Channel, error) { Title: article.Title, Author: user.FirstName + user.LastName, PubDate: article.Created.Format(time.RFC1123Z), - Description: article.Desc, + Description: article.Description, Content: &rss.Content{Value: article.Content}, Categories: tagNames, }) diff --git a/cmd/main.go b/cmd/main.go index d150610..a4061a2 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -47,7 +47,8 @@ func main() { mux.HandleFunc("GET /create-tag/", view.CreateTag) mux.HandleFunc("GET /create-user/", view.CreateUser) - mux.HandleFunc("GET /hub/", view.ShowHub(store)) + mux.HandleFunc("GET /hub/", view.ShowHub(db, store)) + mux.HandleFunc("GET /rejected-articles/", view.ShowRejectedArticles(db, store)) mux.HandleFunc("GET /rss/", view.ShowRSS( db, "Freimaurer Distrikt Niedersachsen und Sachsen-Anhalt", @@ -59,10 +60,13 @@ func main() { mux.HandleFunc("POST /add-tag/", view.AddTag(db, store)) mux.HandleFunc("POST /add-user/", view.AddUser(db, store)) - mux.HandleFunc("POST /finish-article/", view.FinishArticle(db, store)) mux.HandleFunc("POST /login/", view.Login(db, store)) - mux.HandleFunc("POST /review-article/", view.ReviewArticle(db, store)) mux.HandleFunc("POST /publish-article/", view.PublishArticle(db, store)) + mux.HandleFunc("POST /reject-article/", view.RejectArticle(db, store)) + mux.HandleFunc("POST /resubmit-article/", view.ResubmitArticle(db, store)) + mux.HandleFunc("POST /review-rejected-article/", view.ReviewRejectedArticle(db, store)) + mux.HandleFunc("POST /review-unpublished-article/", view.ReviewUnpublishedArticle(db, store)) + mux.HandleFunc("POST /submit-article/", view.SubmitArticle(db, store)) log.Fatalln(http.ListenAndServe(":8080", mux)) } diff --git a/cmd/model/articles.go b/cmd/model/articles.go index 4f0fe18..6c4f75c 100644 --- a/cmd/model/articles.go +++ b/cmd/model/articles.go @@ -8,12 +8,13 @@ import ( func (db *DB) AddArticle(a *Article) (int64, error) { query := ` INSERT INTO articles - (title, description, content, published, author_id) + (title, description, content, published, rejected, author_id) VALUES - (?, ?, ?, ?, ?) + (?, ?, ?, ?, ?, ?) ` - result, err := db.Exec(query, a.Title, a.Desc, a.Content, a.Published, a.AuthorID) + result, err := db.Exec(query, a.Title, a.Description, a.Content, a.Published, + a.Rejected, a.AuthorID) if err != nil { return 0, fmt.Errorf("error inserting article into DB: %v", err) } @@ -37,7 +38,7 @@ func (db *DB) GetArticle(id int64) (*Article, error) { var created []byte var err error - if err := row.Scan(&article.Title, &created, &article.Desc, + if err := row.Scan(&article.Title, &created, &article.Description, &article.Content, &article.Published, &article.AuthorID); err != nil { return nil, fmt.Errorf("error scanning article row: %v", err) } @@ -51,13 +52,14 @@ func (db *DB) GetArticle(id int64) (*Article, error) { return article, nil } -func (db *DB) GetCertainArticles(published bool) ([]*Article, error) { +func (db *DB) GetCertainArticles(published, rejected bool) ([]*Article, error) { query := ` SELECT id, title, created, description, content, author_id FROM articles WHERE published = ? + AND rejected = ? ` - rows, err := db.Query(query, published) + rows, err := db.Query(query, published, rejected) if err != nil { return nil, fmt.Errorf("error querying articles: %v", err) } @@ -67,7 +69,7 @@ func (db *DB) GetCertainArticles(published bool) ([]*Article, error) { article := new(Article) var created []byte - if err = rows.Scan(&article.ID, &article.Title, &created, &article.Desc, + if err = rows.Scan(&article.ID, &article.Title, &created, &article.Description, &article.Content, &article.AuthorID); err != nil { return nil, fmt.Errorf("error scanning article row: %v", err) } diff --git a/cmd/model/db.go b/cmd/model/db.go index 7af4ee2..6b69788 100644 --- a/cmd/model/db.go +++ b/cmd/model/db.go @@ -4,6 +4,7 @@ import ( "bufio" "database/sql" "fmt" + "log" "os" "strings" "syscall" @@ -79,14 +80,28 @@ func OpenDB(dbName string) (*DB, error) { return &db, nil } -func (db *DB) UpdateAttribute(table string, id int64, attribute string, val interface{}) error { - query := fmt.Sprintf(` +func (db *DB) UpdateAttributes(a ...*Attribute) error { + tx, err := db.Begin() + if err != nil { + return fmt.Errorf("error starting transaction: %v", err) + } + + for _, attribute := range a { + query := fmt.Sprintf(` UPDATE %s SET %s = ? WHERE id = ? - `, table, attribute) - if _, err := db.Exec(query, val, id); err != nil { - return fmt.Errorf("error updating article in DB: %v", err) + `, attribute.Table, attribute.AttName) + if _, err := tx.Exec(query, attribute.Value, attribute.ID); err != nil { + if rollbackErr := tx.Rollback(); rollbackErr != nil { + log.Fatalf("error: transaction error: %v, rollback error: %v", err, rollbackErr) + } + return fmt.Errorf("error updating article in DB: %v", err) + } + } + + if err = tx.Commit(); err != nil { + return fmt.Errorf("error committing transaction: %v", err) } return nil } diff --git a/cmd/model/structs.go b/cmd/model/structs.go index 5b060ee..51bac18 100644 --- a/cmd/model/structs.go +++ b/cmd/model/structs.go @@ -11,12 +11,11 @@ const ( ) type User struct { - UserName string - FirstName string - LastName string - RejectedArticles []*Article - ID int64 - Role int + UserName string + FirstName string + LastName string + ID int64 + Role int } type Tag struct { @@ -25,11 +24,19 @@ type Tag struct { } type Article struct { - Title string - Created time.Time - Desc string - Content string - Published bool - ID int64 - AuthorID int64 + Title string + Created time.Time + Description string + Content string + Published bool + Rejected bool + ID int64 + AuthorID int64 +} + +type Attribute struct { + Value interface{} + Table string + AttName string + ID int64 } diff --git a/cmd/view/articles.go b/cmd/view/articles.go index d6e54e7..8f6dc85 100644 --- a/cmd/view/articles.go +++ b/cmd/view/articles.go @@ -10,7 +10,7 @@ import ( "streifling.com/jason/cpolis/cmd/model" ) -func ShowHub(s *control.CookieStore) http.HandlerFunc { +func ShowHub(db *model.DB, s *control.CookieStore) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { session, err := s.Get(r, "cookie") if err != nil { @@ -20,7 +20,7 @@ func ShowHub(s *control.CookieStore) http.HandlerFunc { } tmpl, err := template.ParseFiles("web/templates/hub.html") - template.Must(tmpl, err).ExecuteTemplate(w, "page-content", session.Values["role"]) + template.Must(tmpl, err).ExecuteTemplate(w, "page-content", session.Values["role"].(int)) } } @@ -38,32 +38,8 @@ func WriteArticle(db *model.DB) http.HandlerFunc { } } -func FinishArticle(db *model.DB, s *control.CookieStore) http.HandlerFunc { +func SubmitArticle(db *model.DB, s *control.CookieStore) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - article := new(model.Article) - var err error - - article.Title, err = control.ConvertToPlain(r.PostFormValue("editor-title")) - if err != nil { - log.Println(err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - article.Desc, err = control.ConvertToPlain(r.PostFormValue("editor-desc")) - if err != nil { - log.Println(err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - article.Content, err = control.ConvertToHTML(r.PostFormValue("editor-text")) - if err != nil { - log.Println(err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - session, err := s.Get(r, "cookie") if err != nil { tmpl, err := template.ParseFiles("web/templates/login.html") @@ -71,7 +47,15 @@ func FinishArticle(db *model.DB, s *control.CookieStore) http.HandlerFunc { template.Must(tmpl, err).ExecuteTemplate(w, "page-content", msg) } - article.AuthorID = session.Values["id"].(int64) + article := &model.Article{ + Title: r.PostFormValue("article-title"), + Description: r.PostFormValue("article-description"), + Content: r.PostFormValue("article-content"), + Published: false, + Rejected: false, + AuthorID: session.Values["id"].(int64), + } + article.ID, err = db.AddArticle(article) if err != nil { log.Println(err) @@ -102,20 +86,74 @@ func FinishArticle(db *model.DB, s *control.CookieStore) http.HandlerFunc { } } -func ShowUnpublishedArticles(db *model.DB) http.HandlerFunc { +func ResubmitArticle(db *model.DB, s *control.CookieStore) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - articles, err := db.GetCertainArticles(false) + id, err := strconv.ParseInt(r.PostFormValue("article-id"), 10, 64) if err != nil { log.Println(err) http.Error(w, err.Error(), http.StatusInternalServerError) return } - tmpl, err := template.ParseFiles("web/templates/unpublished-articles.html") - template.Must(tmpl, err).ExecuteTemplate(w, "page-content", articles) + + title := r.PostFormValue("article-title") + description := r.PostFormValue("article-description") + content := r.PostFormValue("article-content") + + if err := db.UpdateAttributes( + &model.Attribute{Table: "articles", ID: id, AttName: "title", Value: title}, + &model.Attribute{Table: "articles", ID: id, AttName: "description", Value: description}, + &model.Attribute{Table: "articles", ID: id, AttName: "content", Value: content}, + &model.Attribute{Table: "articles", ID: id, AttName: "rejected", Value: false}, + ); err != nil { + log.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + session, err := s.Get(r, "cookie") + if err != nil { + tmpl, err := template.ParseFiles("web/templates/login.html") + msg := "Session nicht mehr gültig. Bitte erneut anmelden." + template.Must(tmpl, err).ExecuteTemplate(w, "page-content", msg) + } + + tmpl, err := template.ParseFiles("web/templates/hub.html") + tmpl = template.Must(tmpl, err) + tmpl.ExecuteTemplate(w, "page-content", session.Values["role"]) } } -func ReviewArticle(db *model.DB, s *control.CookieStore) http.HandlerFunc { +func ShowUnpublishedArticles(db *model.DB) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + unpublishedArticles, err := db.GetCertainArticles(false, false) + if err != nil { + log.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + tmpl, err := template.ParseFiles("web/templates/unpublished-articles.html") + tmpl = template.Must(tmpl, err) + tmpl.ExecuteTemplate(w, "page-content", unpublishedArticles) + } +} + +func ShowRejectedArticles(db *model.DB, s *control.CookieStore) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + rejectedArticles, err := db.GetCertainArticles(false, true) + if err != nil { + log.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + tmpl, err := template.ParseFiles("web/templates/rejected-articles.html") + tmpl = template.Must(tmpl, err) + tmpl.ExecuteTemplate(w, "page-content", rejectedArticles) + } +} + +func ReviewUnpublishedArticle(db *model.DB, s *control.CookieStore) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { id, err := strconv.ParseInt(r.PostFormValue("id"), 10, 64) if err != nil { @@ -131,19 +169,34 @@ func ReviewArticle(db *model.DB, s *control.CookieStore) http.HandlerFunc { return } - // session, err := s.Get(r, "cookie") - // if err != nil { - // tmpl, err := template.ParseFiles("web/templates/login.html") - // msg := "Session nicht mehr gültig. Bitte erneut anmelden." - // template.Must(tmpl, err).ExecuteTemplate(w, "page-content", msg) - // } - tmpl, err := template.ParseFiles("web/templates/to-be-published.html") tmpl = template.Must(tmpl, err) tmpl.ExecuteTemplate(w, "page-content", article) } } +func ReviewRejectedArticle(db *model.DB, s *control.CookieStore) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + id, err := strconv.ParseInt(r.PostFormValue("id"), 10, 64) + if err != nil { + log.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + article, err := db.GetArticle(id) + if err != nil { + tmpl, err := template.ParseFiles("web/templates/to-be-published.html") + template.Must(tmpl, err).ExecuteTemplate(w, "page-content", article) + return + } + + tmpl, err := template.ParseFiles("web/templates/rework-article.html") + tmpl = template.Must(tmpl, err) + tmpl.ExecuteTemplate(w, "page-content", article) + } +} + func PublishArticle(db *model.DB, s *control.CookieStore) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { id, err := strconv.ParseInt(r.PostFormValue("id"), 10, 64) @@ -160,7 +213,36 @@ func PublishArticle(db *model.DB, s *control.CookieStore) http.HandlerFunc { template.Must(tmpl, err).ExecuteTemplate(w, "page-content", msg) } - db.UpdateAttribute("articles", id, "published", true) + db.UpdateAttributes( + &model.Attribute{Table: "articles", ID: id, AttName: "published", Value: true}, + &model.Attribute{Table: "articles", ID: id, AttName: "rejected", Value: false}, + ) + + tmpl, err := template.ParseFiles("web/templates/hub.html") + tmpl = template.Must(tmpl, err) + tmpl.ExecuteTemplate(w, "page-content", session.Values["role"]) + } +} + +func RejectArticle(db *model.DB, s *control.CookieStore) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + id, err := strconv.ParseInt(r.PostFormValue("id"), 10, 64) + if err != nil { + log.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + session, err := s.Get(r, "cookie") + if err != nil { + tmpl, err := template.ParseFiles("web/templates/login.html") + msg := "Session nicht mehr gültig. Bitte erneut anmelden." + template.Must(tmpl, err).ExecuteTemplate(w, "page-content", msg) + } + + db.UpdateAttributes( + &model.Attribute{Table: "articles", ID: id, AttName: "rejected", Value: true}, + ) tmpl, err := template.ParseFiles("web/templates/hub.html") tmpl = template.Must(tmpl, err) diff --git a/cmd/view/rss.go b/cmd/view/rss.go index 1c1b2c1..a97775a 100644 --- a/cmd/view/rss.go +++ b/cmd/view/rss.go @@ -7,6 +7,7 @@ import ( "time" "git.streifling.com/jason/rss" + "streifling.com/jason/cpolis/cmd/control" "streifling.com/jason/cpolis/cmd/model" ) @@ -19,7 +20,7 @@ func ShowRSS(db *model.DB, title, link, desc string) http.HandlerFunc { Items: make([]*rss.Item, 0), } - articles, err := db.GetCertainArticles(true) + articles, err := db.GetCertainArticles(true, false) if err != nil { log.Println(err) http.Error(w, err.Error(), http.StatusInternalServerError) @@ -45,12 +46,33 @@ func ShowRSS(db *model.DB, title, link, desc string) http.HandlerFunc { return } + articleTitle, err := control.ConvertToPlain(article.Title) + if err != nil { + log.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + articleDescription, err := control.ConvertToPlain(article.Description) + if err != nil { + log.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + articleContent, err := control.ConvertToHTML(article.Content) + if err != nil { + log.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + channel.Items = append(channel.Items, &rss.Item{ - Title: article.Title, + Title: articleTitle, Author: user.FirstName + user.LastName, PubDate: article.Created.Format(time.RFC1123Z), - Description: article.Desc, - Content: &rss.Content{Value: article.Content}, + Description: articleDescription, + Content: &rss.Content{Value: articleContent}, Categories: tagNames, }) } diff --git a/web/templates/editor.html b/web/templates/editor.html index 6401141..bb45aea 100644 --- a/web/templates/editor.html +++ b/web/templates/editor.html @@ -1,17 +1,13 @@ {{define "page-content"}}

Editor

- - - + + + {{range .}} {{end}} - +
{{end}} - -{{define "html-result"}} -{{.}} -{{end}} diff --git a/web/templates/hub.html b/web/templates/hub.html index c73d737..81bd86e 100644 --- a/web/templates/hub.html +++ b/web/templates/hub.html @@ -1,6 +1,7 @@ {{define "page-content"}}

Hub

+ {{if lt . 2}} diff --git a/web/templates/rejected-articles.html b/web/templates/rejected-articles.html new file mode 100644 index 0000000..934a973 --- /dev/null +++ b/web/templates/rejected-articles.html @@ -0,0 +1,10 @@ +{{define "page-content"}} +
+ {{range .}} + + + {{end}} + +
+ +{{end}} diff --git a/web/templates/rework-article.html b/web/templates/rework-article.html new file mode 100644 index 0000000..2e81a6e --- /dev/null +++ b/web/templates/rework-article.html @@ -0,0 +1,10 @@ +{{define "page-content"}} +

Editor

+
+ + + + + +
+{{end}} diff --git a/web/templates/to-be-published.html b/web/templates/to-be-published.html index d84202b..f3c3f46 100644 --- a/web/templates/to-be-published.html +++ b/web/templates/to-be-published.html @@ -1,8 +1,8 @@ {{define "page-content"}}

{{.Title}}

-

{{.Desc}}

- {{.Content}} +

{{.Description}}

+ {{.Content}} diff --git a/web/templates/unpublished-articles.html b/web/templates/unpublished-articles.html index ad9d468..d66c5e8 100644 --- a/web/templates/unpublished-articles.html +++ b/web/templates/unpublished-articles.html @@ -4,7 +4,7 @@ {{end}} - +
{{end}}