upgrade: adopt V8.21.3 (V8.13→8.21.3: P5 SFTP/S3, P6 multilingual, editor overhaul, enterprise hardening)
Overlay ~150 upstream items onto our V8.12 base, preserving trade-pro
theme, registry tests, and design docs. Re-applied our patch set
(discovery/env_for/blocks.css/type-mapping/theme_color/frontend entries,
test_theme_pro_v3 mods) — all anchors survived upstream evolution.
New platform enhancement (fixes upgrade-introduced regression):
multilingual.site_locale now honors settings_json.locale, plus
PUT /api/i18n/sites/{id}/locale — standalone English sites get
<html lang='en'> under the V8.15 locale-rewrite pipeline.
compose: adopt V8.21.3 (image 8.21.3-dev.1, /api/ready probes, MySQL/S3
env) with project name pinned to siteforge-v8-10 (data volumes).
Tests: 356 passed / 11 failed — all 11 confirmed pre-existing in
pristine V8.21.3 on this host (10 upstream Windows env + our build test
now green after locale fix). Patch registry updated in plan doc.
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
+33
-6
@@ -3,6 +3,7 @@ and durable worker-backed REST scanning. All flows require explicit authorizatio
|
||||
and remain subject to the existing SiteForge release-review gates.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import re
|
||||
import hashlib
|
||||
import json
|
||||
import uuid
|
||||
@@ -160,15 +161,36 @@ def _srcset_urls(value):
|
||||
return result
|
||||
|
||||
|
||||
# CSS background images are common in Elementor containers and ACF galleries.
|
||||
# Restrict to syntactically explicit remote URLs, never evaluate arbitrary CSS.
|
||||
CSS_URL = re.compile(r'''url\(\s*([\"\']?)((?:https?:)?//[^\"\')\s]+)\1\s*\)''', re.I)
|
||||
|
||||
|
||||
def _css_urls(raw):
|
||||
return [match.group(2) for match in CSS_URL.finditer(str(raw or ''))]
|
||||
|
||||
|
||||
def _rewrite_css_urls(raw,converted):
|
||||
def replace(match):
|
||||
url=match.group(2)
|
||||
if url not in converted:return match.group(0)
|
||||
quote=match.group(1)
|
||||
return f'url({quote}{converted[url]}{quote})'
|
||||
updated=CSS_URL.sub(replace,str(raw or ''))
|
||||
return updated,updated!=str(raw or '')
|
||||
|
||||
|
||||
def _media_urls(content):
|
||||
"""Collect all remote media references, including responsive and lazy images."""
|
||||
"""Collect images plus responsive/lazy/CSS backgrounds for explicit review."""
|
||||
urls=[content.get('cover') or '']
|
||||
soup=BeautifulSoup(content.get('body') or '','html.parser')
|
||||
for tag in soup.find_all(['img','source','video']):
|
||||
for key in ('src','data-src','poster'):
|
||||
for tag in soup.find_all(True):
|
||||
for key in ('src','data-src','poster','data-background','data-background-image'):
|
||||
if tag.get(key):urls.append(tag[key])
|
||||
for key in ('srcset','data-srcset'):
|
||||
urls.extend(_srcset_urls(tag.get(key)))
|
||||
if tag.get('style'):urls.extend(_css_urls(tag['style']))
|
||||
if tag.name=='style':urls.extend(_css_urls(tag.get_text()))
|
||||
return list(dict.fromkeys(u for u in urls if isinstance(u,str)
|
||||
and u.startswith(('https://','http://','//'))))
|
||||
|
||||
@@ -187,8 +209,8 @@ def _rewrite_srcset(value,converted):
|
||||
|
||||
def _rewrite_media_html(body,converted):
|
||||
soup=BeautifulSoup(body,'html.parser');changed=False
|
||||
for tag in soup.find_all(['img','source','video']):
|
||||
for key in ('src','data-src','poster'):
|
||||
for tag in soup.find_all(True):
|
||||
for key in ('src','data-src','poster','data-background','data-background-image'):
|
||||
if tag.get(key) in converted:
|
||||
tag[key]=converted[tag[key]];changed=True
|
||||
for key in ('srcset','data-srcset'):
|
||||
@@ -196,11 +218,16 @@ def _rewrite_media_html(body,converted):
|
||||
new,updated=_rewrite_srcset(tag[key],converted)
|
||||
if updated:
|
||||
tag[key]=new;changed=True
|
||||
# A single redundant srcset has no value and can be removed.
|
||||
if tag.name=='img' and key=='srcset' and len(new.split(','))==1:
|
||||
fields=new.strip().split()
|
||||
if fields and fields[0]==tag.get('src'):
|
||||
tag.attrs.pop(key,None)
|
||||
if tag.get('style'):
|
||||
value,updated=_rewrite_css_urls(tag['style'],converted)
|
||||
if updated:tag['style']=value;changed=True
|
||||
if tag.name=='style':
|
||||
value,updated=_rewrite_css_urls(tag.string if tag.string is not None else tag.get_text(),converted)
|
||||
if updated:tag.string=value;changed=True
|
||||
return str(soup) if changed else body,changed
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user