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
@@ -187,6 +187,12 @@ class AppointmentSettingsController extends BaseController
#[Route('/api/v1/appointment-settings/weekly-schedule', methods: ['POST'])]
public function createSchedule(Request $request, #[CurrentUser] User $user): JsonResponse
{
// پیش‌چکِ منشی، پیش از هر واکشی. چکِ اصلی (denyDoctorAccess) به پزشکِ
// همان رکورد نیاز دارد و بالا نمی‌رود؛ ولی سهمِ منشی از آن همیشه همین
// توگل است، پس این خط هیچ مسیرِ مجازی نمی‌بندد و فقط ۴۰۴ را به ۴۰۳
// تبدیل می‌کند. یافتهٔ ۱۰ آدیت ۲۰۲۶-۰۸-۰۷.
$this->secretaryAccess->denyUnlessGranted($user, 'appointment_settings', 'update');
$data = json_decode($request->getContent(), true) ?? [];
$doctorUuid = trim($data['doctor_uuid'] ?? '');
@@ -243,6 +249,12 @@ class AppointmentSettingsController extends BaseController
#[Route('/api/v1/appointment-settings/weekly-schedule/{uuid}', methods: ['PATCH'])]
public function updateSchedule(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
{
// پیش‌چکِ منشی، پیش از هر واکشی. چکِ اصلی (denyDoctorAccess) به پزشکِ
// همان رکورد نیاز دارد و بالا نمی‌رود؛ ولی سهمِ منشی از آن همیشه همین
// توگل است، پس این خط هیچ مسیرِ مجازی نمی‌بندد و فقط ۴۰۴ را به ۴۰۳
// تبدیل می‌کند. یافتهٔ ۱۰ آدیت ۲۰۲۶-۰۸-۰۷.
$this->secretaryAccess->denyUnlessGranted($user, 'appointment_settings', 'update');
$data = json_decode($request->getContent(), true) ?? [];
// uuid may be doctor uuid or schedule uuid
@@ -325,6 +337,12 @@ class AppointmentSettingsController extends BaseController
#[Route('/api/v1/booking-setting/{uuid}', methods: ['DELETE'])]
public function deleteSchedule(string $uuid, #[CurrentUser] User $user): JsonResponse
{
// پیش‌چکِ منشی، پیش از هر واکشی. چکِ اصلی (denyDoctorAccess) به پزشکِ
// همان رکورد نیاز دارد و بالا نمی‌رود؛ ولی سهمِ منشی از آن همیشه همین
// توگل است، پس این خط هیچ مسیرِ مجازی نمی‌بندد و فقط ۴۰۴ را به ۴۰۳
// تبدیل می‌کند. یافتهٔ ۱۰ آدیت ۲۰۲۶-۰۸-۰۷.
$this->secretaryAccess->denyUnlessGranted($user, 'appointment_settings', 'update');
$schedule = $this->scheduleRepo->findByUuid($uuid);
if ($schedule === null) {
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'برنامه یافت نشد', 404);
@@ -366,6 +384,12 @@ class AppointmentSettingsController extends BaseController
#[Route('/api/v1/appointment-settings/date-override', methods: ['POST'])]
public function createOverride(Request $request, #[CurrentUser] User $user): JsonResponse
{
// پیش‌چکِ منشی، پیش از هر واکشی. چکِ اصلی (denyDoctorAccess) به پزشکِ
// همان رکورد نیاز دارد و بالا نمی‌رود؛ ولی سهمِ منشی از آن همیشه همین
// توگل است، پس این خط هیچ مسیرِ مجازی نمی‌بندد و فقط ۴۰۴ را به ۴۰۳
// تبدیل می‌کند. یافتهٔ ۱۰ آدیت ۲۰۲۶-۰۸-۰۷.
$this->secretaryAccess->denyUnlessGranted($user, 'appointment_settings', 'update');
$data = json_decode($request->getContent(), true) ?? [];
$doctorUuid = trim($data['doctor_uuid'] ?? '');
$dateStr = trim($data['date'] ?? '');
@@ -399,6 +423,12 @@ class AppointmentSettingsController extends BaseController
#[Route('/api/v1/appointment-settings/date-override/{uuid}', methods: ['PATCH'])]
public function updateOverride(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
{
// پیش‌چکِ منشی، پیش از هر واکشی. چکِ اصلی (denyDoctorAccess) به پزشکِ
// همان رکورد نیاز دارد و بالا نمی‌رود؛ ولی سهمِ منشی از آن همیشه همین
// توگل است، پس این خط هیچ مسیرِ مجازی نمی‌بندد و فقط ۴۰۴ را به ۴۰۳
// تبدیل می‌کند. یافتهٔ ۱۰ آدیت ۲۰۲۶-۰۸-۰۷.
$this->secretaryAccess->denyUnlessGranted($user, 'appointment_settings', 'update');
$override = $this->overrideRepo->findByUuid($uuid);
if ($override === null) {
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'Override یافت نشد', 404);
@@ -425,6 +455,12 @@ class AppointmentSettingsController extends BaseController
#[Route('/api/v1/appointment-settings/date-override/{uuid}', methods: ['DELETE'])]
public function deleteOverride(string $uuid, #[CurrentUser] User $user): JsonResponse
{
// پیش‌چکِ منشی، پیش از هر واکشی. چکِ اصلی (denyDoctorAccess) به پزشکِ
// همان رکورد نیاز دارد و بالا نمی‌رود؛ ولی سهمِ منشی از آن همیشه همین
// توگل است، پس این خط هیچ مسیرِ مجازی نمی‌بندد و فقط ۴۰۴ را به ۴۰۳
// تبدیل می‌کند. یافتهٔ ۱۰ آدیت ۲۰۲۶-۰۸-۰۷.
$this->secretaryAccess->denyUnlessGranted($user, 'appointment_settings', 'update');
$override = $this->overrideRepo->findByUuid($uuid);
if ($override === null) {
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'Override یافت نشد', 404);
@@ -484,6 +520,12 @@ class AppointmentSettingsController extends BaseController
#[Route('/api/v1/appointment-settings/holidays/{uuid}', methods: ['DELETE'])]
public function deleteHoliday(string $uuid, #[CurrentUser] User $user): JsonResponse
{
// پیش‌چکِ منشی، پیش از هر واکشی. چکِ اصلی (denyDoctorAccess) به پزشکِ
// همان رکورد نیاز دارد و بالا نمی‌رود؛ ولی سهمِ منشی از آن همیشه همین
// توگل است، پس این خط هیچ مسیرِ مجازی نمی‌بندد و فقط ۴۰۴ را به ۴۰۳
// تبدیل می‌کند. یافتهٔ ۱۰ آدیت ۲۰۲۶-۰۸-۰۷.
$this->secretaryAccess->denyUnlessGranted($user, 'appointment_settings', 'update');
$holiday = $this->holidayRepo->findByUuid($uuid);
if ($holiday === null) {
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'تعطیلات یافت نشد', 404);
@@ -501,6 +543,12 @@ class AppointmentSettingsController extends BaseController
#[Route('/api/v1/appointment-settings/holidays', methods: ['POST'])]
public function createHoliday(Request $request, #[CurrentUser] User $user): JsonResponse
{
// پیش‌چکِ منشی، پیش از هر واکشی. چکِ اصلی (denyDoctorAccess) به پزشکِ
// همان رکورد نیاز دارد و بالا نمی‌رود؛ ولی سهمِ منشی از آن همیشه همین
// توگل است، پس این خط هیچ مسیرِ مجازی نمی‌بندد و فقط ۴۰۴ را به ۴۰۳
// تبدیل می‌کند. یافتهٔ ۱۰ آدیت ۲۰۲۶-۰۸-۰۷.
$this->secretaryAccess->denyUnlessGranted($user, 'appointment_settings', 'update');
$data = json_decode($request->getContent(), true) ?? [];
$doctorUuid = trim($data['doctor_uuid'] ?? '');
$startStr = trim($data['start_date'] ?? '');
@@ -541,6 +589,12 @@ class AppointmentSettingsController extends BaseController
#[Route('/api/v1/appointment-settings/holidays/{uuid}', methods: ['PATCH'])]
public function updateHoliday(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
{
// پیش‌چکِ منشی، پیش از هر واکشی. چکِ اصلی (denyDoctorAccess) به پزشکِ
// همان رکورد نیاز دارد و بالا نمی‌رود؛ ولی سهمِ منشی از آن همیشه همین
// توگل است، پس این خط هیچ مسیرِ مجازی نمی‌بندد و فقط ۴۰۴ را به ۴۰۳
// تبدیل می‌کند. یافتهٔ ۱۰ آدیت ۲۰۲۶-۰۸-۰۷.
$this->secretaryAccess->denyUnlessGranted($user, 'appointment_settings', 'update');
$holiday = $this->holidayRepo->findByUuid($uuid);
if ($holiday === null) {
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'تعطیلات یافت نشد', 404);