5c3c27e0af
Раздел «Справка» (категории/статьи/предложка), правки ad_connector и обновление тестов feed под обязательную 2FA. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
37 lines
1.2 KiB
Python
37 lines
1.2 KiB
Python
"""Очистка HTML тел статей справки (как в feed)."""
|
|
from __future__ import annotations
|
|
|
|
import html
|
|
import re
|
|
|
|
import nh3
|
|
|
|
ALLOWED_ATTRIBUTES = {tag: set(attrs) for tag, attrs in nh3.ALLOWED_ATTRIBUTES.items()}
|
|
for _tag in (
|
|
'span', 'p', 'td', 'th', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
|
'li', 'ul', 'ol', 'div', 'img', 'table', 'blockquote', 'pre', 'figure', 'a',
|
|
):
|
|
ALLOWED_ATTRIBUTES.setdefault(_tag, set()).add('style')
|
|
|
|
|
|
def clean_html(value: str) -> str:
|
|
return nh3.clean(value or '', attributes=ALLOWED_ATTRIBUTES)
|
|
|
|
|
|
def plain_text_to_html(text: str) -> str:
|
|
"""Преобразует текст из textarea в безопасные параграфы."""
|
|
text = (text or '').replace('\r\n', '\n').replace('\r', '\n').strip()
|
|
if not text:
|
|
return ''
|
|
# Уже похоже на HTML — чистим как есть
|
|
if re.search(r'<[a-zA-Z][^>]*>', text):
|
|
return clean_html(text)
|
|
parts = [p.strip() for p in text.split('\n\n') if p.strip()]
|
|
if not parts:
|
|
parts = [text]
|
|
blocks = []
|
|
for part in parts:
|
|
escaped = html.escape(part).replace('\n', '<br>')
|
|
blocks.append(f'<p>{escaped}</p>')
|
|
return clean_html(''.join(blocks))
|