Files
insertr/internal/engine/content.go
Joakim 448b66a974 Fix critical enhancement hanging bug caused by nil context in content injection
Replace nil context with context.Background() in content.go to prevent database operations from hanging indefinitely. Clean up outdated documentation files and add current project structure analysis.
2025-10-26 21:26:48 +01:00

146 lines
4.2 KiB
Go

package engine
import (
"context"
"slices"
"strings"
"golang.org/x/net/html"
)
// addContentAttributes adds data-content-id attribute only
func (e *ContentEngine) addContentAttributes(node *html.Node, contentID string) {
// Add data-content-id attribute
SetAttribute(node, "data-content-id", contentID)
}
// injectContent injects content from database into elements
func (e *ContentEngine) injectContent(elements []ProcessedElement, siteID string) error {
for i := range elements {
elem := &elements[i]
// Get content from database by ID - FIXED: Use context.Background() instead of nil
contentItem, err := e.client.GetContent(context.Background(), siteID, elem.ID)
if err != nil {
// Content not found - skip silently (enhancement mode should not fail on missing content)
continue
}
if contentItem != nil {
// Inject the content into the element
elem.Content = contentItem.HTMLContent
// Update injector siteID for this operation
// HACK: I do not like this. Injector refactor?
e.injector.siteID = siteID
e.injector.injectHTMLContent(elem.Node, contentItem.HTMLContent)
}
}
return nil
}
// extractHTMLContent extracts the inner HTML content from a node
func (e *ContentEngine) extractHTMLContent(node *html.Node) string {
var content strings.Builder
// Render all child nodes in order to preserve HTML structure
for child := node.FirstChild; child != nil; child = child.NextSibling {
if err := html.Render(&content, child); err == nil {
// All nodes (text and element) rendered in correct order
}
}
return strings.TrimSpace(content.String())
}
// extractOriginalTemplate extracts the outer HTML of the element (including the element itself)
func (e *ContentEngine) extractOriginalTemplate(node *html.Node) string {
var buf strings.Builder
if err := html.Render(&buf, node); err != nil {
return ""
}
return buf.String()
}
// extractCleanTemplate extracts a clean template without data-content-id attributes and with placeholder content. Used for collection template variants.
func (e *ContentEngine) extractCleanTemplate(node *html.Node) string {
// Clone the node to avoid modifying the original
clonedNode := e.cloneNode(node)
// Remove all data-content-id attributes and replace content with placeholders
e.walkNodes(clonedNode, func(n *html.Node) {
if n.Type == html.ElementNode {
// Remove data-content-id attribute
e.removeAttribute(n, "data-content-id")
// If this is an .insertr element, replace content with placeholder
if e.hasClass(n, "insertr") {
placeholderText := e.getPlaceholderForElement(n.Data)
// Clear existing children and add placeholder text
for child := n.FirstChild; child != nil; {
next := child.NextSibling
n.RemoveChild(child)
child = next
}
n.AppendChild(&html.Node{
Type: html.TextNode,
Data: placeholderText,
})
}
}
})
var buf strings.Builder
if err := html.Render(&buf, clonedNode); err != nil {
return ""
}
return buf.String()
}
// removeAttribute removes an attribute from an HTML node
func (e *ContentEngine) removeAttribute(n *html.Node, key string) {
for i, attr := range n.Attr {
if attr.Key == key {
n.Attr = slices.Delete(n.Attr, i, i+1)
break
}
}
}
// hasClass checks if a node has a specific class
func (e *ContentEngine) hasClass(n *html.Node, className string) bool {
for _, attr := range n.Attr {
if attr.Key == "class" {
classes := strings.Fields(attr.Val)
if slices.Contains(classes, className) {
return true
}
}
}
return false
}
// getPlaceholderForElement returns appropriate placeholder text for different element types
func (e *ContentEngine) getPlaceholderForElement(elementType string) string {
placeholders := map[string]string{
"h1": "Heading 1",
"h2": "Heading 2",
"h3": "Heading 3",
"h4": "Heading 4",
"h5": "Heading 5",
"h6": "Heading 6",
"p": "Paragraph text",
"span": "Text",
"div": "Content block",
"button": "Button",
"a": "Link text",
"li": "List item",
"blockquote": "Quote text",
}
if placeholder, exists := placeholders[elementType]; exists {
return placeholder
}
return "Enter content..."
}