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..." }