|
|
|
|
@@ -3,6 +3,7 @@ package engine
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"golang.org/x/net/html"
|
|
|
|
|
)
|
|
|
|
|
@@ -385,6 +386,30 @@ func (e *ContentEngine) extractHTMLContent(node *html.Node) string {
|
|
|
|
|
return strings.TrimSpace(content.String())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// extractTextContent extracts only the text content from a node (for individual content storage)
|
|
|
|
|
func (e *ContentEngine) extractTextContent(node *html.Node) string {
|
|
|
|
|
var text strings.Builder
|
|
|
|
|
|
|
|
|
|
// Walk through all text nodes to extract content
|
|
|
|
|
e.walkNodes(node, func(n *html.Node) {
|
|
|
|
|
if n.Type == html.TextNode {
|
|
|
|
|
text.WriteString(n.Data)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return strings.TrimSpace(text.String())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// getAttributeValue gets an attribute value from an HTML node
|
|
|
|
|
func (e *ContentEngine) getAttributeValue(n *html.Node, attrKey string) string {
|
|
|
|
|
for _, attr := range n.Attr {
|
|
|
|
|
if attr.Key == attrKey {
|
|
|
|
|
return attr.Val
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// extractOriginalTemplate extracts the outer HTML of the element (including the element itself)
|
|
|
|
|
func (e *ContentEngine) extractOriginalTemplate(node *html.Node) string {
|
|
|
|
|
var buf strings.Builder
|
|
|
|
|
@@ -394,6 +419,86 @@ func (e *ContentEngine) extractOriginalTemplate(node *html.Node) string {
|
|
|
|
|
return buf.String()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// extractCleanTemplate extracts a clean template without data-content-id attributes and with placeholder content
|
|
|
|
|
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 = append(n.Attr[:i], n.Attr[i+1:]...)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// hasClass checks if an HTML 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)
|
|
|
|
|
for _, class := range classes {
|
|
|
|
|
if class == className {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// getPlaceholderForElement returns appropriate placeholder text for an element type
|
|
|
|
|
func (e *ContentEngine) getPlaceholderForElement(elementType string) string {
|
|
|
|
|
placeholders := map[string]string{
|
|
|
|
|
"blockquote": "Enter your quote here...",
|
|
|
|
|
"cite": "Enter author name...",
|
|
|
|
|
"h1": "Enter heading...",
|
|
|
|
|
"h2": "Enter heading...",
|
|
|
|
|
"h3": "Enter heading...",
|
|
|
|
|
"p": "Enter text...",
|
|
|
|
|
"span": "Enter text...",
|
|
|
|
|
"div": "Enter content...",
|
|
|
|
|
"a": "Enter link text...",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if placeholder, exists := placeholders[elementType]; exists {
|
|
|
|
|
return placeholder
|
|
|
|
|
}
|
|
|
|
|
return "Enter content..."
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// processCollection handles collection detection, persistence and reconstruction
|
|
|
|
|
func (e *ContentEngine) processCollection(collectionNode *html.Node, collectionID, siteID string) error {
|
|
|
|
|
// 1. Check if collection exists in database
|
|
|
|
|
@@ -417,27 +522,15 @@ func (e *ContentEngine) processCollection(collectionNode *html.Node, collectionI
|
|
|
|
|
|
|
|
|
|
fmt.Printf("✅ Created new collection: %s with templates and initial items\n", collectionID)
|
|
|
|
|
} else {
|
|
|
|
|
// 4. Database-first approach: Check if collection items already exist
|
|
|
|
|
existingItems, err := e.client.GetCollectionItems(siteID, collectionID)
|
|
|
|
|
// 4. Existing collection: Always reconstruct from database (database is source of truth)
|
|
|
|
|
err = e.reconstructCollectionItems(collectionNode, collectionID, siteID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to check existing collection items: %w", err)
|
|
|
|
|
return fmt.Errorf("failed to reconstruct collection %s: %w", collectionID, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(existingItems) == 0 {
|
|
|
|
|
// 5. Collection exists but no items - store original children as initial items
|
|
|
|
|
err = e.storeInitialCollectionItems(collectionNode, collectionID, siteID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to store initial collection items for %s: %w", collectionID, err)
|
|
|
|
|
}
|
|
|
|
|
fmt.Printf("✅ Stored initial items for existing collection: %s\n", collectionID)
|
|
|
|
|
} else {
|
|
|
|
|
// 6. Items exist: reconstruct from database (normal case)
|
|
|
|
|
err = e.reconstructCollectionItems(collectionNode, collectionID, siteID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to reconstruct collection %s: %w", collectionID, err)
|
|
|
|
|
}
|
|
|
|
|
fmt.Printf("✅ Reconstructed collection: %s from database (%d items)\n", collectionID, len(existingItems))
|
|
|
|
|
}
|
|
|
|
|
// Get final item count for logging
|
|
|
|
|
existingItems, _ := e.client.GetCollectionItems(siteID, collectionID)
|
|
|
|
|
fmt.Printf("✅ Reconstructed collection: %s from database (%d items)\n", collectionID, len(existingItems))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
@@ -468,7 +561,7 @@ func (e *ContentEngine) extractAndStoreTemplatesAndItems(collectionNode *html.No
|
|
|
|
|
// Extract templates from existing children and store them
|
|
|
|
|
var templateIDs []int
|
|
|
|
|
for i, templateElement := range templateElements {
|
|
|
|
|
templateHTML := e.extractOriginalTemplate(templateElement)
|
|
|
|
|
templateHTML := e.extractCleanTemplate(templateElement)
|
|
|
|
|
templateName := fmt.Sprintf("template-%d", i+1)
|
|
|
|
|
isDefault := (i == 0) // First template is default
|
|
|
|
|
|
|
|
|
|
@@ -517,69 +610,49 @@ func (e *ContentEngine) reconstructCollectionItems(collectionNode *html.Node, co
|
|
|
|
|
child = next
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add items from database in position order
|
|
|
|
|
// Add items from database in position order using unified .insertr approach
|
|
|
|
|
for _, item := range items {
|
|
|
|
|
// Get the template for this item
|
|
|
|
|
templateHTML, exists := templateMap[item.TemplateID]
|
|
|
|
|
if !exists {
|
|
|
|
|
fmt.Printf("⚠️ Template %d not found for item %s\n", item.TemplateID, item.ItemID)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Parse the template HTML
|
|
|
|
|
templateDoc, err := html.Parse(strings.NewReader(templateHTML))
|
|
|
|
|
// Parse the stored structural HTML with content IDs (no template needed for reconstruction)
|
|
|
|
|
structuralDoc, err := html.Parse(strings.NewReader(item.HTMLContent))
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Printf("⚠️ Failed to parse template HTML for %s: %v\n", item.ItemID, err)
|
|
|
|
|
fmt.Printf("⚠️ Failed to parse stored HTML for %s: %v\n", item.ItemID, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find the body element and extract the template structure
|
|
|
|
|
var templateBody *html.Node
|
|
|
|
|
e.walkNodes(templateDoc, func(n *html.Node) {
|
|
|
|
|
var structuralBody *html.Node
|
|
|
|
|
e.walkNodes(structuralDoc, func(n *html.Node) {
|
|
|
|
|
if n.Type == html.ElementNode && n.Data == "body" {
|
|
|
|
|
templateBody = n
|
|
|
|
|
structuralBody = n
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if templateBody != nil && templateBody.FirstChild != nil {
|
|
|
|
|
// Clone the template structure (first child of body)
|
|
|
|
|
templateNode := templateBody.FirstChild
|
|
|
|
|
clonedTemplate := e.cloneNode(templateNode)
|
|
|
|
|
if structuralBody != nil {
|
|
|
|
|
// Process each .insertr element using Injector pattern (unified approach)
|
|
|
|
|
injector := NewInjector(e.client, siteID)
|
|
|
|
|
|
|
|
|
|
// Replace the template's inner content with the stored item content
|
|
|
|
|
// Clear the cloned template's children
|
|
|
|
|
for child := clonedTemplate.FirstChild; child != nil; {
|
|
|
|
|
next := child.NextSibling
|
|
|
|
|
clonedTemplate.RemoveChild(child)
|
|
|
|
|
child = next
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Parse and add the item's content
|
|
|
|
|
itemDoc, err := html.Parse(strings.NewReader(item.HTMLContent))
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Printf("⚠️ Failed to parse item content for %s: %v\n", item.ItemID, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var itemBody *html.Node
|
|
|
|
|
e.walkNodes(itemDoc, func(n *html.Node) {
|
|
|
|
|
if n.Type == html.ElementNode && n.Data == "body" {
|
|
|
|
|
itemBody = n
|
|
|
|
|
// Walk through structural elements and hydrate with content from content table
|
|
|
|
|
e.walkNodes(structuralBody, func(n *html.Node) {
|
|
|
|
|
if n.Type == html.ElementNode && e.hasClass(n, "insertr") {
|
|
|
|
|
// Get content ID from data attribute
|
|
|
|
|
contentID := e.getAttributeValue(n, "data-content-id")
|
|
|
|
|
if contentID != "" {
|
|
|
|
|
// Use Injector to hydrate content (unified .insertr approach)
|
|
|
|
|
element := &Element{Node: n, Type: "html"}
|
|
|
|
|
err := injector.InjectContent(element, contentID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Printf("⚠️ Failed to inject content for %s: %v\n", contentID, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if itemBody != nil {
|
|
|
|
|
// Move all children from item body to cloned template
|
|
|
|
|
for itemChild := itemBody.FirstChild; itemChild != nil; {
|
|
|
|
|
next := itemChild.NextSibling
|
|
|
|
|
itemBody.RemoveChild(itemChild)
|
|
|
|
|
clonedTemplate.AppendChild(itemChild)
|
|
|
|
|
itemChild = next
|
|
|
|
|
}
|
|
|
|
|
// Add hydrated structural elements directly to collection (stored HTML has complete structure)
|
|
|
|
|
for structuralChild := structuralBody.FirstChild; structuralChild != nil; {
|
|
|
|
|
next := structuralChild.NextSibling
|
|
|
|
|
structuralBody.RemoveChild(structuralChild)
|
|
|
|
|
collectionNode.AppendChild(structuralChild)
|
|
|
|
|
structuralChild = next
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add the reconstructed item to collection
|
|
|
|
|
collectionNode.AppendChild(clonedTemplate)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -587,6 +660,139 @@ func (e *ContentEngine) reconstructCollectionItems(collectionNode *html.Node, co
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// processChildElementsAsContent processes .insertr elements within a collection child and stores them as individual content
|
|
|
|
|
func (e *ContentEngine) processChildElementsAsContent(childElement *html.Node, siteID, itemID string) ([]ContentEntry, error) {
|
|
|
|
|
var contentEntries []ContentEntry
|
|
|
|
|
elementIndex := 0
|
|
|
|
|
|
|
|
|
|
// Walk through child element to find .insertr elements
|
|
|
|
|
e.walkNodes(childElement, func(n *html.Node) {
|
|
|
|
|
if n.Type == html.ElementNode && e.hasClass(n, "insertr") {
|
|
|
|
|
// Use core IDGenerator for unified ID generation (like individual .insertr elements)
|
|
|
|
|
contentID := e.idGenerator.Generate(n, "collection-item")
|
|
|
|
|
|
|
|
|
|
// Extract actual content from the element
|
|
|
|
|
actualContent := e.extractTextContent(n)
|
|
|
|
|
|
|
|
|
|
// Store as individual content entry (unified .insertr approach)
|
|
|
|
|
_, err := e.client.CreateContent(siteID, contentID, actualContent, "", "system")
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Printf("⚠️ Failed to create content %s: %v\n", contentID, err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add to content entries for structural template generation
|
|
|
|
|
contentEntries = append(contentEntries, ContentEntry{
|
|
|
|
|
ID: contentID,
|
|
|
|
|
SiteID: siteID,
|
|
|
|
|
HTMLContent: actualContent,
|
|
|
|
|
Template: e.extractOriginalTemplate(n),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
elementIndex++
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return contentEntries, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// generateStructuralTemplateFromChild creates structure-only HTML from child element with content IDs
|
|
|
|
|
func (e *ContentEngine) generateStructuralTemplateFromChild(childElement *html.Node, contentEntries []ContentEntry) (string, error) {
|
|
|
|
|
// Clone the child element to avoid modifying original
|
|
|
|
|
clonedChild := e.cloneNode(childElement)
|
|
|
|
|
entryIndex := 0
|
|
|
|
|
|
|
|
|
|
// Walk through cloned element and replace content with content IDs
|
|
|
|
|
e.walkNodes(clonedChild, func(n *html.Node) {
|
|
|
|
|
if n.Type == html.ElementNode && e.hasClass(n, "insertr") {
|
|
|
|
|
if entryIndex < len(contentEntries) {
|
|
|
|
|
// Set the data-content-id attribute
|
|
|
|
|
e.setAttribute(n, "data-content-id", contentEntries[entryIndex].ID)
|
|
|
|
|
|
|
|
|
|
// Clear content - this will be hydrated during reconstruction
|
|
|
|
|
for child := n.FirstChild; child != nil; {
|
|
|
|
|
next := child.NextSibling
|
|
|
|
|
n.RemoveChild(child)
|
|
|
|
|
child = next
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
entryIndex++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Render the complete structural template including container
|
|
|
|
|
var sb strings.Builder
|
|
|
|
|
html.Render(&sb, clonedChild)
|
|
|
|
|
|
|
|
|
|
return sb.String(), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// createVirtualElementFromTemplate creates a virtual DOM element from template HTML for API usage
|
|
|
|
|
// This allows API path to use the same structure extraction as enhancement path
|
|
|
|
|
func (e *ContentEngine) createVirtualElementFromTemplate(templateHTML string) (*html.Node, error) {
|
|
|
|
|
// Parse the template HTML
|
|
|
|
|
templateDoc, err := html.Parse(strings.NewReader(templateHTML))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to parse template HTML: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find the body element and extract the template structure
|
|
|
|
|
var templateBody *html.Node
|
|
|
|
|
e.walkNodes(templateDoc, func(n *html.Node) {
|
|
|
|
|
if n.Type == html.ElementNode && n.Data == "body" {
|
|
|
|
|
templateBody = n
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if templateBody != nil && templateBody.FirstChild != nil {
|
|
|
|
|
// Return the first child of body (the actual template element)
|
|
|
|
|
return templateBody.FirstChild, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("template does not contain valid structure")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CreateCollectionItemFromTemplate creates a collection item using the unified engine approach
|
|
|
|
|
// This replaces TemplateProcessor with engine-native functionality
|
|
|
|
|
func (e *ContentEngine) CreateCollectionItemFromTemplate(
|
|
|
|
|
siteID, collectionID string,
|
|
|
|
|
templateID int,
|
|
|
|
|
templateHTML string,
|
|
|
|
|
lastEditedBy string,
|
|
|
|
|
) (*CollectionItemWithTemplate, error) {
|
|
|
|
|
// Generate unique item ID
|
|
|
|
|
itemID := fmt.Sprintf("%s-item-%d", collectionID, time.Now().Unix())
|
|
|
|
|
|
|
|
|
|
// Create virtual element from template (like enhancement path)
|
|
|
|
|
virtualElement, err := e.createVirtualElementFromTemplate(templateHTML)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to create virtual element: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Process .insertr elements and create content entries (unified approach)
|
|
|
|
|
contentEntries, err := e.processChildElementsAsContent(virtualElement, siteID, itemID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to process content entries: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Generate structural template using unified engine method
|
|
|
|
|
structuralTemplate, err := e.generateStructuralTemplateFromChild(virtualElement, contentEntries)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to generate structural template: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create collection item with structural template
|
|
|
|
|
collectionItem, err := e.client.CreateCollectionItem(
|
|
|
|
|
siteID, collectionID, itemID, templateID, structuralTemplate, 0, lastEditedBy,
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to create collection item: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return collectionItem, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// cloneNode creates a deep copy of an HTML node
|
|
|
|
|
func (e *ContentEngine) cloneNode(node *html.Node) *html.Node {
|
|
|
|
|
cloned := &html.Node{
|
|
|
|
|
@@ -614,28 +820,6 @@ func (e *ContentEngine) cloneNode(node *html.Node) *html.Node {
|
|
|
|
|
return cloned
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// storeInitialCollectionItems stores original children as collection items (for existing collections)
|
|
|
|
|
func (e *ContentEngine) storeInitialCollectionItems(collectionNode *html.Node, collectionID, siteID string) error {
|
|
|
|
|
// Get existing templates for this collection
|
|
|
|
|
templates, err := e.client.GetCollectionTemplates(siteID, collectionID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to get collection templates: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(templates) == 0 {
|
|
|
|
|
fmt.Printf("⚠️ No templates found for collection %s, skipping initial items storage\n", collectionID)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Use template IDs from existing templates
|
|
|
|
|
var templateIDs []int
|
|
|
|
|
for _, template := range templates {
|
|
|
|
|
templateIDs = append(templateIDs, template.TemplateID)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return e.storeChildrenAsCollectionItems(collectionNode, collectionID, siteID, templateIDs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// storeChildrenAsCollectionItems stores HTML children as collection items in database
|
|
|
|
|
func (e *ContentEngine) storeChildrenAsCollectionItems(collectionNode *html.Node, collectionID, siteID string, templateIDs []int) error {
|
|
|
|
|
// Find existing children elements to store as items
|
|
|
|
|
@@ -653,24 +837,33 @@ func (e *ContentEngine) storeChildrenAsCollectionItems(collectionNode *html.Node
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Store each child as a collection item (database-first pattern like .insertr)
|
|
|
|
|
// Store each child using unified .insertr approach (content table + structural template)
|
|
|
|
|
for i, childElement := range childElements {
|
|
|
|
|
// Generate item ID (like content ID generation)
|
|
|
|
|
itemID := fmt.Sprintf("%s-initial-%d", collectionID, i+1)
|
|
|
|
|
|
|
|
|
|
// Extract HTML content from the child (reuse .insertr pattern)
|
|
|
|
|
htmlContent := e.extractHTMLContent(childElement)
|
|
|
|
|
// Process .insertr elements within this child (unified approach)
|
|
|
|
|
contentEntries, err := e.processChildElementsAsContent(childElement, siteID, itemID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to process content for item %s: %w", itemID, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Generate structural template with content IDs (no actual content)
|
|
|
|
|
structuralTemplate, err := e.generateStructuralTemplateFromChild(childElement, contentEntries)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to generate structural template for item %s: %w", itemID, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Use appropriate template ID (cycle through available templates)
|
|
|
|
|
templateID := templateIDs[i%len(templateIDs)]
|
|
|
|
|
|
|
|
|
|
// Store as collection item
|
|
|
|
|
_, err := e.client.CreateCollectionItem(siteID, collectionID, itemID, templateID, htmlContent, i+1, "system")
|
|
|
|
|
// Store structural template in collection_items (content lives in content table)
|
|
|
|
|
_, err = e.client.CreateCollectionItem(siteID, collectionID, itemID, templateID, structuralTemplate, i+1, "system")
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create collection item %s: %w", itemID, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Printf("✅ Stored initial collection item: %s (template %d)\n", itemID, templateID)
|
|
|
|
|
fmt.Printf("✅ Stored initial collection item: %s (template %d) with %d content entries\n", itemID, templateID, len(contentEntries))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|