forked from jason/cpolis
		
	Merge branch 'devel'
This commit is contained in:
		@@ -5,8 +5,10 @@ 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",
 | 
			
		||||
 
 | 
			
		||||
@@ -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",
 | 
			
		||||
		Port:        ":8080",
 | 
			
		||||
		PicsDir:     "/var/www/cpolis/pics",
 | 
			
		||||
		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)
 | 
			
		||||
	if os.IsNotExist(err) {
 | 
			
		||||
		fileStrings := strings.Split(cfgFile, "/")
 | 
			
		||||
	return path, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		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)
 | 
			
		||||
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) {
 | 
			
		||||
		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,114 @@ 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)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.Port = fmt.Sprint(":", port)
 | 
			
		||||
 | 
			
		||||
	c.RSSFile, err = filepath.Abs(c.RSSFile)
 | 
			
		||||
	c.ConfigFile, err = mkFile(c.ConfigFile, 0600, 0700)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("error finding absolute path for RSS file: %v", err)
 | 
			
		||||
		return fmt.Errorf("error setting up file: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.WebDir, err = filepath.Abs(c.WebDir)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Config) handleFile(configFile string) error {
 | 
			
		||||
	_, err := toml.DecodeFile(configFile, c)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("error finding absolute path for web directory: %v", err)
 | 
			
		||||
		return fmt.Errorf("error reading config 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) 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 +228,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
 | 
			
		||||
 
 | 
			
		||||
@@ -7,4 +7,5 @@ module.exports = {
 | 
			
		||||
    plugins: [
 | 
			
		||||
        require('@tailwindcss/typography')
 | 
			
		||||
    ],
 | 
			
		||||
    darkMode: 'selector',
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,30 +2,21 @@
 | 
			
		||||
@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 {
 | 
			
		||||
@@ -33,9 +24,41 @@ textarea {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.editor-toolbar.fullscreen {
 | 
			
		||||
    @apply bg-slate-50 dark:bg-slate-950
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.editor-preview {
 | 
			
		||||
    @apply bg-slate-50 dark:bg-slate-950
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,49 +4,49 @@
 | 
			
		||||
 | 
			
		||||
    {{if lt . 4}}
 | 
			
		||||
    <div class="mb-3">
 | 
			
		||||
        <h2>Autor</h2>
 | 
			
		||||
        <h2>Artikel</h2>
 | 
			
		||||
        <div class="grid 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>
 | 
			
		||||
        <h2>Ausgabe</h2>
 | 
			
		||||
        <div class="grid 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>
 | 
			
		||||
        {{end}}
 | 
			
		||||
    </div>
 | 
			
		||||
    {{end}}
 | 
			
		||||
 | 
			
		||||
    {{if eq . 0}}
 | 
			
		||||
    {{if lt . 4}}
 | 
			
		||||
    <div class="mb-3">
 | 
			
		||||
        <h2>Administrator</h2>
 | 
			
		||||
        <h2>Benutzer</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>
 | 
			
		||||
            <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}}
 | 
			
		||||
 
 | 
			
		||||
@@ -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 style="width: 800px;"
 | 
			
		||||
    class="bg-slate-50 dark:bg-slate-950 flex flex-col justify-between 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,18 +33,44 @@
 | 
			
		||||
        </div>
 | 
			
		||||
    </main>
 | 
			
		||||
 | 
			
		||||
    <footer class="text-center text-gray-500 dark:text-gray-400 my-8">
 | 
			
		||||
    <footer class="text-center text-gray-500 my-8">
 | 
			
		||||
        <p>
 | 
			
		||||
            © 2024 Jason Streifling. Alle Rechte vorbehalten.
 | 
			
		||||
        </p>
 | 
			
		||||
        <p>
 | 
			
		||||
            v0.8.3 - <strong>Hinweis:</strong> Diese Software befindet sich noch in der Entwicklung und kann Fehler
 | 
			
		||||
            v0.9.0 - <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.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>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user