From 551473f8e59b66b1d1b3fc23bca8a23b055d2214 Mon Sep 17 00:00:00 2001 From: isaacclad Date: Tue, 7 Jul 2026 00:47:01 +0000 Subject: [PATCH] Preserve WooCommerce HTML in product descriptions with a rich text editor. Stop stripping tags on sync so paragraphs and bold book titles round-trip to Woo. Co-authored-by: Cursor --- app/Services/Woo/CatalogWriteService.php | 7 +- app/Services/Woo/CategoryIngestService.php | 3 +- app/Services/Woo/ProductIngestService.php | 7 +- app/Support/WooHtml.php | 73 +++++++++++++++++++ resources/js/app.js | 2 + resources/js/woo-rich-text.js | 21 ++++++ .../views/components/woo/rich-text.blade.php | 37 ++++++++++ resources/views/woo/categories/form.blade.php | 8 +- resources/views/woo/products/form.blade.php | 16 ++-- tests/Unit/WooHtmlTest.php | 37 ++++++++++ 10 files changed, 192 insertions(+), 19 deletions(-) create mode 100644 app/Support/WooHtml.php create mode 100644 resources/js/woo-rich-text.js create mode 100644 resources/views/components/woo/rich-text.blade.php create mode 100644 tests/Unit/WooHtmlTest.php diff --git a/app/Services/Woo/CatalogWriteService.php b/app/Services/Woo/CatalogWriteService.php index 477f308..27b2aeb 100644 --- a/app/Services/Woo/CatalogWriteService.php +++ b/app/Services/Woo/CatalogWriteService.php @@ -5,6 +5,7 @@ namespace App\Services\Woo; use App\Models\WooCategory; use App\Models\WooProduct; use App\Models\WooStore; +use App\Support\WooHtml; class CatalogWriteService { @@ -112,8 +113,8 @@ class CatalogWriteService 'sale_price' => $this->majorPrice($input['sale_price'] ?? null), 'manage_stock' => (bool) ($input['manage_stock'] ?? false), 'stock_quantity' => isset($input['stock_quantity']) ? (int) $input['stock_quantity'] : null, - 'short_description' => $input['short_description'] ?? null, - 'description' => $input['description'] ?? null, + 'short_description' => WooHtml::sanitize($input['short_description'] ?? null), + 'description' => WooHtml::sanitize($input['description'] ?? null), 'categories' => $categories, 'images' => $input['images'] ?? null, ], fn ($value) => $value !== null && $value !== ''); @@ -127,7 +128,7 @@ class CatalogWriteService return array_filter([ 'name' => (string) ($input['name'] ?? ''), 'slug' => $input['slug'] ?? null, - 'description' => $input['description'] ?? null, + 'description' => WooHtml::sanitize($input['description'] ?? null), 'parent' => $parent > 0 ? $parent : 0, ], fn ($value) => $value !== null && $value !== ''); } diff --git a/app/Services/Woo/CategoryIngestService.php b/app/Services/Woo/CategoryIngestService.php index 00a11ce..2f2fff7 100644 --- a/app/Services/Woo/CategoryIngestService.php +++ b/app/Services/Woo/CategoryIngestService.php @@ -4,6 +4,7 @@ namespace App\Services\Woo; use App\Models\WooCategory; use App\Models\WooStore; +use App\Support\WooHtml; use Carbon\Carbon; class CategoryIngestService @@ -27,7 +28,7 @@ class CategoryIngestService 'parent_external_id' => $parent > 0 ? $parent : null, 'name' => (string) ($payload['name'] ?? 'Category'), 'slug' => (string) ($payload['slug'] ?? 'category-'.$externalId), - 'description' => $this->nullableString($payload['description'] ?? null), + 'description' => WooHtml::sanitize($payload['description'] ?? null), 'product_count' => max(0, (int) ($payload['count'] ?? 0)), 'wc_updated_at' => $this->updatedAt($payload), 'metadata' => [ diff --git a/app/Services/Woo/ProductIngestService.php b/app/Services/Woo/ProductIngestService.php index 164314c..ffb527c 100644 --- a/app/Services/Woo/ProductIngestService.php +++ b/app/Services/Woo/ProductIngestService.php @@ -4,6 +4,7 @@ namespace App\Services\Woo; use App\Models\WooProduct; use App\Models\WooStore; +use App\Support\WooHtml; use Carbon\Carbon; class ProductIngestService @@ -54,8 +55,8 @@ class ProductIngestService 'manage_stock' => (bool) ($payload['manage_stock'] ?? false), 'category_external_ids' => $categoryIds, 'images' => $images, - 'short_description' => $this->nullableString($payload['short_description'] ?? null), - 'description' => $this->nullableString($payload['description'] ?? null), + 'short_description' => WooHtml::sanitize($payload['short_description'] ?? null), + 'description' => WooHtml::sanitize($payload['description'] ?? null), 'wc_updated_at' => $this->updatedAt($payload), 'metadata' => [ 'permalink' => $payload['permalink'] ?? null, @@ -92,7 +93,7 @@ class ProductIngestService private function nullableString(mixed $value): ?string { - $text = trim(strip_tags((string) $value)); + $text = trim((string) $value); return $text === '' ? null : $text; } diff --git a/app/Support/WooHtml.php b/app/Support/WooHtml.php new file mode 100644 index 0000000..ab858f2 --- /dev/null +++ b/app/Support/WooHtml.php @@ -0,0 +1,73 @@ +