diff --git a/templating/engine.go b/templating/engine.go
index d8f2cec..0b95297 100644
--- a/templating/engine.go
+++ b/templating/engine.go
@@ -69,10 +69,15 @@ func PageIcon(iconType string) template.HTML {
}
}
-// GetPieceURL generates a piece view URL from year, issue number, and page
-func GetPieceURL(year, issueNum, page int) string {
- pieceID := fmt.Sprintf("%d-%03d-%03d", year, issueNum, page)
- return "/beitrag/" + pieceID
+// GetPieceURL generates a piece view URL from year, issue number, page, and optional piece ID
+func GetPieceURL(year, issueNum, page int, pieceID ...string) string {
+ if len(pieceID) > 0 && pieceID[0] != "" && pieceID[0] != "0" {
+ // Use just the piece ID (no year/issue prefix in URL)
+ return "/beitrag/" + pieceID[0]
+ }
+ // Fallback to old format for backward compatibility
+ id := fmt.Sprintf("%d-%03d-%03d", year, issueNum, page)
+ return "/beitrag/" + id
}
// IssueContext formats an issue reference into a readable context string
@@ -296,6 +301,13 @@ func (e *Engine) funcs() error {
return result
})
+ e.AddFunc("split", func(s, delimiter string) []string {
+ if s == "" {
+ return []string{}
+ }
+ return strings.Split(s, delimiter)
+ })
+
e.AddFunc("sortStrings", func(items interface{}) []string {
v := reflect.ValueOf(items)
if v.Kind() != reflect.Slice {
diff --git a/views/routes/ausgabe/components/_inhaltsverzeichnis.gohtml b/views/routes/ausgabe/components/_inhaltsverzeichnis.gohtml
index 5ec0bbf..6b4de6e 100644
--- a/views/routes/ausgabe/components/_inhaltsverzeichnis.gohtml
+++ b/views/routes/ausgabe/components/_inhaltsverzeichnis.gohtml
@@ -96,7 +96,7 @@
duration-200">
Ganzer Beitrag
@@ -231,7 +231,7 @@
duration-200">
Ganzer Beitrag
diff --git a/views/routes/components/_akteur_beitraege.gohtml b/views/routes/components/_akteur_beitraege.gohtml
index c005bed..32942b8 100644
--- a/views/routes/components/_akteur_beitraege.gohtml
+++ b/views/routes/components/_akteur_beitraege.gohtml
@@ -66,6 +66,16 @@
{{- end -}}
{{- end -}}
+ {{- /* Add "Ganzer Beitrag" link if piece spans multiple issues */ -}}
+ {{- $firstGroupItem := index $groupedItems 0 -}}
+ {{- if gt (len $firstGroupItem.Item.IssueRefs) 1 -}}
+ {{ " " }}
+ {{- end -}}
{{- end -}}
diff --git a/views/routes/components/_akteur_werke.gohtml b/views/routes/components/_akteur_werke.gohtml
index ac3d499..b431dbe 100644
--- a/views/routes/components/_akteur_werke.gohtml
+++ b/views/routes/components/_akteur_werke.gohtml
@@ -1,6 +1,17 @@
{{ $a := . }}
{{ $works := LookupWorks $a }}
-{{- if ne (len $works) 0 -}}
+{{ $allPieces := LookupPieces $a }}
+
+{{- /* Filter pieces for work-related categories */ -}}
+{{- $workPieces := slice -}}
+{{- range $_, $p := $allPieces -}}
+ {{- $categoryFlags := GetCategoryFlags $p.Item -}}
+ {{- if or $categoryFlags.Rezension $categoryFlags.Auszug $categoryFlags.Theaterkritik $categoryFlags.Uebersetzung $categoryFlags.Kommentar $categoryFlags.Replik $categoryFlags.Anzeige $categoryFlags.Provinienz -}}
+ {{- $workPieces = append $workPieces $p -}}
+ {{- end -}}
+{{- end -}}
+
+{{- if or (ne (len $works) 0) (ne (len $workPieces) 0) -}}
@@ -117,39 +128,48 @@
{{- if eq (len $categories) 0 -}}
{{- $categories = append $categories "Beitrag" -}}
{{- end -}}
- {{- $categoryName := "" -}}
- {{- if eq (len $categories) 0 -}}
- {{- $categoryName = "Beitrag" -}}
- {{- else -}}
- {{- $sortedCategories := sortStrings $categories -}}
- {{- $categoryName = joinWithUnd $sortedCategories -}}
- {{- end -}}
- {{- $existing := index $groupedByCategory $categoryName -}}
+ {{- /* Get additional authors for this specific piece */ -}}
+ {{- $pieceAdditionalAuthorIDs := slice -}}
+ {{- range $agentref := $p.Item.AgentRefs -}}
+ {{- if and (or (eq $agentref.Category "") (eq $agentref.Category "autor")) (ne $agentref.Ref $a.ID) -}}
+ {{- $pieceAdditionalAuthorIDs = append $pieceAdditionalAuthorIDs $agentref.Ref -}}
+ {{- end -}}
+ {{- end -}}
+ {{- $sortedAdditionalAuthorIDs := sortStrings $pieceAdditionalAuthorIDs -}}
+
+ {{- /* Create grouping key with category + additional authors */ -}}
+ {{- $sortedCategories := sortStrings $categories -}}
+ {{- $categoryName := joinWithUnd $sortedCategories -}}
+ {{- $groupKey := printf "%s|%s" $categoryName (joinWithUnd $sortedAdditionalAuthorIDs) -}}
+
+ {{- $existing := index $groupedByCategory $groupKey -}}
{{- if $existing -}}
- {{- $groupedByCategory = merge $groupedByCategory (dict $categoryName (append $existing $p)) -}}
+ {{- $groupedByCategory = merge $groupedByCategory (dict $groupKey (append $existing $p)) -}}
{{- else -}}
- {{- $groupedByCategory = merge $groupedByCategory (dict $categoryName (slice $p)) -}}
+ {{- $groupedByCategory = merge $groupedByCategory (dict $groupKey (slice $p)) -}}
{{- end -}}
{{- end -}}
{{- /* Display each category group */ -}}
- {{- range $categoryName, $categoryPieces := $groupedByCategory -}}
+ {{- range $groupKey, $categoryPieces := $groupedByCategory -}}
+ {{- /* Extract category and additional authors from group key */ -}}
+ {{- $keyParts := split $groupKey "|" -}}
+ {{- $categoryName := index $keyParts 0 -}}
+
{{ $categoryName }}
- {{- /* Check for additional authors (non-current actor) */ -}}
+ {{- /* Get additional authors from first piece in group */ -}}
+ {{- $firstPiece := index $categoryPieces 0 -}}
{{- $additionalAuthorIDs := slice -}}
- {{- range $_, $p := $categoryPieces -}}
- {{- range $agentref := $p.Item.AgentRefs -}}
- {{- if and (or (eq $agentref.Category "") (eq $agentref.Category "autor")) (ne $agentref.Ref $a.ID) -}}
- {{- $additionalAuthorIDs = append $additionalAuthorIDs $agentref.Ref -}}
- {{- end -}}
+ {{- range $agentref := $firstPiece.Item.AgentRefs -}}
+ {{- if and (or (eq $agentref.Category "") (eq $agentref.Category "autor")) (ne $agentref.Ref $a.ID) -}}
+ {{- $additionalAuthorIDs = append $additionalAuthorIDs $agentref.Ref -}}
{{- end -}}
{{- end -}}
- {{- $uniqueAuthorIDs := unique $additionalAuthorIDs -}}
- {{- if $uniqueAuthorIDs -}}
- {{ " " }}von {{ range $i, $authorID := $uniqueAuthorIDs }}{{- if gt $i 0 }} und {{ end }}{{- $agent := GetAgent $authorID -}}{{- if and $agent (gt (len $agent.Names) 0) -}}{{ index $agent.Names 0 }}{{- end -}}{{ end }}
+ {{- if $additionalAuthorIDs -}}
+ {{ " " }}von {{ range $i, $authorID := $additionalAuthorIDs }}{{- if gt $i 0 }} und {{ end }}{{- $agent := GetAgent $authorID -}}{{- if and $agent (gt (len $agent.Names) 0) -}}{{ index $agent.Names 0 }}{{- end -}}{{ end }}
{{- end -}}{{ ":" }}
{{- /* Show all citations for this category inline with commas */ -}}
@@ -174,12 +194,152 @@
{{- end -}}
{{- end -}}
+ {{- /* Add "Ganzer Beitrag" link if piece spans multiple issues */ -}}
+ {{- $firstPiece := index $categoryPieces 0 -}}
+ {{- if gt (len $firstPiece.Item.IssueRefs) 1 -}}
+ {{ " " }}
+ {{- end -}}
{{- end -}}
{{ end }}
{{ end }}
+
+ {{- /* Process standalone work pieces that aren't linked to specific works */ -}}
+ {{- if ne (len $workPieces) 0 -}}
+ {{- /* Group standalone work pieces by category and additional authors */ -}}
+ {{- $standaloneGrouped := dict -}}
+ {{- range $_, $p := $workPieces -}}
+ {{- /* Skip pieces that are already covered by works above */ -}}
+ {{- $isPieceInWorks := false -}}
+ {{- range $_, $w := $works -}}
+ {{- $workPiecesCheck := LookupPieces $w.Item -}}
+ {{- range $_, $wp := $workPiecesCheck -}}
+ {{- if eq $wp.Item.ID $p.Item.ID -}}
+ {{- $isPieceInWorks = true -}}
+ {{- end -}}
+ {{- end -}}
+ {{- end -}}
+
+ {{- if not $isPieceInWorks -}}
+ {{- /* Get categories for this piece */ -}}
+ {{- $categoryFlags := GetCategoryFlags $p.Item -}}
+ {{- $categories := slice -}}
+ {{- if $categoryFlags.Rezension -}}
+ {{- $categories = append $categories "Rezension" -}}
+ {{- end -}}
+ {{- if $categoryFlags.Auszug -}}
+ {{- $categories = append $categories "Auszug" -}}
+ {{- end -}}
+ {{- if $categoryFlags.Theaterkritik -}}
+ {{- $categories = append $categories "Theaterkritik" -}}
+ {{- end -}}
+ {{- if $categoryFlags.Uebersetzung -}}
+ {{- $categories = append $categories "Übersetzung" -}}
+ {{- end -}}
+ {{- if $categoryFlags.Kommentar -}}
+ {{- $categories = append $categories "Kommentar" -}}
+ {{- end -}}
+ {{- if $categoryFlags.Replik -}}
+ {{- $categories = append $categories "Replik" -}}
+ {{- end -}}
+ {{- if $categoryFlags.Anzeige -}}
+ {{- $categories = append $categories "Anzeige" -}}
+ {{- end -}}
+ {{- if $categoryFlags.Provinienz -}}
+ {{- $categories = append $categories "Provinienz" -}}
+ {{- end -}}
+
+ {{- if ne (len $categories) 0 -}}
+ {{- /* Get additional authors for this specific piece */ -}}
+ {{- $pieceAdditionalAuthorIDs := slice -}}
+ {{- range $agentref := $p.Item.AgentRefs -}}
+ {{- if and (or (eq $agentref.Category "") (eq $agentref.Category "autor")) (ne $agentref.Ref $a.ID) -}}
+ {{- $pieceAdditionalAuthorIDs = append $pieceAdditionalAuthorIDs $agentref.Ref -}}
+ {{- end -}}
+ {{- end -}}
+ {{- $sortedAdditionalAuthorIDs := sortStrings $pieceAdditionalAuthorIDs -}}
+
+ {{- /* Create grouping key with category + additional authors */ -}}
+ {{- $sortedCategories := sortStrings $categories -}}
+ {{- $categoryName := joinWithUnd $sortedCategories -}}
+ {{- $groupKey := printf "%s|%s" $categoryName (joinWithUnd $sortedAdditionalAuthorIDs) -}}
+
+ {{- $existing := index $standaloneGrouped $groupKey -}}
+ {{- if $existing -}}
+ {{- $standaloneGrouped = merge $standaloneGrouped (dict $groupKey (append $existing $p)) -}}
+ {{- else -}}
+ {{- $standaloneGrouped = merge $standaloneGrouped (dict $groupKey (slice $p)) -}}
+ {{- end -}}
+ {{- end -}}
+ {{- end -}}
+ {{- end -}}
+
+ {{- /* Display standalone work pieces */ -}}
+ {{- range $groupKey, $categoryPieces := $standaloneGrouped -}}
+
+ {{- end -}}
+ {{- end -}}
+
{{ end }}
diff --git a/views/routes/components/_piece_summary.gohtml b/views/routes/components/_piece_summary.gohtml
index f06fe88..5c7a616 100644
--- a/views/routes/components/_piece_summary.gohtml
+++ b/views/routes/components/_piece_summary.gohtml
@@ -140,61 +140,65 @@
{{- /* Generate piece descriptions */ -}}
{{- if has $categories "Rezension" -}}
- {{- $authorFound := false -}}
+ {{- /* Collect all additional authors (not current actor) */ -}}
+ {{- $additionalAuthors := slice -}}
+ {{- $currentAuthorFound := false -}}
{{- range $agentref := $piece.AgentRefs -}}
{{- if (or (eq $agentref.Category "") (eq $agentref.Category "autor")) -}}
{{- $agent := GetAgent $agentref.Ref -}}
{{- if and $agent (gt (len $agent.Names) 0) -}}
{{- if ne $agentref.Ref $currentActorID -}}
- {{ index $agent.Names 0 }},
+ {{- $additionalAuthors = append $additionalAuthors $agent -}}
+ {{- else -}}
+ {{- $currentAuthorFound = true -}}
{{- end -}}
- {{ $categoryName }} von:
- {{ if $workAuthorName }}
- {{ $workAuthorName }},
- {{ end }}
- {{ if $workTitle }}
- {{ $workTitle }}
- {{ else if $title }}
- {{ $title }}
- {{ else }}
- [Werk unbekannt]
- {{ end }}
- {{- $authorFound = true -}}
- {{- break -}}
{{- end -}}
{{- end -}}
{{- end -}}
- {{- if not $authorFound -}}
- {{ $categoryName }} von:
- {{ if $workAuthorName }}
- {{ $workAuthorName }},
- {{ end }}
- {{ if $workTitle }}
- {{ $workTitle }}
- {{ else if $title }}
- {{ $title }}
- {{ else }}
- [Werk unbekannt]
- {{ end }}
- {{- end -}}
+
+ {{- /* Display review with proper author attribution */ -}}
+ {{- if $additionalAuthors -}}
+ mit {{ range $i, $author := $additionalAuthors }}{{- if gt $i 0 }} und {{ end }}{{ index $author.Names 0 }}{{ end }}, {{ $categoryName }} von:
+ {{- else if $currentAuthorFound -}}
+ {{ $categoryName }} von:
+ {{- else -}}
+ {{ $categoryName }} von:
+ {{- end -}}
+ {{ if $workAuthorName }}
+ {{ $workAuthorName }},
+ {{ end }}
+ {{ if $workTitle }}
+ {{ $workTitle }}
+ {{ else if $title }}
+ {{ $title }}
+ {{ else }}
+ [Werk unbekannt]
+ {{ end }}
{{- else -}}
- {{- $authorFound := false -}}
+ {{- /* Collect all additional authors (not current actor) */ -}}
+ {{- $additionalAuthors := slice -}}
+ {{- $currentAuthorFound := false -}}
{{- range $agentref := $piece.AgentRefs -}}
{{- if (or (eq $agentref.Category "") (eq $agentref.Category "autor")) -}}
{{- $agent := GetAgent $agentref.Ref -}}
{{- if and $agent (gt (len $agent.Names) 0) -}}
{{- if ne $agentref.Ref $currentActorID -}}
- {{ index $agent.Names 0 }},
+ {{- $additionalAuthors = append $additionalAuthors $agent -}}
+ {{- else -}}
+ {{- $currentAuthorFound = true -}}
{{- end -}}
- {{ $categoryName }}{{ if $title }}: „{{ $title }}"{{ end }}
- {{- $authorFound = true -}}
- {{- break -}}
{{- end -}}
{{- end -}}
{{- end -}}
- {{- if not $authorFound -}}
- {{ $categoryName }}{{ if $title }}: „{{ $title }}"{{ else if eq $categoryName "Beitrag" }} ohne Titel{{ end }}
+
+ {{- /* Display with proper author attribution */ -}}
+ {{- if $additionalAuthors -}}
+ mit {{ range $i, $author := $additionalAuthors }}{{- if gt $i 0 }} und {{ end }}{{ index $author.Names 0 }}{{ end }}, {{ $categoryName }}{{ if $title }}: {{ $title }}{{ end }}
+ {{- else if $currentAuthorFound -}}
+ {{ $categoryName }}{{ if $title }}: {{ $title }}{{ end }}
+ {{- else -}}
+ {{ $categoryName }}{{ if $title }}: {{ $title }}{{ else if eq $categoryName "Beitrag" }} ohne Titel{{ end }}
{{- end -}}
{{- end -}}
diff --git a/views/routes/piece/body.gohtml b/views/routes/piece/body.gohtml
index 1b383ed..ca7f0d8 100644
--- a/views/routes/piece/body.gohtml
+++ b/views/routes/piece/body.gohtml
@@ -19,13 +19,14 @@
{{ else }}
-
+
Keine Bilder verfügbar
-
+
Für diesen Beitrag sind derzeit keine Seitenbilder verfügbar.
+
{{ end }}
\ No newline at end of file
diff --git a/xmlmodels/pieces.go b/xmlmodels/pieces.go
index cd2c96c..256b9e3 100644
--- a/xmlmodels/pieces.go
+++ b/xmlmodels/pieces.go
@@ -1,13 +1,16 @@
package xmlmodels
import (
+ "crypto/sha256"
+ "encoding/hex"
"encoding/json"
"encoding/xml"
+ "fmt"
+ "sort"
"strconv"
"strings"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/xmlprovider"
- "github.com/google/uuid"
)
const (
@@ -34,6 +37,69 @@ func (p Piece) String() string {
return string(data)
}
+// generateContentBasedID creates a deterministic ID based on piece content
+func (p Piece) generateContentBasedID() string {
+ var parts []string
+
+ // Add title if available
+ if len(p.Title) > 0 && p.Title[0] != "" {
+ parts = append(parts, "title:"+strings.ToLower(strings.TrimSpace(p.Title[0])))
+ }
+
+ // Add incipit if available
+ if len(p.Incipit) > 0 && p.Incipit[0] != "" {
+ incipit := strings.ToLower(strings.TrimSpace(p.Incipit[0]))
+ // Limit incipit to first 50 characters to avoid overly long IDs
+ if len(incipit) > 50 {
+ incipit = incipit[:50]
+ }
+ parts = append(parts, "incipit:"+incipit)
+ }
+
+ // Add author references
+ var authors []string
+ for _, agent := range p.AgentRefs {
+ if agent.Category == "" || agent.Category == "autor" {
+ authors = append(authors, agent.Ref)
+ }
+ }
+ sort.Strings(authors) // Ensure consistent ordering
+ if len(authors) > 0 {
+ parts = append(parts, "authors:"+strings.Join(authors, ","))
+ }
+
+ // Add categories
+ var categories []string
+ for _, cat := range p.CategoryRefs {
+ if cat.Category != "" {
+ categories = append(categories, cat.Category)
+ }
+ }
+ sort.Strings(categories) // Ensure consistent ordering
+ if len(categories) > 0 {
+ parts = append(parts, "categories:"+strings.Join(categories, ","))
+ }
+
+ // If we have no meaningful content, create a minimal hash from issue refs
+ if len(parts) == 0 {
+ // Use issue references as fallback content
+ for _, issue := range p.IssueRefs {
+ parts = append(parts, fmt.Sprintf("issue:%d-%d-%d-%d", issue.When.Year, issue.Nr, issue.Von, issue.Bis))
+ }
+ // If still no content, use a generic identifier
+ if len(parts) == 0 {
+ parts = append(parts, "unknown-piece")
+ }
+ }
+
+ // Create hash of combined content
+ content := strings.Join(parts, "|")
+ hash := sha256.Sum256([]byte(content))
+
+ // Return first 12 characters of hex hash for reasonable ID length
+ return hex.EncodeToString(hash[:])[:12]
+}
+
func (p Piece) Categories() map[string]bool {
cats := make(map[string]bool)
for _, c := range p.CategoryRefs {
@@ -70,24 +136,23 @@ func (p Piece) Categories() map[string]bool {
}
func (p Piece) Keys() []string {
- if len(p.keys) > 0 {
- return p.keys
- }
-
+ // Always regenerate keys to ensure we use the new content-based logic
ret := make([]string, 0, 3)
+
+ // Primary ID: Use existing ID if available, otherwise content-based ID
+ var primaryID string
if p.ID != "" {
- ret = append(ret, p.ID)
+ primaryID = p.ID
+ } else {
+ primaryID = p.generateContentBasedID()
}
+ ret = append(ret, primaryID)
- // TODO: sensible IDs
- uid := uuid.New()
- ret = append(ret, uid.String())
-
+ // Create issue-specific keys using the primary ID for lookup
for _, i := range p.IssueRefs {
- ret = append(ret, strconv.Itoa(i.When.Year)+"-"+strconv.Itoa(i.Nr)+"-"+uid.String())
+ ret = append(ret, strconv.Itoa(i.When.Year)+"-"+strconv.Itoa(i.Nr)+"-"+primaryID)
}
- p.keys = ret
return ret
}