# Blog Image Storage Guide

**Last Updated:** 2026-01-10

## Overview

This guide documents the blog image storage system, including how images are downloaded, converted, stored, and referenced in blog posts.

## Storage Structure

### Directory Location

- **Physical Storage:** `/v2/img/insights/` (aligns with website structure)
- **Public URL:** `/insights/bilder/` (SEO-optimized German path)
- **Format:** Direct paths (not proxy) for better SEO and performance
- **Structure:** Flat structure (no subdirectories) for simplicity
- **URL Rewrite:** Apache `.htaccess` rewrites `/insights/bilder/` → `/v2/img/insights/`

### Image Formats

- **WebP:** All images converted to WebP format (except SVGs)
- **SVG:** Preserved as-is (not converted)
- **Quality:** WebP quality set to 85% (good balance of quality/size)

## Image Processing Pipeline

### 1. Download Images

**Script:** `scripts/blog/download-images.py`

Downloads all unique images from WordPress URLs:

```bash
python3 scripts/blog/download-images.py
```

**Process:**

- Loads unique images from `docs/data/blog-images-list.json`
- Downloads each image from WordPress URL
- Handles errors and retries (3 attempts)
- Saves to `/v2/img/insights/` directory (physical storage)
- Generates download report: `docs/data/blog-image-download-report.json`

**Statistics:**

- Total unique images: 191
- Featured images: 98
- Content images: 93

### 2. Convert to WebP

**Script:** `scripts/blog/convert-images.py`

Converts PNG/JPG/JPEG images to WebP format:

```bash
python3 scripts/blog/convert-images.py
```

**Process:**

- Converts PNG (98), JPG (55), JPEG (37) to WebP
- Preserves SVG (1) as-is
- Optimizes WebP quality (85%)
- Removes original files after conversion
- Generates conversion report: `docs/data/blog-image-conversion-report.json`

**Results:**

- Average size reduction: ~70-90% for PNGs
- Average size reduction: ~20-40% for JPGs
- Total images: 186 (185 WebP + 1 SVG)

### 3. Update Post JSON Files

**Script:** `scripts/blog/update-post-paths.py`

Updates image paths in post JSON files:

```bash
python3 scripts/blog/update-post-paths.py
```

**Process:**

- Updates `featured_image.src` paths
- Updates `images[]` array paths
- Maps WordPress URLs to local paths
- Preserves all other image metadata (alt, width, height)
- Generates path mapping: `docs/data/blog-image-path-mapping.json`

### 4. Update HTML Content

**Script:** `scripts/blog/update-html-paths.py`

Updates WordPress URLs in HTML content:

```bash
python3 scripts/blog/update-html-paths.py
```

**Process:**

- Updates `src` attributes in `<img>` tags
- Updates `srcset` attributes (converts to local paths)
- Preserves all other attributes
- Handles HTML entity encoding

### 5. Fix SVG References

**Script:** `scripts/blog/fix-svg-references.py`

Fixes SVG references (e.g., `ordio_white.webp` → `ordio_white.svg`):

```bash
python3 scripts/blog/fix-svg-references.py
```

## Verification

### Verify Images Exist

**Script:** `scripts/blog/verify-images.py`

Verifies that all referenced images exist:

```bash
python3 scripts/blog/verify-images.py
```

**Checks:**

- Featured images exist
- Images array references exist
- HTML content image references exist
- Generates verification report: `docs/data/blog-image-verification-report.json`

### Validate JSON Paths

**Script:** `scripts/blog/validate-json-paths.py`

Validates that all paths are correct and no WordPress URLs remain:

```bash
python3 scripts/blog/validate-json-paths.py
```

**Checks:**

- Featured image paths are local
- Images array paths are local
- HTML content has no WordPress URLs
- JSON syntax is valid

## Code Components

### Helper Functions

**File:** `v2/config/blog-template-helpers.php`

**Function:** `get_image_with_fallback()`

- **Purpose:** Get image data from featured image array
- **Behavior:** Returns image data if local file exists, otherwise null
- **No Fallback:** WordPress URL lookup removed (all images should be stored locally)

### Post Header Component

**File:** `v2/components/blog/PostHeader.php`

- Displays featured image
- Uses `get_image_with_fallback()` to get image data
- No WordPress fallback logic

### Post Content Component

**File:** `v2/components/blog/PostContent.php`

- Processes content images
- Defensive cleanup for any remaining WordPress URLs
- Adds proper attributes (loading, alt, width, height, sizes)
- Converts WordPress URLs to local paths if found

## Path Format

### Public URL Format

```
/insights/bilder/filename.webp
```

### Examples

- Featured image: `/insights/bilder/Uberstunden-Rechte-Pflichten-Gesetze.webp`
- Content image: `/insights/bilder/Ordio-Personal-828-1-768x512.webp`
- SVG: `/insights/bilder/ordio_white.svg`

**Note:** The public URL `/insights/bilder/` is rewritten to the physical storage location `/v2/img/insights/` via Apache `.htaccess` rewrite rule.

## Best Practices

### Adding New Images

1. **Download:** Add image to WordPress or download directly
2. **Convert:** Convert to WebP format (quality 85%)
3. **Store:** Place in `/v2/img/insights/` directory (physical storage)
4. **Reference:** Use public URL `/insights/bilder/filename.webp` (SEO-optimized)
5. **Update:** Update post JSON file with correct path

### Image Optimization

- **Format:** Use WebP for all images (except SVGs)
- **Quality:** 85% WebP quality (good balance)
- **Dimensions:** Preserve original dimensions
- **Transparency:** Handle PNG transparency correctly

### Path References

- **Featured Images:** Use in `featured_image.src`
- **Content Images:** Use in `images[]` array and HTML content
- **Direct Paths:** Use direct paths (not proxy) for SEO

## Verification Scripts

### Comprehensive Audit

**Script:** `v2/scripts/blog/comprehensive-image-audit.py`

Runs all verification checks and generates comprehensive reports:

```bash
python3 v2/scripts/blog/comprehensive-image-audit.py
```

**Checks:**

- Image references audit
- Physical images audit
- Image mapping cross-reference
- Featured images verification
- Content images verification
- URL rewrite testing
- Image wrapping testing

### Image References Audit

**Script:** `v2/scripts/blog/audit-image-references.py`

Extracts all image references from all blog post JSON files:

```bash
python3 v2/scripts/blog/audit-image-references.py
```

**Output:** `docs/data/blog-image-references-audit.json`

**Checks:**

- Featured images per post
- Images array entries per post
- HTML content image references
- WordPress URLs still present
- Local paths referenced

### Physical Images Audit

**Script:** `v2/scripts/blog/audit-physical-images.py`

Catalogs all files in `/v2/img/insights/`:

```bash
python3 v2/scripts/blog/audit-physical-images.py --check-duplicates
```

**Output:** `docs/data/blog-physical-images-audit.json`

**Checks:**

- All files with sizes, formats, modification dates
- Orphaned files (not referenced in any post)
- Duplicate files (same content, different names)

### Image Mapping Audit

**Script:** `v2/scripts/blog/audit-image-mapping.py`

Cross-references image references with physical files:

```bash
python3 v2/scripts/blog/audit-image-mapping.py
```

**Output:** `docs/data/blog-image-mapping-audit.json`

**Checks:**

- Posts with missing images
- Posts with broken image paths
- Posts with WordPress URLs
- Posts with incorrect formats
- Posts with missing alt text
- Posts with missing dimensions

### Featured Images Verification

**Script:** `v2/scripts/blog/verify-featured-images.py`

Verifies all featured images:

```bash
python3 v2/scripts/blog/verify-featured-images.py
```

**Output:** `docs/data/blog-featured-images-verification.json`

**Checks:**

- All featured images use `/insights/bilder/` format
- All featured image files exist
- All featured images are WebP or SVG
- All featured images have valid alt text
- All featured images have width/height

### Content Images Verification

**Script:** `v2/scripts/blog/verify-content-images.py`

Verifies all HTML content images:

```bash
python3 v2/scripts/blog/verify-content-images.py
```

**Output:** `docs/data/blog-content-images-verification.json`

**Checks:**

- All `<img src>` attributes use `/insights/bilder/` format
- All `srcset` attributes use `/insights/bilder/` format
- All referenced files exist
- No WordPress URLs remain in HTML content
- All images have proper attributes (alt, loading, width, height)

### URL Rewrite Testing

**Script:** `v2/scripts/blog/test-url-rewrite.py`

Tests that `.htaccess` rewrite rule works correctly:

```bash
python3 v2/scripts/blog/test-url-rewrite.py --sample-size 30
```

**Output:** `docs/data/blog-url-rewrite-test.json`

**Checks:**

- Image URLs are accessible (200 status)
- Rewrite rule is working correctly

### Image Health Monitoring

**Script:** `v2/scripts/blog/monitor-image-health.py`

Continuous monitoring script for image issues:

```bash
python3 v2/scripts/blog/monitor-image-health.py
```

**Output:** `docs/data/blog-image-health-monitor.json`

**Checks:**

- WordPress URLs detected
- Missing image files
- Logs results to `docs/data/blog-image-health-monitor.log`

Can be run periodically (e.g., via cron) to catch new issues.

## Troubleshooting

### Missing Images

1. **Check File Exists:** Verify image exists in `/v2/img/insights/` (physical storage)
2. **Check URL Rewrite:** Verify `.htaccess` rewrite rule is active
3. **Check Path:** Verify path in post JSON uses `/insights/bilder/` (public URL)
4. **Check Format:** Ensure WebP format (except SVGs)
5. **Check Permissions:** Ensure file permissions are correct (644)
6. **Run Verification:** Use `verify-content-images.py` to identify missing files
7. **Check srcset:** Missing srcset variants are normal (browser falls back to main src)

### WordPress URLs Still Present

1. **Run Update Script:** Run `convert-wordpress-urls-in-json.py` again
2. **Check HTML Entities:** Some URLs may have HTML entity encoding
3. **Defensive Cleanup:** PostContent component handles remaining URLs
4. **Run Audit:** Use `audit-image-references.py` to find remaining WordPress URLs

### SVG References

1. **Check Extension:** Ensure SVG references use `.svg` extension
2. **Run Fix Script:** Run `fix-svg-references.py` if needed
3. **Verify File:** Ensure SVG file exists

### Images Not Displaying

1. **Check CSS:** Verify `blog.css` image rules are correct
2. **Check Minification:** Run `npm run minify` to update `blog.min.css`
3. **Check Browser Console:** Look for JavaScript errors
4. **Check Network Tab:** Verify images return 200 status
5. **Check Lightbox Wrapper:** Verify images are wrapped correctly for lightbox

### Common Issues

**Issue:** Images appear broken in browser

- **Solution:** Check file exists, verify rewrite rule, check path format

**Issue:** Images load but don't display

- **Solution:** Check CSS rules, verify minified CSS is up to date

**Issue:** Missing srcset variants

- **Solution:** This is normal - browser falls back to main src. Only fix if main src is missing.

**Issue:** WordPress URLs in HTML content

- **Solution:** Run `convert-wordpress-urls-in-json.py` and verify PostContent.php defensive cleanup

## Reports

All scripts generate reports in `docs/data/`:

### Audit Reports

- `blog-image-references-audit.json` - All image references across posts
- `blog-physical-images-audit.json` - Physical file inventory
- `blog-image-mapping-audit.json` - Cross-reference analysis
- `blog-comprehensive-audit.json` - Comprehensive audit results

### Verification Reports

- `blog-featured-images-verification.json` - Featured images verification
- `blog-content-images-verification.json` - Content images verification
- `blog-url-rewrite-test.json` - URL rewrite testing results
- `blog-image-wrapping-test.json` - Image wrapping pattern analysis

### Processing Reports

- `blog-image-download-report.json` - Download statistics
- `blog-image-conversion-report.json` - Conversion statistics
- `blog-image-path-mapping.json` - Path mapping reference
- `blog-image-verification-report.json` - Verification results

### Monitoring Reports

- `blog-image-health-monitor.json` - Health monitoring results
- `blog-image-health-monitor.log` - Health monitoring log file

## Maintenance

### Regular Tasks

1. **Download New Images:** Run download script for new images
2. **Convert to WebP:** Run conversion script
3. **Update Paths:** Run update scripts
4. **Verify:** Run verification scripts
5. **Validate:** Run validation scripts
6. **Monitor Health:** Run `monitor-image-health.py` periodically
7. **Run Comprehensive Audit:** Run `comprehensive-image-audit.py` monthly

### Automated Fixes

**Fix Image Attributes:** `v2/scripts/blog/fix-image-attributes.py`

Adds missing alt text, dimensions, and loading attributes:

```bash
python3 v2/scripts/blog/fix-image-attributes.py --dry-run  # Preview changes
python3 v2/scripts/blog/fix-image-attributes.py  # Apply fixes
```

**Fix Missing Images:** `v2/scripts/blog/fix-missing-images.py`

Attempts to download missing images (requires original WordPress URLs):

```bash
python3 v2/scripts/blog/fix-missing-images.py --dry-run
```

**Convert WordPress URLs:** `v2/scripts/blog/convert-wordpress-urls-in-json.py`

Converts remaining WordPress URLs to local paths:

```bash
python3 v2/scripts/blog/convert-wordpress-urls-in-json.py --dry-run
python3 v2/scripts/blog/convert-wordpress-urls-in-json.py
```

### Cleanup

- Remove temporary download files
- Remove unused functions
- Update documentation
- Review reports
- Archive old audit reports

## Related Documentation

- [IMAGE_FIX_SUMMARY.md](./IMAGE_FIX_SUMMARY.md) - Image display fix summary
- [FEATURED_IMAGE_FALLBACK_FIX.md](./FEATURED_IMAGE_FALLBACK_FIX.md) - Featured image fallback fix
- [BLOG_TYPOGRAPHY_GUIDE.md](./BLOG_TYPOGRAPHY_GUIDE.md) - Typography and content formatting
