mirror of
https://github.com/Theodor-Springmann-Stiftung/kgpz_web.git
synced 2025-10-28 16:45:32 +00:00
Added a lot of different things
This commit is contained in:
@@ -24,6 +24,6 @@ func GetYear(kgpz *app.KGPZ) fiber.Handler {
|
||||
return c.SendStatus(fiber.StatusNotFound)
|
||||
}
|
||||
|
||||
return c.Render("/", fiber.Map{"model": issues})
|
||||
return c.Render("/", fiber.Map{"model": issues, "title": "Ausgaben " + y})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ const (
|
||||
Category
|
||||
Issue
|
||||
Piece
|
||||
Unknown
|
||||
)
|
||||
|
||||
type ParseErrorLevel int64
|
||||
@@ -34,14 +35,14 @@ const (
|
||||
type ParseMessage struct {
|
||||
XMLType XMLEntityType
|
||||
XMLPath string
|
||||
Object fmt.Stringer
|
||||
Object string
|
||||
Message string
|
||||
MessageType ParseErrorLevel
|
||||
}
|
||||
|
||||
func (pm ParseMessage) String() string {
|
||||
if pm.Object != nil {
|
||||
return fmt.Sprintf("%s: %s\n%s\n%s", pm.XMLType, pm.XMLPath, pm.Object.String(), pm.Message)
|
||||
if pm.Object != "" {
|
||||
return fmt.Sprintf("%s: %s\n%s\n%s", pm.XMLType, pm.XMLPath, pm.Object, pm.Message)
|
||||
}
|
||||
return fmt.Sprintf("%s: %s\n%s", pm.XMLType, pm.XMLPath, pm.Message)
|
||||
}
|
||||
@@ -50,9 +51,9 @@ type ParseLogger struct {
|
||||
mu sync.Mutex
|
||||
ParseInfo chan ParseMessage
|
||||
ParseErrors chan ParseMessage
|
||||
ParseObjects chan fmt.Stringer
|
||||
ParseObjects chan string
|
||||
messages []ParseMessage
|
||||
objects []fmt.Stringer
|
||||
objects []string
|
||||
State ParseErrorLevel
|
||||
subs []func(ParseMessage)
|
||||
}
|
||||
@@ -116,7 +117,7 @@ func (pl *ParseLogger) ClearMessages() {
|
||||
pl.messages = []ParseMessage{}
|
||||
}
|
||||
|
||||
func (pl *ParseLogger) LogInfo(xmlType XMLEntityType, xmlPath string, object fmt.Stringer, message string) {
|
||||
func (pl *ParseLogger) LogInfo(xmlType XMLEntityType, xmlPath string, object string, message string) {
|
||||
pl.ParseInfo <- ParseMessage{
|
||||
XMLType: xmlType,
|
||||
XMLPath: xmlPath,
|
||||
@@ -126,7 +127,7 @@ func (pl *ParseLogger) LogInfo(xmlType XMLEntityType, xmlPath string, object fmt
|
||||
}
|
||||
}
|
||||
|
||||
func (pl *ParseLogger) LogError(xmlType XMLEntityType, xmlPath string, object fmt.Stringer, message string) {
|
||||
func (pl *ParseLogger) LogError(xmlType XMLEntityType, xmlPath string, object string, message string) {
|
||||
pl.ParseErrors <- ParseMessage{
|
||||
XMLType: xmlType,
|
||||
XMLPath: xmlPath,
|
||||
@@ -136,7 +137,7 @@ func (pl *ParseLogger) LogError(xmlType XMLEntityType, xmlPath string, object fm
|
||||
}
|
||||
}
|
||||
|
||||
func (pl *ParseLogger) LogWarning(xmlType XMLEntityType, xmlPath string, object fmt.Stringer, message string) {
|
||||
func (pl *ParseLogger) LogWarning(xmlType XMLEntityType, xmlPath string, object string, message string) {
|
||||
pl.ParseErrors <- ParseMessage{
|
||||
XMLType: xmlType,
|
||||
XMLPath: xmlPath,
|
||||
@@ -146,7 +147,7 @@ func (pl *ParseLogger) LogWarning(xmlType XMLEntityType, xmlPath string, object
|
||||
}
|
||||
}
|
||||
|
||||
func (pl *ParseLogger) LogFatal(xmlType XMLEntityType, xmlPath string, object fmt.Stringer, message string) {
|
||||
func (pl *ParseLogger) LogFatal(xmlType XMLEntityType, xmlPath string, object string, message string) {
|
||||
pl.ParseErrors <- ParseMessage{
|
||||
XMLType: xmlType,
|
||||
XMLPath: xmlPath,
|
||||
@@ -187,7 +188,7 @@ func (pl *ParseLogger) PrintObjects() {
|
||||
pl.mu.Lock()
|
||||
defer pl.mu.Unlock()
|
||||
for _, o := range pl.objects {
|
||||
ObjDebug(&o, "Object")
|
||||
Debug(o)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/Theodor-Springmann-Stiftung/kgpz_web/server"
|
||||
"github.com/Theodor-Springmann-Stiftung/kgpz_web/templating"
|
||||
"github.com/Theodor-Springmann-Stiftung/kgpz_web/views"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -46,7 +47,9 @@ func main() {
|
||||
kgpz := app.NewKGPZ(cfg)
|
||||
kgpz.Init()
|
||||
|
||||
engine := templating.NewEngine(&views.LayoutFS, &views.RoutesFS, kgpz)
|
||||
engine := templating.NewEngine(&views.LayoutFS, &views.RoutesFS)
|
||||
engine.Funcs(kgpz)
|
||||
engine.Globals(fiber.Map{"isDev": cfg.Config.Debug, "name": "KGPZ", "lang": "de"})
|
||||
|
||||
server := server.Create(kgpz, cfg, engine)
|
||||
Start(kgpz, server)
|
||||
|
||||
@@ -95,7 +95,7 @@ func (l *Library) Serialize(commit string) {
|
||||
wg.Wait()
|
||||
|
||||
go func() {
|
||||
l.Cleanup(commit)
|
||||
l.Cleanup()
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -110,11 +110,11 @@ func (l *Library) Prepare(commit string) {
|
||||
l.Pieces.Prepare(commit)
|
||||
}
|
||||
|
||||
func (l *Library) Cleanup(commit string) {
|
||||
l.Agents.Cleanup(commit)
|
||||
l.Places.Cleanup(commit)
|
||||
l.Works.Cleanup(commit)
|
||||
l.Categories.Cleanup(commit)
|
||||
l.Issues.Cleanup(commit)
|
||||
l.Pieces.Cleanup(commit)
|
||||
func (l *Library) Cleanup() {
|
||||
l.Agents.Cleanup()
|
||||
l.Places.Cleanup()
|
||||
l.Works.Cleanup()
|
||||
l.Categories.Cleanup()
|
||||
l.Issues.Cleanup()
|
||||
l.Pieces.Cleanup()
|
||||
}
|
||||
|
||||
@@ -56,10 +56,14 @@ func (p *XMLProvider[T]) Serialize(dataholder XMLRootElement[T], path string) er
|
||||
return fmt.Errorf("No commit set")
|
||||
}
|
||||
|
||||
p.mu.Lock()
|
||||
commit := &p.parses[len(p.parses)-1]
|
||||
p.mu.Unlock()
|
||||
|
||||
// Introduce goroutine for every path, locking on append:
|
||||
if err := UnmarshalFile(path, dataholder); err != nil {
|
||||
logging.Error(err, "Could not unmarshal file: "+path)
|
||||
logging.ParseMessages.ParseErrors <- logging.ParseMessage{MessageType: logging.ErrorMessage, Message: "Could not unmarshal file: " + path}
|
||||
logging.ParseMessages.LogError(logging.Unknown, path, "", "Could not unmarshal file.")
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.failed = append(p.failed, path)
|
||||
@@ -69,7 +73,7 @@ func (p *XMLProvider[T]) Serialize(dataholder XMLRootElement[T], path string) er
|
||||
for _, item := range dataholder.Children() {
|
||||
// INFO: Mostly it's just one ID, so the double loop is not that bad.
|
||||
for _, id := range item.GetIDs() {
|
||||
p.Infos.Store(id, ItemInfo{Source: path, Parse: &p.parses[len(p.parses)-1]})
|
||||
p.Infos.Store(id, ItemInfo{Source: path, Parse: commit})
|
||||
p.Items.Store(id, item)
|
||||
}
|
||||
}
|
||||
@@ -86,11 +90,11 @@ func (p *XMLProvider[T]) Cleanup() {
|
||||
return
|
||||
}
|
||||
|
||||
lastcommit := p.parses[len(p.parses)-1].Commit
|
||||
lastcommit := &p.parses[len(p.parses)-1]
|
||||
todelete := make([]string, 0)
|
||||
p.Infos.Range(func(key, value interface{}) bool {
|
||||
info := value.(ItemInfo)
|
||||
if info.Parse.Commit != lastcommit {
|
||||
if info.Parse != lastcommit {
|
||||
if !slices.Contains(p.failed, info.Source) {
|
||||
todelete = append(todelete, key.(string))
|
||||
}
|
||||
|
||||
@@ -130,6 +130,8 @@ func (s *Server) Start() {
|
||||
|
||||
srv.Use(recover.New())
|
||||
|
||||
srv.Use("assets", static(&views.StaticFS))
|
||||
|
||||
// TODO: Dont cache static assets, bc storage gets huge
|
||||
// INFO: Maybe fiber does this already?
|
||||
if s.Config.Debug {
|
||||
@@ -153,8 +155,6 @@ func (s *Server) Start() {
|
||||
}))
|
||||
}
|
||||
|
||||
srv.Use(STATIC_PREFIX, static(&views.StaticFS))
|
||||
|
||||
srv.Get("/:year?", controllers.GetYear(s.kgpz))
|
||||
srv.Get("/:year/:issue/:page?", controllers.GetIssue(s.kgpz))
|
||||
srv.Get("/:year/:issue/beilage/:page?", controllers.GetIssue(s.kgpz))
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/Theodor-Springmann-Stiftung/kgpz_web/app"
|
||||
"github.com/Theodor-Springmann-Stiftung/kgpz_web/functions"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type Engine struct {
|
||||
@@ -15,24 +16,24 @@ type Engine struct {
|
||||
LayoutRegistry *LayoutRegistry
|
||||
TemplateRegistry *TemplateRegistry
|
||||
|
||||
mu *sync.Mutex
|
||||
FuncMap template.FuncMap
|
||||
mu *sync.Mutex
|
||||
FuncMap template.FuncMap
|
||||
GlobalData fiber.Map
|
||||
}
|
||||
|
||||
// INFO: We pass the app here to be able to access the config and other data for functions
|
||||
// which also means we must reload the engine if the app changes
|
||||
func NewEngine(layouts, templates *fs.FS, app *app.KGPZ) *Engine {
|
||||
func NewEngine(layouts, templates *fs.FS) *Engine {
|
||||
e := Engine{
|
||||
mu: &sync.Mutex{},
|
||||
LayoutRegistry: NewLayoutRegistry(*layouts),
|
||||
TemplateRegistry: NewTemplateRegistry(*templates),
|
||||
}
|
||||
|
||||
e.MapFuncs(app)
|
||||
return &e
|
||||
}
|
||||
|
||||
func (e *Engine) MapFuncs(app *app.KGPZ) error {
|
||||
func (e *Engine) Funcs(app *app.KGPZ) error {
|
||||
e.mu.Lock()
|
||||
e.FuncMap = make(map[string]interface{})
|
||||
e.mu.Unlock()
|
||||
@@ -51,6 +52,18 @@ func (e *Engine) MapFuncs(app *app.KGPZ) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Engine) Globals(data fiber.Map) {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
if e.GlobalData == nil {
|
||||
e.GlobalData = data
|
||||
} else {
|
||||
for k, v := range data {
|
||||
(e.GlobalData)[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Engine) Load() error {
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(2)
|
||||
@@ -96,6 +109,14 @@ func (e *Engine) AddFunc(name string, fn interface{}) {
|
||||
|
||||
func (e *Engine) Render(out io.Writer, path string, data interface{}, layout ...string) error {
|
||||
// TODO: check if a reload is needed if files on disk have changed
|
||||
ld := data.(fiber.Map)
|
||||
gd := e.GlobalData
|
||||
if e.GlobalData != nil {
|
||||
for k, v := range ld {
|
||||
gd[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
var l *template.Template
|
||||
@@ -123,7 +144,7 @@ func (e *Engine) Render(out io.Writer, path string, data interface{}, layout ...
|
||||
return err
|
||||
}
|
||||
|
||||
err = lay.Execute(out, data)
|
||||
err = lay.Execute(out, gd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
type SingleIssueViewModel struct {
|
||||
IssueViewModel
|
||||
No string
|
||||
No int
|
||||
Year string
|
||||
Pieces PieceListViewModel
|
||||
Next IssueViewModel
|
||||
@@ -25,13 +25,14 @@ func NewSingleIssueView(y string, No string, lib *xmlprovider.Library) (*SingleI
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sivm := SingleIssueViewModel{IssueViewModel: *ivm, No: No, Year: y, Pieces: *pl}
|
||||
|
||||
no, err := strconv.Atoi(No)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sivm := SingleIssueViewModel{IssueViewModel: *ivm, No: no, Year: y, Pieces: *pl}
|
||||
|
||||
sivm.Pieces.Sort(y, no)
|
||||
|
||||
return &sivm, nil
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
hello
|
||||
BIN
views/assets/logo/dev_favicon.png
Normal file
BIN
views/assets/logo/dev_favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
File diff suppressed because one or more lines are too long
@@ -1,10 +1,26 @@
|
||||
<!doctype html>
|
||||
<html class="w-full h-full" lang="de">
|
||||
<html class="w-full h-full" {{ if .lang }}lang="{{ .lang }}"{{ end }}>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
|
||||
{{ block "head" . }}
|
||||
<!-- Default Head elements -->
|
||||
{{ end }}
|
||||
|
||||
{{ if .isDev }}
|
||||
<link rel="icon" href="/assets/logo/dev_favicon.png" />
|
||||
{{ else }}
|
||||
<link rel="icon" href="/assets/logo/favicon.png" />
|
||||
{{ end }}
|
||||
|
||||
{{ if (and .name .title) }}
|
||||
<title>{{ .name }} - {{ .title }}</title>
|
||||
{{ else if .name }}
|
||||
<title>{{ .name }}</title>
|
||||
{{ else if .title }}
|
||||
<title>{{ .title }}</title>
|
||||
{{ end }}
|
||||
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/assets/style.css" />
|
||||
<link href="/assets/css/remixicon.css" rel="stylesheet" />
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
{{ $y := .model.Year }}
|
||||
|
||||
{{ range $year := .model.AvailableYears }}
|
||||
<a href="/{{ $year }}" aria-active="{{ eq $year $y }}">{{ $year }}</a>
|
||||
<a href="/{{ $year }}" {{ if eq $year $y }}aria-current="page"{{ end }}>{{ $year }}</a>
|
||||
{{ end }}
|
||||
{{ range $index, $month := .model.Issues }}
|
||||
|
||||
|
||||
@@ -15,7 +15,11 @@ Issue found!
|
||||
<ol>
|
||||
{{ range $issue := $piece.IssueRefs }}
|
||||
<li>
|
||||
<a href="/{{- $issue.Datum -}}/{{- $issue.Nr -}}">
|
||||
<a
|
||||
href="/{{- $issue.Datum -}}/{{- $issue.Nr -}}"
|
||||
{{ if and (eq $issue.Nr $model.No) (eq $issue.Datum $model.Year) }}
|
||||
aria-current="page"
|
||||
{{ end }}>
|
||||
{{- $issue.Datum }} Nr.
|
||||
{{ $issue.Nr -}}
|
||||
</a>
|
||||
|
||||
@@ -74,7 +74,9 @@
|
||||
font-display: swap;
|
||||
src: url(/public/fonts/SourceSans3-BoldItalic.ttf) format("truetype");
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
html {
|
||||
font-size: 15.5px;
|
||||
}
|
||||
@@ -102,122 +104,7 @@
|
||||
@apply ml-14 list-disc;
|
||||
}
|
||||
|
||||
.pb-login-input {
|
||||
@apply flex flex-col my-2 px-3 py-2 rounded bg-slate-100 transition-all duration-150 box-border border border-slate-100;
|
||||
}
|
||||
|
||||
.pb-login-input label {
|
||||
@apply text-sm font-bold text-slate-600 pb-0.5;
|
||||
}
|
||||
|
||||
.pb-login-input:focus-within {
|
||||
@apply bg-slate-200 border border-slate-700 shadow;
|
||||
}
|
||||
|
||||
input {
|
||||
@apply bg-transparent focus:outline-none focus:border-none border-none outline-none;
|
||||
}
|
||||
|
||||
input[type="radio"] {
|
||||
@apply scale-[0.85] shadow-none relative bottom-[-0.075rem] !select-none ml-1;
|
||||
}
|
||||
|
||||
input[type="radio"]:checked + span {
|
||||
@apply text-slate-700;
|
||||
}
|
||||
|
||||
label {
|
||||
@apply select-none;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin: 0 0 2em 0;
|
||||
}
|
||||
|
||||
.fancy {
|
||||
line-height: 0.3rem;
|
||||
text-align: center;
|
||||
font-variant: small-caps;
|
||||
}
|
||||
|
||||
.fancy span {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.fancy span:before,
|
||||
.fancy span:after {
|
||||
content: "";
|
||||
height: 0.5rem;
|
||||
width: 140px;
|
||||
@apply border-t border-slate-400 absolute top-4;
|
||||
}
|
||||
|
||||
.fancy span:after {
|
||||
@apply rounded-tr-full;
|
||||
}
|
||||
|
||||
.fancy span:before {
|
||||
@apply rounded-tl-full;
|
||||
}
|
||||
|
||||
.fancy span:before {
|
||||
right: 100%;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.fancy span:after {
|
||||
left: 100%;
|
||||
margin-left: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.setup-columns {
|
||||
p {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
p:not(:first-of-type) {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.settings {
|
||||
> label,
|
||||
> .fielddesc {
|
||||
@apply col-span-4 font-bold font-serif text-sm pt-1;
|
||||
}
|
||||
|
||||
> div {
|
||||
@apply col-span-8;
|
||||
}
|
||||
|
||||
input,
|
||||
textarea,
|
||||
.settings-info {
|
||||
@apply w-full;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="url"],
|
||||
input[type="email"],
|
||||
textarea {
|
||||
@apply px-1.5 py-0.5 bg-slate-100 border-slate-400 border focus:bg-slate-50 rounded-sm;
|
||||
}
|
||||
|
||||
.settings-info {
|
||||
@apply px-1.5 py-0.5;
|
||||
}
|
||||
|
||||
.formhelp {
|
||||
@apply flex flex-row gap-x-1.5 mt-1;
|
||||
}
|
||||
|
||||
.formhelp i {
|
||||
@apply text-slate-400;
|
||||
}
|
||||
|
||||
.formhelp .formhelptext {
|
||||
@apply text-slate-700 text-sm leading-tight pt-0.5;
|
||||
a[aria-current="page"] {
|
||||
@apply !text-red-500;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user