Compare commits

..

65 Commits

Author SHA1 Message Date
118fbd79b3 Corrected error messages for CliArgs 2024-03-29 09:48:03 +01:00
5e18ba52ad Added DBName into CliArgs 2024-03-29 09:16:41 +01:00
1d91d0c216 Added ability to upload media and parse cli arguments 2024-03-29 09:07:17 +01:00
f49fc8ca0b Generate RSS to file 2024-03-28 12:51:33 +01:00
2ff9cb1051 Cleaned up templates 2024-03-28 08:41:38 +01:00
780daac675 Fixed bug not showing correct issue in RSS feed 2024-03-28 07:41:11 +01:00
32d80e87a0 Fixed bug in publishing issue 2024-03-28 07:34:36 +01:00
e82a0c6abe Add ability to update tags when resubmitting article 2024-03-28 07:29:49 +01:00
548d2c6023 Incorporated issues 2024-03-28 07:00:37 +01:00
509e1d41db Corrected copyright 2024-03-28 06:59:39 +01:00
63d5fbd127 Disabled option to do transaction from view 2024-03-28 06:58:59 +01:00
f5c1a5e8d0 Added copyright 2024-03-17 15:29:12 +01:00
40d1104a66 Set pubDate to published time and date 2024-03-17 09:41:09 +01:00
736673c33f Now everyone only sees their own rejected articles 2024-03-17 09:15:37 +01:00
1a2e18359c Added ability to view tags when rejecting and change tags when reworking articles 2024-03-17 08:46:49 +01:00
d1d3a81565 Implemented retry logic on all transactions 2024-03-15 18:37:24 +01:00
bc2302d31f Implement retry logic for UpdateAttributes 2024-03-15 15:18:02 +01:00
1b02e3fad2 Added logout 2024-03-12 20:27:39 +01:00
dc06a6e9aa Fixed dumb routing mistake 2024-03-12 19:56:22 +01:00
63370ada01 Added ability to edit user info 2024-03-11 21:08:27 +01:00
5145e1cd94 Added ability to reject and rework article 2024-03-10 15:03:46 +01:00
28eb7e236a Split up db.go into multiple files 2024-03-09 11:06:03 +01:00
4a7883f886 Also missed rss.go 2024-03-09 10:27:55 +01:00
b69a0688ef Missed main when converting to MVC 2024-03-09 10:27:04 +01:00
0e18c7a7db Changed everything to MVC 2024-03-09 10:25:20 +01:00
3ab9a8fe10 Converted RSS feed to be DB based 2024-03-09 10:12:46 +01:00
6dfd49a5cd Reachieve basic functionality 2024-03-07 20:11:28 +01:00
5455bdb12b Articles and tags are now inserted into DB correctly 2024-03-07 15:31:00 +01:00
9eaafdc712 Converted articles and tags to DB base 2024-03-06 20:53:17 +01:00
d127be4c93 Handle rollbackError with log.Fatalf() 2024-03-06 16:58:41 +01:00
34832e397f Corrected transaction for ChangePassword() 2024-03-06 16:51:08 +01:00
2c0b4878f4 Use transaction when necessary 2024-03-06 15:37:59 +01:00
f0e078c011 Load *ArticleList, *Taglist and *Channel correctly 2024-03-06 15:37:42 +01:00
a8fc28af36 Moved main.go into cmd 2024-03-05 18:26:50 +01:00
e4f085f762 Added Tags to RSS feed as categories 2024-03-05 18:20:34 +01:00
6136f83dfc Converted RSS package to git.streifling.com/jason/rss 2024-03-05 17:13:59 +01:00
21170c9cc2 A bit of cleaning up 2024-03-05 16:38:18 +01:00
6502aa7ec1 Added partial support for tags 2024-03-03 13:56:49 +01:00
e5bdc235b6 Initial sessions implementation 2024-03-03 09:16:49 +01:00
655992c8b2 Just a bit of cleaning up 2024-03-02 09:09:55 +01:00
aa7fcd6075 Created func for minimum spec for rss and article structs, thereby crushing an annoying bug that was caused by not initializing channels but waiting for messages to go through them 2024-03-02 00:28:42 +01:00
f9cc90a948 Changed articles and rss to channels 2024-03-01 21:01:38 +01:00
7f2433c30b Implemented proper User struct 2024-03-01 12:25:53 +01:00
f34efc95dd Added ability to publish articles 2024-03-01 11:30:31 +01:00
935d0a1ca4 Added article list for written but non-published articles 2024-02-27 14:10:27 +01:00
48d4d482b2 Convert title and description to plain text 2024-02-27 09:03:21 +01:00
8dae3ca21e Implemented hub 2024-02-24 15:31:33 +01:00
6f02852212 Add ability to display feed 2024-02-24 14:49:29 +01:00
4cc2110c4b Added messages and field memory for adding user 2024-02-24 13:25:32 +01:00
04cbee097c Require all fields to be filled out when creating a new user 2024-02-24 12:10:34 +01:00
93423ae606 Implemented logging to file 2024-02-24 11:41:01 +01:00
41113b24a8 Check if user already exists and bug fix 2024-02-24 10:56:12 +01:00
2247f316a3 Added ability to add user 2024-02-24 10:28:12 +01:00
9beedf9b2b Added ability to login 2024-02-24 09:54:25 +01:00
7d6f96a185 Check user credentials before adding user 2024-02-22 20:12:09 +01:00
8d47146a7c Added ability to update Passwords 2024-02-22 19:27:41 +01:00
4853184ba1 Added ability to add user 2024-02-22 18:49:51 +01:00
50895249df Changed error messages 2024-02-22 15:23:29 +01:00
6e91253908 Added HTML sanitizer 2024-02-22 15:22:45 +01:00
9bb6010319 Added initial support for MySQL databases 2024-02-18 16:37:13 +01:00
75a0af055c Handle misssed errors for encoding and decoding feeds 2024-02-18 14:31:28 +01:00
171a0dd250 Added description and a way to save and restore the RSS feed. 2024-02-18 14:01:06 +01:00
372882a252 Create RSS from HTML 2024-02-18 12:41:49 +01:00
2d0b53a254 Show HTML on website 2024-02-18 10:48:37 +01:00
2447f50bac First implementation of web based editor to HTML pipeline 2024-02-18 10:07:49 +01:00
20 changed files with 144 additions and 364 deletions

View File

@@ -1,44 +0,0 @@
root = "."
testdata_dir = "testdata"
tmp_dir = "tmp"
[build]
args_bin = []
bin = "./tmp/main -key tmp/key.gob -log tmp/cpolis.log -pics tmp/pics -rss tmp/orientexpress_alle.rss -web web"
cmd = "go build -o ./tmp/main ./cmd/main.go"
delay = 0
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html", "css"]
include_file = []
kill_delay = "0s"
log = "build-errors.log"
poll = false
poll_interval = 0
rerun = false
rerun_delay = 500
send_interrupt = false
stop_on_error = false
[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"
[log]
main_only = false
time = false
[misc]
clean_on_exit = false
[screen]
clear_on_rebuild = false
keep_scroll = true

1
.gitignore vendored
View File

@@ -23,4 +23,3 @@ go.work
# Custom stuff # Custom stuff
tmp/ tmp/
style.css

View File

@@ -10,9 +10,7 @@ type CliArgs struct {
DBName string DBName string
KeyFile string KeyFile string
LogFile string LogFile string
Port string
PicsDir string PicsDir string
RSSFile string
WebDir string WebDir string
} }
@@ -20,11 +18,9 @@ func HandleCliArgs() (*CliArgs, error) {
var err error var err error
cliArgs := new(CliArgs) cliArgs := new(CliArgs)
keyFile := flag.String("key", "/var/www/cpolis/cpolis.key", "key file") keyFile := flag.String("key", "/var/www/cpolis.key", "key file")
logFile := flag.String("log", "/var/log/cpolis.log", "log file") logFile := flag.String("log", "/var/log/cpolis.log", "log file")
picsDir := flag.String("pics", "/var/www/cpolis/pics", "pictures directory") picsDir := flag.String("pics", "/var/www/cpolis/pics", "pictures directory")
port := flag.Int("port", 8080, "port")
rssFile := flag.String("rss", "/var/www/cpolis/cpolis.rss", "RSS file")
webDir := flag.String("web", "/var/www/cpolis/web", "web directory") webDir := flag.String("web", "/var/www/cpolis/web", "web directory")
flag.StringVar(&cliArgs.DBName, "db", "cpolis", "DB name") flag.StringVar(&cliArgs.DBName, "db", "cpolis", "DB name")
flag.Parse() flag.Parse()
@@ -33,24 +29,14 @@ func HandleCliArgs() (*CliArgs, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("error finding absolute path for KeyFile: %v", err) return nil, fmt.Errorf("error finding absolute path for KeyFile: %v", err)
} }
cliArgs.LogFile, err = filepath.Abs(*logFile) cliArgs.LogFile, err = filepath.Abs(*logFile)
if err != nil { if err != nil {
return nil, fmt.Errorf("error finding absolute path for LogFile: %v", err) return nil, fmt.Errorf("error finding absolute path for LogFile: %v", err)
} }
cliArgs.PicsDir, err = filepath.Abs(*picsDir) cliArgs.PicsDir, err = filepath.Abs(*picsDir)
if err != nil { if err != nil {
return nil, fmt.Errorf("error finding absolute path for PicsDir: %v", err) return nil, fmt.Errorf("error finding absolute path for PicsDir: %v", err)
} }
cliArgs.Port = fmt.Sprint(":", *port)
cliArgs.RSSFile, err = filepath.Abs(*rssFile)
if err != nil {
return nil, fmt.Errorf("error finding absolute path for RSSFile: %v", err)
}
cliArgs.WebDir, err = filepath.Abs(*webDir) cliArgs.WebDir, err = filepath.Abs(*webDir)
if err != nil { if err != nil {
return nil, fmt.Errorf("error finding absolute path for WebDir: %v", err) return nil, fmt.Errorf("error finding absolute path for WebDir: %v", err)

View File

@@ -54,14 +54,8 @@ func main() {
mux.HandleFunc("GET /edit-user/", view.EditUser(args, db, store)) mux.HandleFunc("GET /edit-user/", view.EditUser(args, db, store))
mux.HandleFunc("GET /hub/", view.ShowHub(args, db, store)) mux.HandleFunc("GET /hub/", view.ShowHub(args, db, store))
mux.HandleFunc("GET /logout/", view.Logout(args, store)) mux.HandleFunc("GET /logout/", view.Logout(args, store))
mux.HandleFunc("GET /publish-issue/", mux.HandleFunc("GET /publish-issue/", view.PublishLatestIssue(args, db, store))
view.PublishLatestIssue(args, db, store)) mux.HandleFunc("GET /rejected-articles/", view.ShowRejectedArticles(args, db, store))
mux.HandleFunc("GET /rejected-articles/",
view.ShowRejectedArticles(args, db, store))
mux.HandleFunc("GET /review-rejected-article/{id}/",
view.ReviewRejectedArticle(args, db, store))
mux.HandleFunc("GET /review-unpublished-article/{id}/",
view.ReviewUnpublishedArticle(args, db, store))
mux.HandleFunc("GET /rss/", view.ShowRSS(args, mux.HandleFunc("GET /rss/", view.ShowRSS(args,
db, db,
"Freimaurer Distrikt Niedersachsen und Sachsen-Anhalt", "Freimaurer Distrikt Niedersachsen und Sachsen-Anhalt",
@@ -69,22 +63,20 @@ func main() {
"Freiheit, Gleichheit, Brüderlichkeit, Toleranz und Humanität", "Freiheit, Gleichheit, Brüderlichkeit, Toleranz und Humanität",
)) ))
mux.HandleFunc("GET /this-issue/", view.ShowCurrentArticles(args, db)) mux.HandleFunc("GET /this-issue/", view.ShowCurrentArticles(args, db))
mux.HandleFunc("GET /unpublished-articles/", mux.HandleFunc("GET /unpublished-articles/", view.ShowUnpublishedArticles(args, db))
view.ShowUnpublishedArticles(args, db))
mux.HandleFunc("GET /write-article/", view.WriteArticle(args, db)) mux.HandleFunc("GET /write-article/", view.WriteArticle(args, db))
mux.HandleFunc("POST /add-tag/", view.AddTag(args, db, store)) mux.HandleFunc("POST /add-tag/", view.AddTag(args, db, store))
mux.HandleFunc("POST /add-user/", view.AddUser(args, db, store)) mux.HandleFunc("POST /add-user/", view.AddUser(args, db, store))
mux.HandleFunc("POST /login/", view.Login(args, db, store)) mux.HandleFunc("POST /login/", view.Login(args, db, store))
mux.HandleFunc("POST /publish-article/{id}/", mux.HandleFunc("POST /publish-article/", view.PublishArticle(args, db, store))
view.PublishArticle(args, db, store)) mux.HandleFunc("POST /reject-article/", view.RejectArticle(args, db, store))
mux.HandleFunc("POST /reject-article/{id}/", mux.HandleFunc("POST /resubmit-article/", view.ResubmitArticle(args, db, store))
view.RejectArticle(args, db, store)) mux.HandleFunc("POST /review-rejected-article/", view.ReviewRejectedArticle(args, db, store))
mux.HandleFunc("POST /resubmit-article/{id}/", mux.HandleFunc("POST /review-unpublished-article/", view.ReviewUnpublishedArticle(args, db, store))
view.ResubmitArticle(args, db, store))
mux.HandleFunc("POST /submit-article/", view.SubmitArticle(args, db, store)) mux.HandleFunc("POST /submit-article/", view.SubmitArticle(args, db, store))
mux.HandleFunc("POST /update-user/", view.UpdateUser(args, db, store)) mux.HandleFunc("POST /update-user/", view.UpdateUser(args, db, store))
mux.HandleFunc("POST /upload-image/", view.UploadImage(args)) mux.HandleFunc("POST /upload-image/", view.UploadImage(args))
log.Fatalln(http.ListenAndServe(args.Port, mux)) log.Fatalln(http.ListenAndServe(":8080", mux))
} }

View File

@@ -92,7 +92,7 @@ func SubmitArticle(c *control.CliArgs, db *model.DB, s *control.CookieStore) htt
func ResubmitArticle(c *control.CliArgs, db *model.DB, s *control.CookieStore) http.HandlerFunc { func ResubmitArticle(c *control.CliArgs, db *model.DB, s *control.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) id, err := strconv.ParseInt(r.PostFormValue("article-id"), 10, 64)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
@@ -202,7 +202,7 @@ func ReviewUnpublishedArticle(c *control.CliArgs, db *model.DB, s *control.Cooki
} }
data := new(htmlData) data := new(htmlData)
id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) id, err := strconv.ParseInt(r.PostFormValue("id"), 10, 64)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
@@ -238,7 +238,7 @@ func ReviewRejectedArticle(c *control.CliArgs, db *model.DB, s *control.CookieSt
} }
data := new(htmlData) data := new(htmlData)
id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) id, err := strconv.ParseInt(r.PostFormValue("id"), 10, 64)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
@@ -278,7 +278,7 @@ func ReviewRejectedArticle(c *control.CliArgs, db *model.DB, s *control.CookieSt
func PublishArticle(c *control.CliArgs, db *model.DB, s *control.CookieStore) http.HandlerFunc { func PublishArticle(c *control.CliArgs, db *model.DB, s *control.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) id, err := strconv.ParseInt(r.PostFormValue("id"), 10, 64)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
@@ -319,7 +319,7 @@ func PublishArticle(c *control.CliArgs, db *model.DB, s *control.CookieStore) ht
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
if err = control.SaveRSS(c.RSSFile, feed); err != nil { if err = control.SaveRSS("tmp/orientexpress_alle.rss", feed); err != nil {
log.Println(err) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
@@ -333,7 +333,7 @@ func PublishArticle(c *control.CliArgs, db *model.DB, s *control.CookieStore) ht
func RejectArticle(c *control.CliArgs, db *model.DB, s *control.CookieStore) http.HandlerFunc { func RejectArticle(c *control.CliArgs, db *model.DB, s *control.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) id, err := strconv.ParseInt(r.PostFormValue("id"), 10, 64)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)

View File

@@ -1,8 +0,0 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./web/templates/*.html"],
theme: {
extend: {}
},
plugins: [],
}

View File

@@ -1,41 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
width: 800px;
@apply mx-auto text-slate-900;
}
h2 {
@apply font-bold mb-2 text-2xl;
}
form {
@apply flex flex-col gap-y-3;
}
input[type="file"] {
@apply border rounded-md w-full;
}
input[type="password"],
input[type="text"] {
@apply border h-8 rounded-md;
}
textarea {
@apply border h-32 rounded-md;
}
.btn-area {
@apply flex gap-4 mt-4;
}
.btn {
@apply bg-slate-50 border my-2 px-3 py-2 rounded-md w-full hover:bg-slate-100;
}
.action-btn {
@apply bg-slate-800 border my-2 px-3 py-2 rounded-md text-slate-50 w-full hover:bg-slate-700;
}

0
web/static/css/style.css Normal file
View File

View File

@@ -1,12 +1,10 @@
{{define "page-content"}} {{define "page-content"}}
<h2>Neuer Tag</h2> <h2>Neuer Benutzer</h2>
<form> <form>
<input required name="tag" placeholder="Tag eingeben" type="text" /> <input required name="tag" placeholder="Tag" type="text" />
<div class="btn-area"> <input type="submit" value="Anlegen" hx-post="/add-tag/" hx-target="#page-content" />
<input class="action-btn" type="submit" value="Anlegen" hx-post="/add-tag/" hx-target="#page-content" />
<button class="btn" hx-get="/hub/" hx-target="#page-content">Abbrechen</button>
</div>
</form> </form>
<button hx-get="/hub/" hx-target="#page-content">Abbrechen</button>
{{end}} {{end}}

View File

@@ -1,54 +1,33 @@
{{define "page-content"}} {{define "page-content"}}
<h2>Neuer Benutzer</h2> <h2>Neuer Benutzer</h2>
<form> <form>
<div class="grid grid-cols-3 gap-4">
<div> <div>
<label for="username">Benutzername</label> <input required name="username" placeholder="Benutzername" type="text" value="{{.UserName}}" />
<input class="w-full" required name="username" type="text" value="{{.UserName}}" /> <input required name="password" placeholder="Passwort" type="password" />
</div> <input required name="password2" placeholder="Passwort wiederholen" type="password" />
<div> </div>
<label for="password">Passwort</label>
<input class="w-full" required name="password" placeholder="***" type="password" /> <div>
</div> <input required name="first-name" placeholder="Vorname" type="text" value="{{.FirstName}}" />
<div> <input required name="last-name" placeholder="Nachname" type="text" value="{{.LastName}}" />
<label for="password2">Passwort wiederholen</label>
<input class="w-full" required name="password2" placeholder="***" type="password" />
</div>
<div>
<label for="first-name">Vorname</label>
<input class="w-full" required name="first-name" type="text" value="{{.FirstName}}" />
</div>
<div>
<label for="last-name">Nachname</label>
<input class="w-full" required name="last-name" type="text" value="{{.LastName}}" />
</div>
</div> </div>
<div class="flex gap-4">
<div> <div>
<input required id="author" name="role" type="radio" value="3" {{if eq .Role 3 }}checked{{end}} /> <input required id="author" name="role" type="radio" value="3" {{if eq .Role 3 }}checked{{end}} />
<label for="author">Autor</label> <label for="author">Autor</label>
</div>
<div>
<input required id="editor" name="role" type="radio" value="2" {{if eq .Role 2 }}checked{{end}} /> <input required id="editor" name="role" type="radio" value="2" {{if eq .Role 2 }}checked{{end}} />
<label for="editor">Redakteur</label> <label for="editor">Redakteur</label>
</div>
<div>
<input required id="publisher" name="role" type="radio" value="1" {{if eq .Role 1 }}checked{{end}} /> <input required id="publisher" name="role" type="radio" value="1" {{if eq .Role 1 }}checked{{end}} />
<label for="publisher">Herausgeber</label> <label for="publisher">Herausgeber</label>
</div>
<div>
<input required id="admin" name="role" type="radio" value="0" {{if eq .Role 0 }}checked{{end}} /> <input required id="admin" name="role" type="radio" value="0" {{if eq .Role 0 }}checked{{end}} />
<label for="admin">Admin</label> <label for="admin">Admin</label>
</div> </div>
</div>
<div class="btn-area"> <input type="submit" value="Anlegen" hx-post="/add-user/" hx-target="#page-content" />
<input class="action-btn" type="submit" value="Anlegen" hx-post="/add-user/" hx-target="#page-content" />
<button class="btn" hx-get="/hub/" hx-target="#page-content">Abbrechen</button>
</div>
</form> </form>
<button hx-get="/hub/" hx-target="#page-content">Abbrechen</button>
<script> <script>
var msg = "{{.Msg}}"; var msg = "{{.Msg}}";
if (msg != "") { if (msg != "") {

View File

@@ -1,15 +1,13 @@
{{define "page-content"}} {{define "page-content"}}
<div class="flex flex-col gap-4"> <div>
{{range .}} {{range .}}
<div class="border px-2 py-1 rounded-md"> <div>
<h1 class="font-bold text-2xl">{{.Title}}</h1> <h1>{{.Title}}</h1>
<p>{{.Description}}</p> <p>{{.Description}}</p>
</div> </div>
{{end}} {{end}}
</div> </div>
<div class="btn-area"> <button hx-get="/publish-issue/" hx-target="#page-content">Ausgabe publizieren</button>
<button class="action-btn" hx-get="/publish-issue/" hx-target="#page-content">Ausgabe publizieren</button> <button hx-get="/hub/" hx-target="#page-content">Abbrechen</button>
<button class="btn" hx-get="/hub/" hx-target="#page-content">Abbrechen</button>
</div>
{{end}} {{end}}

View File

@@ -1,36 +1,19 @@
{{define "page-content"}} {{define "page-content"}}
<form> <form>
<div class="grid grid-cols-3 gap-4">
<div> <div>
<label for="username">Benutzername</label> <input name="username" type="text" value="{{.UserName}}" />
<input class="w-full" name="username" type="text" value="{{.UserName}}" /> <input name="first-name" type="text" value="{{.FirstName}}" />
</div> <input name="last-name" type="text" value="{{.LastName}}" />
<div>
<label for="first-name">Vorname</label>
<input class="w-full" name="first-name" type="text" value="{{.FirstName}}" />
</div>
<div>
<label for="last-name">Nachname</label>
<input class="w-full" name="last-name" type="text" value="{{.LastName}}" />
</div>
<div>
<label for="old-password">Altes Passwort</label>
<input class="w-full" name="old-password" placeholder="***" type="password" />
</div>
<div>
<label for="password">Passwort</label>
<input class="w-full" name="password" placeholder="***" type="password" />
</div>
<div>
<label for="password2">Passwort wiederholen</label>
<input class="w-full" name="password2" placeholder="***" type="password" />
</div>
</div> </div>
<div class="btn-area"> <div>
<input class="action-btn" type="submit" value="Aktualisieren" hx-post="/update-user/" <input name="old-password" placeholder="Altes Passwort" type="password" />
hx-target="#page-content" /> <input name="password" placeholder="Neues Passwort" type="password" />
<button class="btn" hx-get="/hub/" hx-target="#page-content">Abbrechen</button> <input name="password2" placeholder="Wiederholen" type="password" />
</div> </div>
<input type="submit" value="Aktualisieren" hx-post="/update-user/" hx-target="#page-content" />
</form> </form>
<button hx-get="/hub/" hx-target="#page-content">Abbrechen</button>
{{end}} {{end}}

View File

@@ -1,20 +1,13 @@
{{define "page-content"}} {{define "page-content"}}
<h2>Editor</h2> <h2>Editor</h2>
<form> <form>
<div class="flex flex-col gap-y-1"> <div>
<label for="article-title">Titel</label> <input name="article-title" placeholder="Titel" type="text" />
<input name="article-title" type="text" /> <textarea name="article-description" placeholder="Beschreibung"></textarea>
</div> <textarea name="article-content" placeholder="Artikel"></textarea>
<div class="flex flex-col">
<label for="article-description">Beschreibung</label>
<textarea name="article-description"></textarea>
</div>
<div class="flex flex-col">
<label for="article-content">Artikel</label>
<textarea name="article-content"></textarea>
</div> </div>
<div class="flex gap-4"> <div>
{{range .}} {{range .}}
<div> <div>
<input id="{{.Name}}" name="tags" type="checkbox" value="{{.ID}}" /> <input id="{{.Name}}" name="tags" type="checkbox" value="{{.ID}}" />
@@ -24,16 +17,15 @@
</div> </div>
<div id="editor-images"> <div id="editor-images">
<input class="mb-2" name="article-image" type="file" hx-encoding="multipart/form-data" hx-post="/upload-image/" <input name="article-image" type="file" hx-encoding="multipart/form-data" hx-post="/upload-image/"
hx-swap="beforeend" hx-target="#editor-images" /> hx-swap="beforeend" hx-target="#editor-images" />
</div> </div>
<div class="btn-area"> <input type="submit" value="Senden" hx-post="/submit-article/" hx-target="#page-content" />
<input class="action-btn" type="submit" value="Senden" hx-post="/submit-article/" hx-target="#page-content" />
<button class="btn" hx-get="/hub/" hx-target="#page-content">Abbrechen</button>
</div>
</form> </form>
<button hx-get="/hub/" hx-target="#page-content">Abbrechen</button>
<script> <script>
function copyToClipboard(text) { function copyToClipboard(text) {
event.preventDefault(); // Get-Request verhindern event.preventDefault(); // Get-Request verhindern
@@ -57,10 +49,9 @@
{{define "editor-images"}} {{define "editor-images"}}
{{if gt (len .) 0}} {{if gt (len .) 0}}
<div class="border px-2 py-1 rounded-md flex gap-4 justify-between"> <div>
<div class="self-center">{{.}}</div> {{.}}
<button class="bg-slate-50 border my-2 px-3 py-2 rounded-md w-32 hover:bg-slate-100" <button onclick="copyToClipboard('{{.}}')">Kopieren</button>
onclick="copyToClipboard('{{.}}')">Kopieren</button>
</div> </div>
{{end}} {{end}}
{{end}} {{end}}

View File

@@ -1,40 +1,25 @@
{{define "page-content"}} {{define "page-content"}}
<div class="flex flex-col gap-4"> <h2>Hub</h2>
<div class="mb-3"> <div>
<h2>Autor</h2> <button hx-get="/write-article/" hx-target="#page-content">Artikel schreiben</button>
<div class="grid grid-cols-2 gap-x-4 gap-y-2"> <button hx-get="/rejected-articles/" hx-target="#page-content">Abgelehnte Artikel</button>
<button class="btn" hx-get="/write-article/" hx-target="#page-content">Artikel schreiben</button> <button hx-get="/rss/" hx-target="#page-content">RSS Feed</button>
<button class="btn" hx-get="/rejected-articles/" hx-target="#page-content">Abgelehnte Artikel</button> <button hx-get="/edit-user/" hx-target="#page-content">Benutzer bearbeiten</button>
<button class="btn" hx-get="/rss/" hx-target="#page-content">RSS Feed</button>
<button class="btn" hx-get="/edit-user/" hx-target="#page-content">Benutzer bearbeiten</button>
</div>
</div> </div>
{{if lt . 3}} {{if lt . 3}}
<div class="mb-3"> <div>
<h2>Redakteur</h2> <button hx-get="/unpublished-articles/" hx-target="#page-content">Unveröffentlichte Artikel</button>
<div class="grid grid-cols-2 gap-4"> <button hx-get="/create-tag/" hx-target="#page-content">Neuer Tag</button>
<button class="btn" hx-get="/unpublished-articles/" hx-target="#page-content">
Unveröffentlichte Artikel
</button>
<button class="btn" hx-get="/create-tag/" hx-target="#page-content">Neuer Tag</button>
</div>
</div> </div>
{{end}} {{end}}
{{if lt . 2}} {{if lt . 2}}
<div class="mb-3"> <div>
<h2>Herausgeber</h2> <button hx-get="/this-issue/" hx-target="#page-content">Diese Ausgabe</button>
<div class="grid grid-cols-2 gap-4">
<button class="btn" hx-get="/this-issue/" hx-target="#page-content">Diese Ausgabe</button>
</div>
</div> </div>
{{end}} {{end}}
{{if eq . 0}} {{if eq . 0}}
<div class="mb-3"> <div>
<h2>Administrator</h2> <button hx-get="/create-user/" hx-target="#page-content">Benutzer hinzufügen</button>
<div class="grid grid-cols-2 gap-4">
<button class="btn" hx-get="/create-user/" hx-target="#page-content">Benutzer hinzufügen</button>
</div>
</div> </div>
{{end}} {{end}}
</div>
{{end}} {{end}}

View File

@@ -5,13 +5,13 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>Orient Editor</title> <title>Orient Editor</title>
<link href="/web/static/css/style.css" rel="stylesheet"> <link href="web/static/css/style.css" rel="stylesheet">
</head> </head>
<body class="flex flex-col justify-between min-h-[100dvh] bg-slate-50"> <body>
<header class="my-8"> <header>
<h1 class="font-bold text-4xl text-center">Orient Editor</h1> <h1>Orient Editor</h1>
<button class="btn" hx-get="logout" hx-target="#page-content">Abmelden</button> <button hx-get="logout" hx-target="#page-content">Abmelden</button>
</header> </header>
<main> <main>
@@ -19,13 +19,11 @@
{{template "page-content" .}} {{template "page-content" .}}
</div> </div>
<script src="/web/static/js/htmx.min.js"></script> <script src="web/static/js/htmx.min.js"></script>
</main> </main>
<footer class="my-8"> <footer>
<p class="text-center text-gray-500 dark:text-gray-400"> <p>&copy; 2024 Jason Streifling. Alle Rechte vorbehalten.</p>
&copy; 2024 Jason Streifling. Alle Rechte vorbehalten.
</p>
</footer> </footer>
</body> </body>

View File

@@ -1,11 +1,11 @@
{{define "page-content"}} {{define "page-content"}}
<h2>Anmeldung</h2> <h2>Anmeldung</h2>
<form> <form>
<div class="btn-area"> <div>
<input class="w-full" name="username" placeholder="Benutzername" type="text" /> <input name="username" placeholder="Benutzername" type="text" />
<input class="w-full" name="password" placeholder="Passwort" type="password" /> <input name="password" placeholder="Passwort" type="password" />
</div> </div>
<input class="action-btn" type="submit" value="Anmelden" hx-post="/login/" hx-target="#page-content" /> <input type="submit" value="Anmelden" hx-post="/login/" hx-target="#page-content" />
</form> </form>
{{end}} {{end}}

View File

@@ -1,14 +1,18 @@
{{define "page-content"}} {{define "page-content"}}
<div class="flex flex-col gap-4"> <form>
<div>
{{range .RejectedArticles}} {{range .RejectedArticles}}
<div>
{{if index $.MyIDs .ID}} {{if index $.MyIDs .ID}}
<button class="btn" hx-get="/review-rejected-article/{{.ID}}/" hx-target="#page-content"> <input required id="{{.ID}}" name="id" type="radio" value="{{.ID}}" />
<h1 class="font-bold text-2xl">{{.Title}}</h1> <label for="{{.ID}}">{{.Title}}</label>
<p>{{.Description}}</p>
</button>
{{end}} {{end}}
{{end}}
<button class="action-btn" hx-get="/hub/" hx-target="#page-content">Zurück</button>
</div> </div>
{{end}} {{end}}
</div>
<input type="submit" value="Auswählen" hx-post="/review-rejected-article/" hx-target="#page-content" />
</form>
<button hx-get="/hub/" hx-target="#page-content">Zurück</button>
{{end}}

View File

@@ -1,20 +1,14 @@
{{define "page-content"}} {{define "page-content"}}
<h2>Editor</h2> <h2>Editor</h2>
<form> <form>
<div class="flex flex-col gap-y-1"> <div>
<label for="article-title">Titel</label> <input name="article-title" placeholder="Titel" type="text" value="{{.Article.Title}}" />
<input name="article-title" type="text" value="{{.Article.Title}}" /> <textarea name="article-description" placeholder="Beschreibung">{{.Article.Description}}</textarea>
</div>
<div class="flex flex-col">
<label for="article-description">Beschreibung</label>
<textarea name="article-description">{{.Article.Description}}</textarea>
</div>
<div class="flex flex-col">
<label for="article-content">Artikel</label>
<textarea name="article-content" placeholder="Artikel">{{.Article.Content}}</textarea> <textarea name="article-content" placeholder="Artikel">{{.Article.Content}}</textarea>
<input name="article-id" type="hidden" value="{{.Article.ID}}" />
</div> </div>
<div class="flex gap-4"> <div>
{{range .Tags}} {{range .Tags}}
<div> <div>
<input id="tag-{{.Name}}" name="tags" type="checkbox" value="{{.ID}}" {{if index $.Selected <input id="tag-{{.Name}}" name="tags" type="checkbox" value="{{.ID}}" {{if index $.Selected
@@ -24,45 +18,8 @@
{{end}} {{end}}
</div> </div>
<div id="editor-images"> <input type="submit" value="Senden" hx-post="/resubmit-article/" hx-target="#page-content" />
<input class="mb-2" name="article-image" type="file" hx-encoding="multipart/form-data" hx-post="/upload-image/"
hx-swap="beforeend" hx-target="#editor-images" />
</div>
<div class="btn-area">
<input class="action-btn" type="submit" value="Senden" hx-post="/resubmit-article/{{.Article.ID}}/"
hx-target="#page-content" />
<button class="btn" hx-get="/hub/" hx-target="#page-content">Zurück</button>
</div>
</form> </form>
<script> <button hx-get="/hub/" hx-target="#page-content">Zurück</button>
function copyToClipboard(text) {
event.preventDefault(); // Get-Request verhindern
var textarea = document.createElement("textarea");
textarea.textContent = text;
document.body.appendChild(textarea);
textarea.select();
try {
document.execCommand('copy');
} catch (err) {
console.warn('Fehler beim Kopieren', err);
}
document.body.removeChild(textarea);
}
</script>
{{end}}
{{define "editor-images"}}
{{if gt (len .) 0}}
<div class="border px-2 py-1 rounded-md flex gap-4 justify-between">
<div class="self-center">{{.}}</div>
<button class="bg-slate-50 border my-2 px-3 py-2 rounded-md w-32 hover:bg-slate-100"
onclick="copyToClipboard('{{.}}')">Kopieren</button>
</div>
{{end}}
{{end}} {{end}}

View File

@@ -10,12 +10,10 @@
{{end}} {{end}}
</p> </p>
<div class="btn-area"> <input name="id" type="hidden" value="{{.Article.ID}}" />
<input class="action-btn" type="submit" value="Veröffentlichen" hx-post="/publish-article/{{.Article.ID}}/" <input type="submit" value="Veröffentlichen" hx-post="/publish-article/" hx-target="#page-content" />
hx-target="#page-content" /> <input type="submit" value="Ablehnen" hx-post="/reject-article/" hx-target="#page-content" />
<input class="btn" type="submit" value="Ablehnen" hx-post="/reject-article/{{.Article.ID}}/"
hx-target="#page-content" />
<button class="btn" hx-get="/hub/" hx-target="#page-content">Zurück</button>
</div>
</form> </form>
<button hx-get="/hub/" hx-target="#page-content">Zurück</button>
{{end}} {{end}}

View File

@@ -1,11 +1,16 @@
{{define "page-content"}} {{define "page-content"}}
<div class="flex flex-col gap-4"> <form>
<div>
{{range .}} {{range .}}
<button class="btn" hx-get="/review-unpublished-article/{{.ID}}/" hx-target="#page-content"> <div>
<h1 class="font-bold text-2xl">{{.Title}}</h1> <input required id="{{.ID}}" name="id" type="radio" value="{{.ID}}" />
<p>{{.Description}}</p> <label for="{{.ID}}">{{.Title}}</label>
</button>
{{end}}
<button class="btn" hx-get="/hub/" hx-target="#page-content">Zurück</button>
</div> </div>
{{end}} {{end}}
</div>
<input type="submit" value="Auswählen" hx-post="/review-unpublished-article/" hx-target="#page-content" />
</form>
<button hx-get="/hub/" hx-target="#page-content">Zurück</button>
{{end}}