diff --git a/hesabixCore/config/services.yaml b/hesabixCore/config/services.yaml index 8d2ea79..535cf57 100644 --- a/hesabixCore/config/services.yaml +++ b/hesabixCore/config/services.yaml @@ -8,6 +8,7 @@ parameters: archiveTempMediaDir: '%kernel.project_dir%/../hesabixArchive/temp' avatarDir: '%kernel.project_dir%/../hesabixArchive/avatars' sealDir: '%kernel.project_dir%/../hesabixArchive/seal' + SupportFilesDir: '%kernel.project_dir%/../hesabixArchive/support' services: # default configuration for services in *this* file _defaults: diff --git a/hesabixCore/src/Controller/SupportController.php b/hesabixCore/src/Controller/SupportController.php index d56487b..a021876 100644 --- a/hesabixCore/src/Controller/SupportController.php +++ b/hesabixCore/src/Controller/SupportController.php @@ -5,6 +5,7 @@ namespace App\Controller; use App\Entity\Settings; use App\Entity\Support; use App\Entity\User; +use App\Service\Access; use App\Service\Explore; use App\Service\Extractor; use App\Service\Jdate; @@ -14,205 +15,284 @@ use App\Service\registryMGR; use App\Service\SMS; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\ResponseHeaderBag; use Symfony\Component\Routing\Annotation\Route; -use Symfony\Component\Serializer\SerializerInterface; +use Symfony\Component\Security\Core\Exception\AccessDeniedException; class SupportController extends AbstractController { + // ثابت‌ها برای مدیریت خطاها + private const ERROR_TICKET_NOT_FOUND = ['error' => 1, 'message' => 'تیکت یافت نشد.']; + private const ERROR_NO_FILE = ['error' => 2, 'message' => 'فایلی برای این تیکت وجود ندارد.']; + private const ERROR_FILE_NOT_FOUND = ['error' => 3, 'message' => 'فایل در سرور یافت نشد.']; + private const ERROR_ACCESS_DENIED = ['error' => 4, 'message' => 'شما اجازه دسترسی به این فایل را ندارید.']; + private const ERROR_INVALID_PARAMS = ['error' => 999, 'message' => 'تمام موارد لازم را وارد کنید.']; + /** - * function to generate random strings - * @param int $length number of characters in the generated string - * @return string a new string is created with random characters of the desired length + * Generate a random string */ - private function RandomString($length = 32) + private function randomString(int $length = 32): string { - return substr(str_shuffle(str_repeat($x = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ', ceil($length / strlen($x)))), 1, $length); + return substr(str_shuffle(str_repeat('23456789ABCDEFGHJKLMNPQRSTUVWXYZ', ceil($length / 32))), 1, $length); + } + + /** + * Fetch ticket by ID and check ownership + */ + private function getTicket(EntityManagerInterface $entityManager, string $id, bool $checkOwnership = false): ?Support + { + $ticket = $entityManager->getRepository(Support::class)->find($id); + if (!$ticket || ($checkOwnership && $ticket->getSubmitter() !== $this->getUser())) { + return null; + } + return $ticket; } #[Route('/api/admin/support/list', name: 'app_admin_support_list')] public function app_admin_support_list(Extractor $extractor, EntityManagerInterface $entityManager): JsonResponse { $items = $entityManager->getRepository(Support::class)->findBy(['main' => 0], ['id' => 'DESC']); - $res = []; - foreach ($items as $item) { - $res[] = Explore::ExploreSupportTicket($item, $this->getUser()); - } + $res = array_map(fn($item) => Explore::ExploreSupportTicket($item, $this->getUser()), $items); return $this->json($extractor->operationSuccess($res)); } + #[Route('/api/admin/support/view/{id}', name: 'app_admin_support_view')] - public function app_admin_support_view(Extractor $extractor, Jdate $jdate, EntityManagerInterface $entityManager, string $id = ''): JsonResponse + public function app_admin_support_view(Extractor $extractor, EntityManagerInterface $entityManager, string $id): JsonResponse { - $item = $entityManager->getRepository(Support::class)->find($id); - if (!$item) + $item = $this->getTicket($entityManager, $id); + if (!$item) { throw $this->createNotFoundException(); - $replays = $entityManager->getRepository(Support::class)->findBy(['main' => $item->getId()]); - $res = []; - foreach ($replays as $replay) { - if ($replay->getSubmitter() == $this->getUser()) - $replay->setState(1); - else - $replay->setState(0); - $res[] = Explore::ExploreSupportTicket($replay, $this->getUser()); - } - return $this->json( - $extractor->operationSuccess([ - 'item' => Explore::ExploreSupportTicket($item, $this->getUser()), - 'replays' => $res - ]) - ); - } - #[Route('/api/admin/support/mod/{id}', name: 'app_admin_support_mod')] - public function app_admin_support_mod(registryMGR $registryMGR, SMS $SMS, Request $request, EntityManagerInterface $entityManager, Notification $notifi, string $id = ''): JsonResponse - { - $params = []; - if ($content = $request->getContent()) { - $params = json_decode($content, true); } - $item = $entityManager->getRepository(Support::class)->find($id); - if (!$item) - $this->createNotFoundException(); - if (array_key_exists('body', $params)) { - $support = new Support(); - $support->setDateSubmit(time()); - $support->setTitle('0'); - $support->setBody($params['body']); - $support->setState('0'); - $support->setMain($item->getId()); - $support->setSubmitter($this->getUser()); - $entityManager->persist($support); - $entityManager->flush(); - $item->setState('پاسخ داده شده'); - $entityManager->persist($support); - $entityManager->flush(); - //send sms to customer - if ($item->getSubmitter()->getMobile()) { - $SMS->send( - [$item->getId()], - $registryMGR->get('sms', 'ticketReplay'), - $item->getSubmitter()->getMobile() - ); - } - //send notification to user - $settings = $entityManager->getRepository(Settings::class)->findAll()[0]; - $url = '/profile/support-view/' . $item->getId(); - $notifi->insert("به درخواست پشتیبانی پاسخ داده شد", $url, null, $item->getSubmitter()); - return $this->json([ - 'error' => 0, - 'message' => 'successful' - ]); + $replays = $entityManager->getRepository(Support::class)->findBy(['main' => $item->getId()]); + $res = array_map(function ($replay) { + $replay->setState($replay->getSubmitter() === $this->getUser() ? 1 : 0); + return Explore::ExploreSupportTicket($replay, $this->getUser()); + }, $replays); + + return $this->json($extractor->operationSuccess([ + 'item' => Explore::ExploreSupportTicket($item, $this->getUser()), + 'replays' => $res + ])); + } + + #[Route('/api/admin/support/mod/{id}', name: 'app_admin_support_mod')] + public function app_admin_support_mod(registryMGR $registryMGR, SMS $SMS, Request $request, EntityManagerInterface $entityManager, Notification $notifi, string $id): JsonResponse + { + $params = $request->getPayload()->all(); + $item = $this->getTicket($entityManager, $id); + if (!$item) { + throw $this->createNotFoundException(); } + + if (!isset($params['body'])) { + return $this->json(self::ERROR_INVALID_PARAMS); + } + + $support = new Support(); + $support->setDateSubmit(time()) + ->setTitle('0') + ->setBody($params['body']) + ->setState('0') + ->setMain($item->getId()) + ->setSubmitter($this->getUser()); + + // ذخیره موقت برای گرفتن ID + $entityManager->persist($support); + $entityManager->flush(); + + // مدیریت فایل با متد handleFileUpload + $fileName = $this->handleFileUpload($request, $this->getParameter('SupportFilesDir'), $support->getId()); + if ($fileName) { + $support->setFileName($fileName); + } + + $entityManager->persist($support); + $item->setState('پاسخ داده شده'); + $entityManager->persist($item); + $entityManager->flush(); + + // بررسی سوئیچ ارسال SMS + $sendSms = filter_var($params['sendSms'] ?? true, FILTER_VALIDATE_BOOLEAN); // پیش‌فرض true + if ($sendSms && ($mobile = $item->getSubmitter()->getMobile())) { + $SMS->send([$item->getId()], $registryMGR->get('sms', 'ticketReplay'), $mobile); + } + + $settings = $entityManager->getRepository(Settings::class)->findAll()[0]; + $notifi->insert("به درخواست پشتیبانی پاسخ داده شد", "/profile/support-view/{$item->getId()}", null, $item->getSubmitter()); + return $this->json([ - 'error' => 999, - 'message' => 'تمام موارد لازم را وارد کنید.' + 'error' => 0, + 'message' => 'successful', + 'file' => $fileName ]); } + + /** + * Handle file upload and return filename + */ + private function handleFileUpload(Request $request, string $uploadDirectory, int $ticketId): ?string + { + if (!file_exists($uploadDirectory)) { + mkdir($uploadDirectory, 0777, true); + } + + $files = $request->files->get('files'); + if ($files && !empty($files)) { + $file = $files[0]; // فقط اولین فایل + $extension = $file->getClientOriginalExtension(); + $fileName = $ticketId . '.' . $extension; + $file->move($uploadDirectory, $fileName); + return $fileName; + } + return null; + } + #[Route('/api/support/list', name: 'app_support_list')] public function app_support_list(Jdate $jdate, EntityManagerInterface $entityManager): JsonResponse { $items = $entityManager->getRepository(Support::class)->findBy( - [ - 'submitter' => $this->getUser(), - 'main' => 0 - ], - [ - 'id' => 'DESC' - ] + ['submitter' => $this->getUser(), 'main' => 0], + ['id' => 'DESC'] ); + foreach ($items as $item) { $item->setDateSubmit($jdate->jdate('Y/n/d', $item->getDateSubmit())); } + return $this->json($items); } #[Route('/api/support/mod/{id}', name: 'app_support_mod')] public function app_support_mod(registryMGR $registryMGR, SMS $SMS, Request $request, EntityManagerInterface $entityManager, string $id = ''): JsonResponse { - $params = []; - if ($content = $request->getContent()) { - $params = json_decode($content, true); + $params = $request->getPayload()->all(); + $uploadDirectory = $this->getParameter('SupportFilesDir'); + if (!file_exists($uploadDirectory)) { + mkdir($uploadDirectory, 0777, true); } - if ($id == '') { - if (array_key_exists('title', $params) && array_key_exists('body', $params)) { - $item = new Support(); - $item->setBody($params['body']); - $item->setTitle($params['title']); - $item->setDateSubmit(time()); - $item->setSubmitter($this->getUser()); - $item->setMain(0); - $item->setCode($this->RandomString(8)); - $item->setState('در حال پیگیری'); - $entityManager->persist($item); - $entityManager->flush(); - //send sms to manager - $SMS->send( - [$item->getId()], - $registryMGR->get('sms', 'ticketRec'), - $registryMGR->get('ticket', 'managerMobile') - ); - return $this->json([ - 'error' => 0, - 'message' => 'ok', - 'url' => $item->getId() - ]); + if ($id === '') { + if (!isset($params['title'], $params['body'])) { + return $this->json(self::ERROR_INVALID_PARAMS); } - } else { - if (array_key_exists('body', $params)) { - $item = new Support(); - $upper = $entityManager->getRepository(Support::class)->find($id); - if ($upper) - $item->setMain($upper->getid()); - $item->setBody($params['body']); - $item->setTitle($upper->getTitle()); - $item->setDateSubmit(time()); - $item->setSubmitter($this->getUser()); - $item->setState('در حال پیگیری'); - $entityManager->persist($item); - $entityManager->flush(); - $upper->setState('در حال پیگیری'); - $entityManager->persist($upper); - $entityManager->flush(); - //send sms to manager - $SMS->send( - [$item->getId()], - $registryMGR->get('sms', 'ticketRec'), - $registryMGR->get('ticket', 'managerMobile') - ); - return $this->json([ - 'error' => 0, - 'message' => 'ok', - 'url' => $item->getId() - ]); + $item = new Support(); + $item->setBody($params['body']) + ->setTitle($params['title']) + ->setDateSubmit(time()) + ->setSubmitter($this->getUser()) + ->setMain(0) + ->setCode($this->randomString(8)) + ->setState('در حال پیگیری'); + + $entityManager->persist($item); + $entityManager->flush(); + + $fileName = $this->handleFileUpload($request, $uploadDirectory, $item->getId()); + if ($fileName) { + $item->setFileName($fileName); } + + $entityManager->persist($item); + $entityManager->flush(); + + $SMS->send([$item->getId()], $registryMGR->get('sms', 'ticketRec'), $registryMGR->get('ticket', 'managerMobile')); + + return $this->json([ + 'error' => 0, + 'message' => 'ok', + 'url' => $item->getId(), + 'files' => $fileName + ]); } + if (!isset($params['body'])) { + return $this->json(self::ERROR_INVALID_PARAMS); + } + + $upper = $this->getTicket($entityManager, $id); + if (!$upper) { + return $this->json(self::ERROR_TICKET_NOT_FOUND); + } + + $item = new Support(); + $item->setMain($upper->getId()) + ->setBody($params['body']) + ->setTitle($upper->getTitle()) + ->setDateSubmit(time()) + ->setSubmitter($this->getUser()) + ->setState('در حال پیگیری'); + + $entityManager->persist($item); + $entityManager->flush(); + + $fileName = $this->handleFileUpload($request, $uploadDirectory, $item->getId()); + if ($fileName) { + $item->setFileName($fileName); + } + + $entityManager->persist($item); + $upper->setState('در حال پیگیری'); + $entityManager->persist($upper); + $entityManager->flush(); + + $SMS->send([$item->getId()], $registryMGR->get('sms', 'ticketRec'), $registryMGR->get('ticket', 'managerMobile')); + return $this->json([ - 'error' => 999, - 'message' => 'تمام موارد لازم را وارد کنید.' + 'error' => 0, + 'message' => 'ok', + 'url' => $item->getId(), + 'files' => $fileName ]); } #[Route('/api/support/view/{id}', name: 'app_support_view')] - public function app_support_view(Jdate $jdate, EntityManagerInterface $entityManager, string $id = ''): JsonResponse + public function app_support_view(EntityManagerInterface $entityManager, string $id): JsonResponse { - $item = $entityManager->getRepository(Support::class)->find($id); - if (!$item) - throw $this->createNotFoundException(); - if ($item->getSubmitter() != $this->getUser()) + $item = $this->getTicket($entityManager, $id, true); + if (!$item) { throw $this->createAccessDeniedException(); - $replays = $entityManager->getRepository(Support::class)->findBy(['main' => $item->getId()]); - $replaysArray = []; - foreach ($replays as $replay) { - $replaysArray[] = Explore::ExploreSupportTicket($replay, $this->getUser()); } + + $replays = $entityManager->getRepository(Support::class)->findBy(['main' => $item->getId()]); + $replaysArray = array_map(fn($replay) => Explore::ExploreSupportTicket($replay, $this->getUser()), $replays); + return $this->json([ 'item' => Explore::ExploreSupportTicket($item, $this->getUser()), 'replays' => $replaysArray ]); } -} + + #[Route('/api/support/download/file/{id}', name: 'app_support_download_file')] + public function app_support_download_file(EntityManagerInterface $entityManager, string $id): Response + { + $ticket = $this->getTicket($entityManager, $id); + if (!$ticket) { + return $this->json(self::ERROR_TICKET_NOT_FOUND, 404); + } + + $currentUser = $this->getUser(); + if (!$currentUser || (!$this->isGranted('ROLE_ADMIN') && $ticket->getSubmitter() !== $currentUser)) { + return $this->json(self::ERROR_ACCESS_DENIED, 403); + } + + $fileName = $ticket->getFileName(); + if (!$fileName) { + return $this->json(self::ERROR_NO_FILE, 404); + } + + $filePath = $this->getParameter('SupportFilesDir') . '/' . $fileName; + if (!file_exists($filePath)) { + return $this->json(self::ERROR_FILE_NOT_FOUND, 404); + } + + return new BinaryFileResponse($filePath, 200, [ + 'Content-Disposition' => ResponseHeaderBag::DISPOSITION_ATTACHMENT . '; filename="' . $fileName . '"' + ]); + } +} \ No newline at end of file diff --git a/hesabixCore/src/Entity/Support.php b/hesabixCore/src/Entity/Support.php index 85ee98b..7cd8354 100644 --- a/hesabixCore/src/Entity/Support.php +++ b/hesabixCore/src/Entity/Support.php @@ -38,6 +38,9 @@ class Support #[ORM\Column(length: 255, nullable: true)] private ?string $code = null; + #[ORM\Column(length: 255, nullable: true)] + private ?string $fileName = null; + public function getId(): ?int { return $this->id; @@ -126,4 +129,16 @@ class Support return $this; } + + public function getFileName(): ?string + { + return $this->fileName; + } + + public function setFileName(?string $fileName): static + { + $this->fileName = $fileName; + + return $this; + } } diff --git a/hesabixCore/src/Service/Explore.php b/hesabixCore/src/Service/Explore.php index d575db4..5c96921 100644 --- a/hesabixCore/src/Service/Explore.php +++ b/hesabixCore/src/Service/Explore.php @@ -564,6 +564,7 @@ class Explore $res['submitter'] = self::ExploreUser($support->getSubmitter()); $res['main'] = $support->getMain(); $res['owner'] = true; + $res['fileName'] = $support->getFileName(); if ($user->getId() != $support->getSubmitter()->getId()) { $res['owner'] = false; }