Compare commits

..

36 Commits

Author SHA1 Message Date
e3c192359f Merge branch 'devel' 2024-09-11 18:15:31 +02:00
c777a77824 Change version number 2024-09-11 18:15:27 +02:00
46532e4c85 Merge branch 'devel' 2024-09-11 18:15:07 +02:00
c183043dac Fix bug with wrong port 2024-09-11 18:14:54 +02:00
c722135a56 Merge branch 'devel' 2024-09-11 17:18:42 +02:00
391b3bf157 Change version number 2024-09-11 17:18:35 +02:00
0aa479763d Add .btn-area-1 for btn-areas with one button 2024-09-11 17:18:11 +02:00
ff0e229f03 Downgrade go version and dependencies to hopefully fix bug on debian machines 2024-09-11 05:23:58 +02:00
8ef6ff729e Optimized Article struct size 2024-09-10 20:08:13 +02:00
e4624b8705 A bit of code cleanup 2024-09-10 19:59:56 +02:00
887fa863bc Merge branch 'devel' 2024-09-10 19:43:22 +02:00
4592bdf970 Fixed btn-area in to-be-published.html 2024-09-10 19:43:01 +02:00
dadd610b2d Fixed bug that made it possible for an article's content to disappear when reworking it 2024-09-10 19:31:34 +02:00
74d71cfb6a Merge branch 'devel' 2024-09-09 22:03:43 +02:00
4004bcb8f0 Make interface responsive 2024-09-09 22:03:31 +02:00
9e0182ed03 Remove white background from to-be-deleted.html and to-be-published.html because it conflicts with dark mode 2024-09-09 22:03:03 +02:00
e554174c28 Correct size of tag field in add-tag.html 2024-09-09 22:01:26 +02:00
8597f1b849 Recover somehow deleted port command line argument in .air.toml 2024-09-09 21:08:53 +02:00
b04e0e5e81 Rename cover in current-articles.html 2024-09-08 16:33:41 +02:00
ca7e7cddd3 Merge branch 'devel' 2024-09-08 16:22:59 +02:00
62921c8e2a Fix wrong version number 2024-09-08 16:22:28 +02:00
94431a2aa9 Merge branch 'devel' 2024-09-08 16:21:38 +02:00
c7761e2dc8 Fix differing border color and streamline current-articles.html 2024-09-08 16:21:26 +02:00
5b1f20c5bc Merge branch 'devel' 2024-09-08 13:36:22 +02:00
013cddc157 Streamline config and make config file variable 2024-09-08 13:35:30 +02:00
523ff9d2db Restructure hub.html to be mor organized 2024-09-08 11:00:44 +02:00
84960fdd44 Implement dark mode 2024-09-08 11:00:12 +02:00
d0c566f8df Merge branch 'devel' 2024-09-01 21:14:05 +02:00
0a387c1087 Remove unnecessery fmt.Println that was used for debugging 2024-09-01 21:13:52 +02:00
38caf02b2c Upgrade all dependencies 2024-09-01 20:56:30 +02:00
435157a42f Upgrade htmx version 2024-09-01 20:52:50 +02:00
4163afb0b7 Change footer in index.html to have version number on same line as warning 2024-09-01 20:51:35 +02:00
5e586aa49a Merge branch 'devel' 2024-09-01 18:51:33 +02:00
6cc0fb40d7 Add version number to index.html 2024-09-01 18:51:14 +02:00
66b2743d3d Merge branch 'devel' 2024-09-01 18:48:26 +02:00
18617f1dbc Give autogenerated articles tags as well 2024-09-01 18:48:18 +02:00
24 changed files with 310 additions and 171 deletions

View File

@ -5,13 +5,16 @@ tmp_dir = "tmp"
[build]
args_bin = [
"-articles tmp/articles",
"-config tmp/config.toml",
"-desc 'Freiheit, Gleichheit, Brüderlichkeit, Toleranz und Humanität'",
"-domain localhost",
"-firebase tmp/firebase.json",
"-key tmp/key.gob",
"-link https://distrikt-ni-st.de",
"-log tmp/cpolis.log",
"-pdfs tmp/pdfs",
"-pics tmp/pics",
"-port 8080",
"-rss tmp/orientexpress_alle.rss",
"-title 'Freimaurer Distrikt Niedersachsen und Sachsen-Anhalt'",
"-web web",

View File

@ -9,18 +9,18 @@ import (
)
type Article struct {
Title string
Created time.Time
Title string
Description string
Link string
EncURL string
EncLength int
EncType string
Published bool
Rejected bool
ID int64
AuthorID int64
IssueID int64
EncLength int
Published bool
Rejected bool
IsInIssue bool
AutoGenerated bool
}
@ -49,7 +49,6 @@ 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.IsInIssue, a.AutoGenerated)

View File

@ -3,6 +3,7 @@ package backend
import (
"flag"
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"
@ -12,6 +13,7 @@ import (
type Config struct {
ArticleDir string
ConfigFile string
DBName string
Description string
Domain string
@ -30,56 +32,72 @@ type Config struct {
func newConfig() *Config {
return &Config{
ArticleDir: "/var/www/cpolis/articles",
ConfigFile: "/etc/cpolis/config.toml",
DBName: "cpolis",
FirebaseKey: "/var/www/cpolis/serviceAccountKey.json",
KeyFile: "/var/www/cpolis/cpolis.key",
LogFile: "/var/log/cpolis.log",
PDFDir: "/var/www/cpolis/pdfs",
PicsDir: "/var/www/cpolis/pics",
Port: ":8080",
RSSFile: "/var/www/cpolis/cpolis.rss",
WebDir: "/var/www/cpolis/web",
}
}
func (c *Config) readFile() error {
cfgFile, err := filepath.Abs(os.Getenv("HOME") + "/.config/cpolis/config.toml")
func mkDir(path string, perm fs.FileMode) (string, error) {
var err error
stringSlice := strings.Split(path, "/")
name := stringSlice[len(stringSlice)-1]
path, err = filepath.Abs(path)
if err != nil {
return fmt.Errorf("error getting absolute path for config file: %v", err)
return "", fmt.Errorf("error finding absolute path for %v directory: %v", name, err)
}
if err = os.MkdirAll(path, perm); err != nil {
return "", fmt.Errorf("error creating %v directory: %v", name, err)
}
_, err = os.Stat(cfgFile)
return path, nil
}
func mkFile(path string, filePerm, dirPerm fs.FileMode) (string, error) {
var err error
path, err = filepath.Abs(path)
if err != nil {
return "", fmt.Errorf("error finding absolute path for %v: %v", path, err)
}
stringSlice := strings.Split(path, "/")
_, err = os.Stat(path)
if os.IsNotExist(err) {
fileStrings := strings.Split(cfgFile, "/")
dir := strings.Join(fileStrings[0:len(fileStrings)-1], "/")
if err = os.MkdirAll(dir, 0755); err != nil {
return fmt.Errorf("error creating config directory: %v", err)
dir := strings.Join(stringSlice[:len(stringSlice)-1], "/")
if err = os.MkdirAll(dir, dirPerm); err != nil {
return "", fmt.Errorf("error creating %v: %v", dir, err)
}
fileName := fileStrings[len(fileStrings)-1]
fileName := stringSlice[len(stringSlice)-1]
file, err := os.Create(dir + "/" + fileName)
if err != nil {
return fmt.Errorf("error creating config file: %v", err)
return "", fmt.Errorf("error creating %v: %v", fileName, err)
}
defer file.Close()
if err = file.Chmod(0644); err != nil {
return fmt.Errorf("error setting permissions for config file: %v", err)
}
} else {
_, err = toml.DecodeFile(cfgFile, c)
if err != nil {
return fmt.Errorf("error reading config file: %v", err)
if err = file.Chmod(filePerm); err != nil {
return "", fmt.Errorf("error setting permissions for %v: %v", fileName, err)
}
}
return nil
return path, nil
}
func (c *Config) handleCliArgs() error {
var port int
var err error
port := 8080
flag.StringVar(&c.ArticleDir, "articles", c.ArticleDir, "articles directory")
flag.StringVar(&c.ConfigFile, "config", c.ConfigFile, "config file")
flag.StringVar(&c.DBName, "db", c.DBName, "DB name")
flag.StringVar(&c.Description, "desc", c.Description, "channel description")
flag.StringVar(&c.Domain, "domain", c.Domain, "domain name")
@ -95,58 +113,117 @@ func (c *Config) handleCliArgs() error {
flag.IntVar(&port, "port", port, "port")
flag.Parse()
c.ArticleDir, err = filepath.Abs(c.ArticleDir)
if err != nil {
return fmt.Errorf("error finding absolute path for articles directory: %v", err)
}
if err = os.MkdirAll(c.ArticleDir, 0755); err != nil {
return fmt.Errorf("error creating articles directory: %v", err)
}
c.FirebaseKey, err = filepath.Abs(c.FirebaseKey)
if err != nil {
return fmt.Errorf("error finding absolute path for Firebase service account key file: %v", err)
}
c.KeyFile, err = filepath.Abs(c.KeyFile)
if err != nil {
return fmt.Errorf("error finding absolute path for key file: %v", err)
}
c.LogFile, err = filepath.Abs(c.LogFile)
if err != nil {
return fmt.Errorf("error finding absolute path for log file: %v", err)
}
c.PDFDir, err = filepath.Abs(c.PDFDir)
if err != nil {
return fmt.Errorf("error finding absolute path for pdfs directory: %v", err)
}
if err = os.MkdirAll(c.PDFDir, 0755); err != nil {
return fmt.Errorf("error creating pdfs directory: %v", err)
}
c.PicsDir, err = filepath.Abs(c.PicsDir)
if err != nil {
return fmt.Errorf("error finding absolute path for pics directory: %v", err)
}
if err = os.MkdirAll(c.PicsDir, 0755); err != nil {
return fmt.Errorf("error creating pics directory: %v", err)
}
if port != 0 {
c.Port = fmt.Sprint(":", port)
c.RSSFile, err = filepath.Abs(c.RSSFile)
if err != nil {
return fmt.Errorf("error finding absolute path for RSS file: %v", err)
}
c.WebDir, err = filepath.Abs(c.WebDir)
c.ConfigFile, err = mkFile(c.ConfigFile, 0600, 0700)
if err != nil {
return fmt.Errorf("error finding absolute path for web directory: %v", err)
return fmt.Errorf("error setting up file: %v", err)
}
if err = os.MkdirAll(c.WebDir, 0755); err != nil {
return fmt.Errorf("error creating web directory: %v", err)
return nil
}
func (c *Config) handleFile(configFile string) error {
_, err := toml.DecodeFile(configFile, c)
if err != nil {
return fmt.Errorf("error reading config file: %v", err)
}
return nil
}
func (c *Config) setupConfig(cliConfig *Config) error {
var err error
defaultConfig := newConfig()
if cliConfig.ArticleDir != defaultConfig.ArticleDir {
c.ArticleDir = cliConfig.ArticleDir
}
c.ArticleDir, err = mkDir(c.ArticleDir, 0700)
if err != nil {
return fmt.Errorf("error setting up directory: %v", err)
}
if cliConfig.DBName != defaultConfig.DBName {
c.DBName = cliConfig.DBName
}
if cliConfig.Description != defaultConfig.Description {
c.Description = cliConfig.Description
}
if cliConfig.Domain != defaultConfig.Domain {
c.Domain = cliConfig.Domain
}
if cliConfig.FirebaseKey != defaultConfig.FirebaseKey {
c.FirebaseKey = cliConfig.FirebaseKey
}
c.FirebaseKey, err = mkFile(c.FirebaseKey, 0600, 0700)
if err != nil {
return fmt.Errorf("error setting up file: %v", err)
}
if cliConfig.KeyFile != defaultConfig.KeyFile {
c.KeyFile = cliConfig.KeyFile
}
c.KeyFile, err = mkFile(c.KeyFile, 0600, 0700)
if err != nil {
return fmt.Errorf("error setting up file: %v", err)
}
if cliConfig.Link != defaultConfig.Link {
c.Link = cliConfig.Link
}
if cliConfig.LogFile != defaultConfig.LogFile {
c.LogFile = cliConfig.LogFile
}
c.LogFile, err = mkFile(c.LogFile, 0600, 0700)
if err != nil {
return fmt.Errorf("error setting up file: %v", err)
}
if cliConfig.PDFDir != defaultConfig.PDFDir {
c.PDFDir = cliConfig.PDFDir
}
c.PDFDir, err = mkDir(c.PDFDir, 0700)
if err != nil {
return fmt.Errorf("error setting up directory: %v", err)
}
if cliConfig.PicsDir != defaultConfig.PicsDir {
c.PicsDir = cliConfig.PicsDir
}
c.PicsDir, err = mkDir(c.PicsDir, 0700)
if err != nil {
return fmt.Errorf("error setting up directory: %v", err)
}
if cliConfig.Port != defaultConfig.Port {
c.Port = cliConfig.Port
}
if cliConfig.RSSFile != defaultConfig.RSSFile {
c.RSSFile = cliConfig.RSSFile
}
c.RSSFile, err = mkFile(c.RSSFile, 0600, 0700)
if err != nil {
return fmt.Errorf("error setting up file: %v", err)
}
if cliConfig.Title != defaultConfig.Title {
c.Title = cliConfig.Title
}
if cliConfig.WebDir != defaultConfig.WebDir {
c.WebDir = cliConfig.WebDir
}
c.WebDir, err = mkDir(c.WebDir, 0700)
if err != nil {
return fmt.Errorf("error setting up directory: %v", err)
}
return nil
@ -154,13 +231,19 @@ func (c *Config) handleCliArgs() error {
func HandleConfig() (*Config, error) {
config := newConfig()
cliConfig := newConfig()
if err := config.readFile(); err != nil {
return nil, fmt.Errorf("error reading config file: %v", err)
if err := cliConfig.handleCliArgs(); err != nil {
return nil, fmt.Errorf("error handling cli arguments: %v", err)
}
if err := config.handleCliArgs(); err != nil {
return nil, fmt.Errorf("error handling cli arguments: %v", err)
err := config.handleFile(cliConfig.ConfigFile)
if err != nil {
return nil, fmt.Errorf("error reading configuration file: %v", err)
}
if err = config.setupConfig(cliConfig); err != nil {
return nil, fmt.Errorf("error setting up files: %v", err)
}
return config, nil

View File

@ -8,9 +8,7 @@ import (
"google.golang.org/api/option"
)
type Client struct {
*auth.Client
}
type Client struct{ *auth.Client }
func NewClient(c *Config) (*Client, error) {
var err error

View File

@ -32,9 +32,12 @@ func GenerateRSS(c *Config, db *DB) (*string, error) {
tagNames = append(tagNames, tag.Name)
}
if article.IsInIssue {
if article.IsInIssue || article.AutoGenerated {
tagNames = append(tagNames, fmt.Sprint("Orient Express ", article.IssueID))
}
if article.AutoGenerated {
tagNames = append(tagNames, "autogenerated")
}
user, err := db.GetUser(article.AuthorID)
if err != nil {

View File

@ -10,9 +10,7 @@ import (
"github.com/gorilla/sessions"
)
type CookieStore struct {
sessions.CookieStore
}
type CookieStore struct{ sessions.CookieStore }
func NewKey() ([]byte, error) {
key := make([]byte, 32)

View File

@ -29,7 +29,7 @@ func WriteArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return
}
type editorHTMLData struct {
type htmlData struct {
Title string
Description string
Content string
@ -37,12 +37,12 @@ func WriteArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
Tags []*b.Tag
Mode int
}
var data htmlData
var data editorHTMLData
if session.Values["article"] == nil {
data = editorHTMLData{}
data = htmlData{}
} else {
data = session.Values["article"].(editorHTMLData)
data = session.Values["article"].(htmlData)
}
data.Mode = EditMode
@ -211,11 +211,10 @@ func ShowRejectedArticles(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerF
return
}
type htmlData struct {
data := new(struct {
MyIDs map[int64]bool
RejectedArticles []*b.Article
}
data := new(htmlData)
})
data.RejectedArticles, err = db.GetCertainArticles(false, true)
if err != nil {
@ -530,16 +529,14 @@ func ReviewArticleForDeletion(c *b.Config, db *b.DB, s *b.CookieStore) http.Hand
return
}
type htmlData struct {
var err error
data := new(struct {
Title string
Description string
Content template.HTML
Tags []*b.Tag
ID int64
}
var err error
data := new(htmlData)
})
data.ID, err = strconv.ParseInt(r.PathValue("id"), 10, 64)
if err != nil {

View File

@ -69,7 +69,6 @@ func PublishLatestIssue(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFun
AuthorID: session.Values["id"].(int64),
AutoGenerated: true,
}
fmt.Println(article.Link)
article.ID, err = db.AddArticle(article)
if err != nil {

View File

@ -14,19 +14,16 @@ import (
func UploadPDF(c *b.Config, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
fmt.Println("Content-Type:", r.Header.Get("Content-Type"))
if _, err := getSession(w, r, c, s); err != nil {
return
}
fmt.Println(1)
if err := r.ParseMultipartForm(10 << 20); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
fmt.Println(2)
file, _, err := r.FormFile("pdf-upload")
if err != nil {
log.Println(err)
@ -35,7 +32,6 @@ func UploadPDF(c *b.Config, s *b.CookieStore) http.HandlerFunc {
}
defer file.Close()
fmt.Println(3)
filename := fmt.Sprint(uuid.New(), ".pdf")
absFilepath, err := filepath.Abs(fmt.Sprint(c.PDFDir, "/", filename))
if err != nil {
@ -44,7 +40,6 @@ func UploadPDF(c *b.Config, s *b.CookieStore) http.HandlerFunc {
return
}
fmt.Println(4)
pdf, err := os.Create(absFilepath)
if err != nil {
log.Println(err)
@ -53,14 +48,12 @@ func UploadPDF(c *b.Config, s *b.CookieStore) http.HandlerFunc {
}
defer pdf.Close()
fmt.Println(5)
if _, err = io.Copy(pdf, file); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
fmt.Println(6)
w.WriteHeader(http.StatusOK)
}
}

View File

@ -277,12 +277,12 @@ func ShowAllUsers(c *b.Config, db *b.DB, s *b.CookieStore, action string) http.H
return
}
type htmlData struct {
data := new(struct {
Users map[int64]*b.User
Action string
}
})
data := &htmlData{Action: action}
data.Action = action
data.Users, err = db.GetAllUsers()
if err != nil {
log.Println(err)

View File

@ -7,4 +7,5 @@ module.exports = {
plugins: [
require('@tailwindcss/typography')
],
darkMode: 'selector',
}

View File

@ -2,40 +2,71 @@
@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;
h3 {
@apply font-bold mb-2 text-xl;
}
input[type="password"],
input[type="text"] {
@apply border h-8 rounded-md;
@apply bg-slate-50 dark:bg-slate-950 border border-slate-200 dark:border-slate-800 h-8 rounded-md;
}
textarea {
@apply border h-32 rounded-md;
@apply bg-slate-50 dark:bg-slate-950 border border-slate-200 dark:border-slate-800 h-32 rounded-md;
}
.btn-area-1 {
@apply grid grid-cols-1 gap-x-4 gap-y-1 mt-4;
}
.btn-area {
@apply flex gap-4 mt-4;
@apply grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-1 mt-4;
}
.btn-area-3 {
@apply grid grid-cols-1 md:grid-cols-3 gap-x-4 gap-y-1 mt-4;
}
.btn {
@apply bg-slate-200 border my-2 px-3 py-2 rounded-md w-full hover:bg-slate-100;
@apply bg-slate-200 dark:bg-slate-800 hover:bg-slate-100 dark:hover:bg-slate-900 border border-slate-200 dark:border-slate-800 my-2 px-3 py-2 rounded-md w-full;
}
.action-btn {
@apply bg-slate-800 border my-2 px-3 py-2 rounded-md text-slate-50 w-full hover:bg-slate-700;
@apply bg-slate-800 dark:bg-slate-200 hover:bg-slate-700 dark:hover:bg-slate-300 my-2 px-3 py-2 rounded-md text-slate-50 dark:text-slate-950 w-full;
}
.EasyMDEContainer .CodeMirror {
@apply bg-slate-50 dark:bg-slate-950 border-slate-200 dark:border-slate-800 text-slate-900 dark:text-slate-100
}
.EasyMDEContainer .cm-s-easymde .CodeMirror-cursor {
@apply border-slate-900 dark:border-slate-100
}
.EasyMDEContainer .editor-toolbar > * {
@apply text-slate-900 dark:text-slate-100
}
.EasyMDEContainer .editor-toolbar > .active, .editor-toolbar > button:hover, .editor-preview pre, .cm-s-easymde .cm-comment {
@apply bg-slate-100 dark:bg-slate-900
}
.EasyMDEContainer .CodeMirror-fullscreen {
@apply bg-slate-50 dark:bg-slate-950
}
.editor-toolbar {
@apply border border-slate-200 dark:border-slate-800
}
.editor-toolbar.fullscreen {
@apply bg-slate-50 dark:bg-slate-950
}
.editor-preview {
@apply bg-slate-50 dark:bg-slate-950
}

View File

@ -2,7 +2,7 @@
<h2>Neuer Tag</h2>
<form>
<input required name="tag" placeholder="Tag eingeben" type="text" />
<input class="w-full" name="tag" placeholder="Tag eingeben" required type="text" />
<div class="btn-area">
<input class="action-btn" type="submit" value="Anlegen" hx-post="/tag/add" hx-target="#page-content" />
<button class="btn" hx-get="/hub" hx-target="#page-content">Abbrechen</button>

View File

@ -2,7 +2,7 @@
<h2>Neuer Benutzer</h2>
<form>
<div class="grid grid-cols-3 gap-4">
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
<div>
<label for="username">Benutzername</label>
<input class="w-full" required name="username" type="text" value="{{.UserName}}" />
@ -25,7 +25,7 @@
</div>
</div>
<div class="flex gap-4">
<div class="flex flex-wrap gap-4">
<div>
<input required id="author" name="role" type="radio" value="3" {{if eq .Role 3 }}checked{{end}} />
<label for="author">Autor</label>

View File

@ -7,7 +7,7 @@
<h3>Aktuelle Artikel</h3>
<div class="flex flex-col gap-4">
{{range .}}
<div class="border px-2 py-1 rounded-md">
<div class="border border-slate-200 dark:border-slate-800 px-2 py-1 rounded-md">
<h1 class="font-bold text-2xl">{{.Title}}</h1>
<p>{{.Description}}</p>
</div>
@ -16,21 +16,15 @@
</div>
<div>
<h3>Cover</h3>
<div class="flex">
<h3>Titelseite</h3>
<div class="grid grid-cols-2 gap-4 items-center">
<input class="h-full" name="issue-title" placeholder="Titel" type="text" />
<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>
<h3>Über diese Ausgabe</h3>
<div>

View File

@ -2,7 +2,7 @@
<h2>Profil bearbeiten</h2>
<form>
<div class="grid grid-cols-3 gap-4">
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
<div>
<label for="username">Benutzername</label>
<input class="w-full" name="username" type="text" value="{{.UserName}}" />

View File

@ -2,7 +2,7 @@
<h2>Profil von {{.FirstName}} {{.LastName}} bearbeiten</h2>
<form>
<div class="grid grid-cols-3 gap-4">
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
<div>
<label for="username">Benutzername</label>
<input class="w-full" name="username" type="text" value="{{.UserName}}" />
@ -29,7 +29,7 @@
</div>
</div>
<div class="flex gap-4">
<div class="flex flex-wrap gap-4">
<div>
<input required id="author" name="role" type="radio" value="3" {{if eq .Role 3 }}checked{{end}} />
<label for="author">Autor</label>

View File

@ -25,7 +25,7 @@
</div>
</div>
<div class="btn-area">
<div class="btn-area-1">
<input class="action-btn" type="submit" value="Anlegen" hx-post="/user/add-first" hx-target="#page-content" />
</div>
</form>

View File

@ -4,49 +4,49 @@
{{if lt . 4}}
<div class="mb-3">
<h2>Autor</h2>
<div class="grid grid-cols-2 gap-x-4 gap-y-2">
<h2>Artikel</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-2">
<button class="btn" hx-get="/article/write" hx-target="#page-content">Artikel schreiben</button>
<button class="btn" hx-get="/article/all-rejected" hx-target="#page-content">Abgelehnte Artikel</button>
<button class="btn" hx-get="/user/edit/self" hx-target="#page-content">Profil bearbeiten</button>
</div>
</div>
{{end}}
{{if lt . 3}}
<div class="mb-3">
<h2>Redakteur</h2>
<div class="grid grid-cols-2 gap-x-4 gap-y-2">
<button class="btn" hx-get="/article/all-unpublished" hx-target="#page-content">
{{if lt . 3}}<button class="btn" hx-get="/article/all-unpublished" hx-target="#page-content">
Unveröffentlichte Artikel
</button>
<button class="btn" hx-get="/tag/create" hx-target="#page-content">Neuer Tag</button>
</button>{{end}}
{{if lt . 2}}<button class="btn" hx-get="/article/all-published" hx-target="#page-content">
Artikel löschen
</button>{{end}}
{{if lt . 3}}<button class="btn" hx-get="/tag/create" hx-target="#page-content">Neuer Tag</button>{{end}}
</div>
</div>
{{end}}
{{if lt . 2}}
<div class="mb-3">
<h2>Herausgeber</h2>
<div class="grid grid-cols-2 gap-x-4 gap-y-2">
<h2>Ausgabe</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-2">
<button class="btn" hx-get="/issue/this" hx-target="#page-content">Diese Ausgabe</button>
<button class="btn" hx-get="/article/all-published" hx-target="#page-content">Artikel löschen</button>
<form class="flex" hx-encoding="multipart/form-data">
<label class="btn text-center" for="pdf-upload">PDF hochladen</label>
<input accept=".pdf" class="hidden" id="pdf-upload" name="pdf-upload" type="file"
hx-post="/pdf/upload" />
</form>
</div>
</div>
{{end}}
</div>
{{if eq . 0}}
{{if lt . 4}}
<div class="mb-3">
<h2>Administrator</h2>
<div class="grid grid-cols-2 gap-x-4 gap-y-2">
<button class="btn" hx-get="/user/create" hx-target="#page-content">Benutzer hinzufügen</button>
<button class="btn" hx-get="/user/show-all/edit" hx-target="#page-content">Benutzer bearbeiten</button>
<button class="btn" hx-get="/user/show-all/delete" hx-target="#page-content">Benutzer löschen</button>
<h2>Benutzer</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-2">
<button class="btn" hx-get="/user/edit/self" hx-target="#page-content">Mein Profil bearbeiten</button>
{{if eq . 0}}<button class="btn" hx-get="/user/create" hx-target="#page-content">
Benutzer hinzufügen
</button>{{end}}
{{if eq . 0}}<button class="btn" hx-get="/user/show-all/edit" hx-target="#page-content">
Benutzer bearbeiten
</button>{{end}}
{{if eq . 0}}<button class="btn" hx-get="/user/show-all/delete" hx-target="#page-content">
Benutzer löschen
</button>{{end}}
</div>
</div>
{{end}}

View File

@ -5,13 +5,26 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Orient Editor</title>
<link href="/web/static/css/style.css" rel="stylesheet">
<link rel="stylesheet" href="https://unpkg.com/easymde/dist/easymde.min.css">
</head>
<body class="flex flex-col justify-between min-h-screen bg-slate-50">
<body
class="bg-slate-50 dark:bg-slate-950 container flex flex-col justify-between max-w-screen-lg min-h-screen mx-auto text-slate-900 dark:text-slate-100">
<header class="my-8">
<h1 class="font-bold text-4xl text-center">Orient Editor</h1>
<div class="ml-4">
<label class="cursor-pointer flex items-center relative" for="theme-toggle">
<div class="bg-slate-200 dark:bg-slate-800 block h-6 w-12 rounded-full"></div>
<div
class="absolute bg-slate-800 dark:bg-slate-50 dot left-1 top-1 h-4 w-4 rounded-full transform transition">
</div>
<span class="ml-2">Dunkel</span>
</label>
<input type="checkbox" id="theme-toggle" class="sr-only">
</div>
</header>
<main class="mx-4">
@ -20,17 +33,44 @@
</div>
</main>
<footer class="my-8">
<p class="text-center text-gray-500 dark:text-gray-400">
<footer class="text-center text-gray-500 my-8">
<p>
&copy; 2024 Jason Streifling. Alle Rechte vorbehalten.
</p>
<p class="text-center text-gray-500 dark:text-gray-400">
<strong>Hinweis:</strong> Diese Software befindet sich noch in der Entwicklung und kann Fehler enthalten.
<p>
v0.10.3 - <strong>Hinweis:</strong> Diese Software befindet sich noch in der Entwicklung und kann Fehler
enthalten.
</p>
</footer>
<script src="https://unpkg.com/htmx.org@2.0.1"></script>
<script src="https://unpkg.com/htmx.org@2.0.2"></script>
<script src="https://unpkg.com/easymde/dist/easymde.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
const toggleSwitch = document.getElementById('theme-toggle');
const dot = document.querySelector('.dot');
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark')
toggleSwitch.checked = true;
dot.classList.add('translate-x-6');
} else {
document.documentElement.classList.remove('dark')
}
toggleSwitch.addEventListener('change', () => {
if (toggleSwitch.checked) {
document.documentElement.classList.add('dark');
localStorage.theme = 'dark';
dot.classList.add('translate-x-6');
} else {
document.documentElement.classList.remove('dark');
localStorage.theme = 'light';
dot.classList.remove('translate-x-6');
}
});
});
</script>
</body>
</html>

View File

@ -2,11 +2,13 @@
<h2>Anmeldung</h2>
<form>
<div class="btn-area">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 my-1">
<input class="w-full" name="username" placeholder="Benutzername" type="text" />
<input class="w-full" name="password" placeholder="Passwort" type="password" />
</div>
<div class="mt-2">
<input class="action-btn" type="submit" value="Anmelden" hx-post="/login" hx-target="#page-content" />
</div>
</form>
{{end}}

View File

@ -15,7 +15,7 @@
<div class="flex flex-col gap-y-1">
<label for="easyMDE">Artikel</label>
<textarea id="easyMDE">{{.Content}}</textarea>
<input id="article-content" name="article-content" type="hidden" />
<input id="article-content" name="article-content" type="hidden" value="{{.Content}}" />
</div>
<div>
@ -44,8 +44,6 @@
</form>
<script>
document.getElementById('article-content').value = easyMDE.value();
var easyMDE = new EasyMDE({
element: document.getElementById('easyMDE'),
hideIcons: ['image'],

View File

@ -3,24 +3,24 @@
<div>
<span>Titel</span>
<div class="bg-white border mb-3 px-2 py-2 rounded-md w-full">
<div class="border border-slate-200 dark:border-slate-800 mb-3 px-2 py-2 rounded-md w-full">
{{.Title}}
</div>
<span>Beschreibung</span>
<div class="bg-white border mb-3 px-2 py-2 rounded-md w-full">
<div class="border border-slate-200 dark:border-slate-800 mb-3 px-2 py-2 rounded-md w-full">
{{.Description}}
</div>
<span>Artikel</span>
<div class="bg-white border mb-3 px-2 py-2 rounded-md w-full">
<div class="prose">
<div class="border border-slate-200 dark:border-slate-800 mb-3 px-2 py-2 rounded-md w-full">
<div class="prose text-slate-900 dark:text-slate-100">
{{.Content}}
</div>
</div>
<span>Tags</span>
<div class="bg-white border mb-3 px-2 py-2 rounded-md w-full">
<div class="border border-slate-200 dark:border-slate-800 mb-3 px-2 py-2 rounded-md w-full">
{{range .Tags}}
{{.Name}}
<br>

View File

@ -3,24 +3,24 @@
<div>
<span>Titel</span>
<div class="bg-white border mb-3 px-2 py-2 rounded-md w-full">
<div class="border border-slate-200 dark:border-slate-800 mb-3 px-2 py-2 rounded-md w-full">
{{.Article.Title}}
</div>
<span>Beschreibung</span>
<div class="bg-white border mb-3 px-2 py-2 rounded-md w-full">
<div class="border border-slate-200 dark:border-slate-800 mb-3 px-2 py-2 rounded-md w-full">
{{.Article.Description}}
</div>
<span>Artikel</span>
<div class="bg-white border mb-3 px-2 py-2 rounded-md w-full">
<div class="border border-slate-200 dark:border-slate-800 mb-3 px-2 py-2 rounded-md w-full">
<div class="prose">
{{.Content}}
</div>
</div>
<span>Tags</span>
<div class="bg-white border mb-3 px-2 py-2 rounded-md w-full">
<div class="border border-slate-200 dark:border-slate-800 mb-3 px-2 py-2 rounded-md w-full">
{{if .Article.IsInIssue}}
<span>Orient Express</span>
<br>
@ -31,7 +31,7 @@
{{end}}
</div>
<div class="btn-area">
<div class="btn-area-3">
<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}}"