feat: add RichTextEditor component for rich text editing in articles

feat: create SanitizeBlogBodiesCommand to clean existing blog bodies according to current HTML sanitization policies

test: add AppointmentTreatmentSessionLinkTest to ensure appointment booking functionality works correctly with treatment session links
This commit is contained in:
hamed
2026-08-08 11:40:17 +03:30
parent 934405c42d
commit 47323daa27
36 changed files with 3461 additions and 12686 deletions
+42 -1
View File
@@ -296,7 +296,7 @@ Create a new blog post.
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `title` | string | ✅ | Post title (slug auto-generated) |
| `body` | string | ✅ | Full HTML body (from the admin CKEditor) |
| `body` | string | ✅ | Full HTML body (from the admin CKEditor). **پیش از ذخیره پاک‌سازی می‌شود** — [سیاست پاک‌سازی بدنه](#سیاست-پاکسازی-بدنه). |
| `summary` | string | ❌ | Short excerpt |
| `tags` | integer[] | ❌ | Array of tag IDs |
| `status` | string | ❌ | `"draft"` (default) or `"published"` |
@@ -582,6 +582,47 @@ Upload blog post header image.
---
## سیاست پاک‌سازی بدنه
بدنهٔ مقاله در **لحظهٔ ذخیره** پاک‌سازی می‌شود، نه هنگام نمایش — هر چهار نقطهٔ ورود
(`POST`/`PATCH` در `BlogController` و `RepresentationBlogController`) از
`App\Blog\Service\BlogBodySanitizer` می‌گذرند. دلیلش چند مصرف‌کننده بودنِ بدنه است:
پنل ادمین، سایت عمومی و فید. با پاک‌سازی در لایهٔ نمایش، هر مصرف‌کنندهٔ تازه دوباره
آسیب‌پذیر شروع می‌کرد.
سیاست در `config/packages/html_sanitizer.yaml` است. سه قاعده‌ای که رفتار قابل‌مشاهده
دارند:
- `<script>`, `<style>`, `<iframe>`, `<object>`, `<embed>`, `<form>`, `<input>`,
`<button>`, `<noscript>` با محتوایشان **حذف** می‌شوند (`drop` نه `block`) — وگرنه
`<script>alert(1)</script>` به متنِ `alert(1)` تبدیل می‌شد.
- روی هر `<a>` مقدار `rel="noopener noreferrer"` **تحمیل** می‌شود؛ طرح‌های مجاز فقط
`http`, `https`, `mailto`.
- `<table>` سه attributeِ ظاهریِ قدیمی را نگه می‌دارد — `border`, `cellpadding`,
`cellspacing` — ولی `style` را از **هر** عنصری حذف می‌کند. ظاهرِ جدولِ بدنهٔ مقاله
در پنل از `.blog-body` در `assets/admin/styles.css` می‌آید.
بدنه‌ای که پس از پاک‌سازی خالی شود `422` می‌گیرد، نه اینکه خالی ذخیره شود.
### پاک‌سازی مقاله‌های قدیمی
مقاله‌هایی که پیش از این سیاست ذخیره شده‌اند با یک دستور به همان وضع می‌رسند:
```bash
ddev exec php bin/console app:blog:sanitize-bodies --dry-run # گزارش، بدون نوشتن
ddev exec php bin/console app:blog:sanitize-bodies # اعمال
ddev exec php bin/console app:blog:sanitize-bodies --show=42 # قبل/بعدِ یک مقاله
```
گزارشِ dry-run تغییرها را تفکیک می‌کند: «سخت‌سازی» یعنی فقط `rel` اضافه یا entity
decode شده، و «حذفِ تگ یا attribute غیرمجاز» یعنی آن مقاله markupی داشته که سیاست
نمی‌پذیرد. بدونِ این تفکیک، عددِ کلِ تغییرات گمراه‌کننده است — در اجرای ۲۰۲۶-۰۸-۰۸ از
۴۲۶ مقالهٔ تغییریافته، ۳۹۸ فقط سخت‌سازی بودند.
دستور idempotent است: اجرای دوم صفر تغییر گزارش می‌دهد.
---
## باطل‌سازی کش سایت عمومی (webhook خروجی)
سایت عمومی (`nobat724_front`) پاسخ `GET /api/v1/blog/{slug}` را با `next: { revalidate: 3600, tags: [...] }` کش می‌کند. بدون باطل‌سازی، هر تغییر در پنل ادمین تا یک ساعت روی سایت دیده نمی‌شد.