fix: References section link corruption and download filename issues
**Issue 1: URL corruption in "See this CV in..." links**
- Bug: replaceYearPlaceholder used fmt.Sprintf on ALL URLs
- URLs like "/?lang=es" were corrupted to "/?lang=es%!(EXTRA string=2025)"
- Fix: Changed to strings.ReplaceAll("{{YEAR}}", year)
- Result: Only replaces actual {{YEAR}} placeholders, leaves other URLs intact
**Issue 2: Download filename not respected**
- Bug: Shortcut URLs (cv-jamr-2025-en.pdf) redirected with HTTP 301
- Browsers used original URL filename instead of Content-Disposition header
- Fix: Generate PDF directly in DefaultCVShortcut handler
- Result: Returns PDF with correct filename in Content-Disposition header
Files changed:
- internal/models/cv.go: Fixed replaceYearPlaceholder function
- internal/handlers/cv.go: Changed redirect to direct PDF generation
Both fixes verified:
- "See this CV in Spanish" link: href="/?lang=es" ✓
- Download link: filename=cv-jamr-2025-en.pdf ✓
This commit is contained in:
+38
-5
@@ -260,13 +260,46 @@ func (h *CVHandler) DefaultCVShortcut(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Build redirect URL with default parameters (short + with_skills)
|
||||
redirectURL := fmt.Sprintf("/export/pdf?lang=%s&length=short&icons=show&version=with_skills", lang)
|
||||
// Generate PDF directly instead of redirecting
|
||||
// This ensures the Content-Disposition filename is respected by browsers
|
||||
log.Printf("Shortcut URL: %s → generating PDF (short + with_skills)", path)
|
||||
|
||||
log.Printf("Shortcut URL: %s → %s", path, redirectURL)
|
||||
// Prepare cookies for PDF generation (short, with_skills, light mode)
|
||||
cookies := map[string]string{
|
||||
"cv-length": "short",
|
||||
"cv-icons": "show",
|
||||
"cv-language": lang,
|
||||
"cv-theme": "default", // with_skills = default theme
|
||||
"color-theme": "light", // Always light for PDFs
|
||||
}
|
||||
|
||||
// Redirect to PDF export endpoint
|
||||
http.Redirect(w, r, redirectURL, http.StatusMovedPermanently)
|
||||
// Construct URL for PDF generation
|
||||
targetURL := fmt.Sprintf("http://%s/?lang=%s", h.serverAddr, lang)
|
||||
|
||||
// Generate PDF with screen render mode (for sidebar layout)
|
||||
ctx := r.Context()
|
||||
pdfData, err := h.pdfGenerator.GenerateFromURLWithOptions(ctx, targetURL, cookies, pdf.RenderModeScreen)
|
||||
if err != nil {
|
||||
log.Printf("PDF generation failed for shortcut URL: %v", err)
|
||||
HandleError(w, r, InternalError(err))
|
||||
return
|
||||
}
|
||||
|
||||
// Use the shortcut filename directly (simple, user-friendly)
|
||||
filename := filepath.Base(path) // cv-jamr-2025-en.pdf
|
||||
|
||||
// Set response headers with shortcut filename
|
||||
w.Header().Set("Content-Type", "application/pdf")
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename))
|
||||
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(pdfData)))
|
||||
|
||||
// Write PDF data
|
||||
if _, err := w.Write(pdfData); err != nil {
|
||||
log.Printf("Error writing PDF response: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("PDF generated successfully: %s (%d bytes)", filename, len(pdfData))
|
||||
}
|
||||
|
||||
// ExportPDF handles PDF export requests using chromedp
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -241,7 +242,7 @@ func LoadCV(lang string) (*CV, error) {
|
||||
|
||||
// replaceYearPlaceholder replaces {{YEAR}} with the current year
|
||||
func replaceYearPlaceholder(url string, year string) string {
|
||||
return fmt.Sprintf(url, year)
|
||||
return strings.ReplaceAll(url, "{{YEAR}}", year)
|
||||
}
|
||||
|
||||
// LoadUI loads UI translations from a JSON file for the specified language
|
||||
|
||||
Reference in New Issue
Block a user