src/EventSubscriber/NotificationSubscriber.php line 193

Open in your IDE?
  1. <?php
  2. namespace App\EventSubscriber;
  3. use App\DBAL\Types\CardBrandType;
  4. use App\DBAL\Types\CustomerCommentType;
  5. use App\DBAL\Types\EventPriorityType;
  6. use App\DBAL\Types\EventType;
  7. use App\DBAL\Types\InterviewNoteType;
  8. use App\DBAL\Types\MailboxType;
  9. use App\DBAL\Types\NotificationCommandType;
  10. use App\DBAL\Types\StatusType;
  11. use App\DBAL\Types\TaxReturnFormType;
  12. use App\DBAL\Types\TaxReturnType;
  13. use App\DBAL\Types\TransactionMethodType;
  14. use App\DBAL\Types\TransactionType;
  15. use App\Entity\Chat;
  16. use App\Entity\Config\InterviewNoteMethod;
  17. use App\Entity\Customer;
  18. use App\Entity\Event;
  19. use App\Entity\Feed;
  20. use App\Entity\Issue;
  21. use App\Entity\Lead;
  22. use App\Entity\Project;
  23. use App\Entity\Stream;
  24. use App\Entity\User;
  25. use App\Events;
  26. use App\Service\FayeClient;
  27. use App\Service\MergeFields;
  28. use Doctrine\Common\Collections\ArrayCollection;
  29. use Doctrine\Common\Collections\Collection;
  30. use Doctrine\Persistence\ManagerRegistry;
  31. use EWZ\SymfonyAdminBundle\Event\ObjectEvent;
  32. use EWZ\SymfonyAdminBundle\Event\ReportExportedEvent;
  33. use EWZ\SymfonyAdminBundle\Events as SABEvents;
  34. use EWZ\SymfonyAdminBundle\Util\CommandRunner;
  35. use EWZ\SymfonyAdminBundle\Util\StringUtil;
  36. use Symfony\Component\Asset\Packages;
  37. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  38. use Symfony\Component\EventDispatcher\GenericEvent;
  39. use Symfony\Component\HttpFoundation\Exception\SessionNotFoundException;
  40. use Symfony\Component\HttpFoundation\RequestStack;
  41. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  42. use Symfony\Component\HttpKernel\KernelInterface;
  43. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  44. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  45. use Symfony\Contracts\Translation\TranslatorInterface;
  46. use Twig\Environment as TwigEnvironment;
  47. /**
  48.  * Notify user base on object changes.
  49.  */
  50. class NotificationSubscriber implements EventSubscriberInterface
  51. {
  52.     /** @var TwigEnvironment */
  53.     private $twig;
  54.     /** @var KernelInterface */
  55.     private $kernel;
  56.     /** @var TranslatorInterface */
  57.     private $translator;
  58.     /** @var TokenStorageInterface */
  59.     private $tokenStorage;
  60.     /** @var RequestStack */
  61.     private $requestStack;
  62.     /** @var UrlGeneratorInterface */
  63.     private $urlGenerator;
  64.     /** @var ManagerRegistry */
  65.     private $managerRegistry;
  66.     /** @var Packages */
  67.     private $assetsManager;
  68.     /** @var MergeFields */
  69.     private $mergeFields;
  70.     /** @var FayeClient */
  71.     private $fayeClient;
  72.     /** @var string */
  73.     private $personalCheckTo;
  74.     /** @var string */
  75.     private $pendingUrl;
  76.     /** @var string */
  77.     private $customerUrl;
  78.     /**
  79.      * @param TwigEnvironment       $twig
  80.      * @param KernelInterface       $kernel
  81.      * @param TokenStorageInterface $tokenStorage
  82.      * @param RequestStack          $requestStack
  83.      * @param UrlGeneratorInterface $urlGenerator
  84.      * @param ManagerRegistry       $managerRegistry
  85.      * @param TranslatorInterface   $translator
  86.      * @param Packages              $assetsManager
  87.      * @param MergeFields           $mergeFields
  88.      * @param FayeClient            $fayeClient
  89.      * @param string                $personalCheckTo
  90.      * @param string                $pendingUrl
  91.      * @param string                $customerUrl
  92.      */
  93.     public function __construct(
  94.         TwigEnvironment $twig,
  95.         KernelInterface $kernel,
  96.         TokenStorageInterface $tokenStorage,
  97.         RequestStack $requestStack,
  98.         UrlGeneratorInterface $urlGenerator,
  99.         ManagerRegistry $managerRegistry,
  100.         TranslatorInterface $translator,
  101.         Packages $assetsManager,
  102.         MergeFields $mergeFields,
  103.         FayeClient $fayeClient,
  104.         string $personalCheckTo,
  105.         string $pendingUrl,
  106.         string $customerUrl
  107.     ) {
  108.         $this->twig $twig;
  109.         $this->kernel $kernel;
  110.         $this->tokenStorage $tokenStorage;
  111.         $this->requestStack $requestStack;
  112.         $this->urlGenerator $urlGenerator;
  113.         $this->managerRegistry $managerRegistry;
  114.         $this->translator $translator;
  115.         $this->assetsManager $assetsManager;
  116.         $this->mergeFields $mergeFields;
  117.         $this->fayeClient $fayeClient;
  118.         $this->personalCheckTo $personalCheckTo;
  119.         $this->pendingUrl $pendingUrl;
  120.         $this->customerUrl $customerUrl;
  121.     }
  122.     /**
  123.      * {@inheritdoc}
  124.      */
  125.     public static function getSubscribedEvents(): array
  126.     {
  127.         return [
  128.             Events::LEAD_IMPORT_COMPLETED => 'onLeadImported',
  129.             SABEvents::REPORT_EXPORT_COMPLETED => 'onReportExported',
  130.             SABEvents::NOTIFICATION_OBJECT_CREATED => 'onObjectCreated',
  131.             Events::NOTIFICATION_USER_ASSIGNED => 'onUserAssigned',
  132.             Events::NOTIFICATION_EVENT_POKED => 'onEventPoked',
  133.             Events::NOTIFICATION_EVENT_DUE => 'onEventDue',
  134.             Events::NOTIFICATION_EVENT_UPCOMING => 'onEventUpcoming',
  135.             Events::NOTIFICATION_UPSELL_REQUEST_POKED => 'onUpsellRequestPoked',
  136.             Events::NOTIFICATION_PROJECT_POKED => 'onProjectPoked',
  137.             Events::NOTIFICATION_MESSAGE_SENT => 'onMessageSent',
  138.             Events::NOTIFICATION_CUSTOMER_QA_SIGNED => 'onCustomerQASigned',
  139.             Events::NOTIFICATION_CUSTOMER_STAGE_ITEM_COMPLETED => 'onCustomerStageItemCompleted',
  140.             Events::NOTIFICATION_CUSTOMER_STAGE_COMPLETED => 'onCustomerStageCompleted',
  141.             Events::NOTIFICATION_CUSTOMER_TRANSACTION_UPDATED => 'onCustomerTransactionUpdated',
  142.             Events::NOTIFICATION_CHAT_MESSAGE_CREATED => 'onChatMessageCreated',
  143.             Events::NOTIFICATION_CHAT_MESSAGE_REACTED => 'onChatMessageReacted',
  144.             Events::NOTIFICATION_CHAT_DELETED => 'onChatDeleted',
  145.         ];
  146.     }
  147.     /**
  148.      * @param GenericEvent $event
  149.      */
  150.     public function onLeadImported(GenericEvent $event): void
  151.     {
  152.         /** @var User $user */
  153.         $user $event->getSubject();
  154.         $this->addNotification($userNotificationCommandType::LEAD_IMPORTED$user$event->getArguments());
  155.     }
  156.     /**
  157.      * @param ReportExportedEvent $event
  158.      */
  159.     public function onReportExported(ReportExportedEvent $event): void
  160.     {
  161.         $report $event->getReport();
  162.         $user $event->getUser();
  163.         $fileName $event->getFileName();
  164.         $this->addNotification($reportNotificationCommandType::REPORT_EXPORTED$user, [
  165.             'file_name' => $fileName,
  166.         ]);
  167.     }
  168.     /**
  169.      * @param ObjectEvent $event
  170.      */
  171.     public function onObjectCreated(ObjectEvent $event): void
  172.     {
  173.         $object $event->getObject();
  174.         // set comment body
  175.         $commentBody $this->translator->trans('notification.object_created', [
  176.             '%actor%' => $this->getUser() ?: '—',
  177.             '%article%' => $this->getIndefiniteArticle($this->formatObjectName($object)),
  178.             '%object_id%' => $object->getId(),
  179.             '%object_url%' => '#',
  180.             '%object_name%' => $this->formatObjectName($object),
  181.             '%object_extra%' => null,
  182.             '%object_properties%' => null,
  183.             '%object_body%' => null,
  184.         ]);
  185.         switch (\get_class($object)) {
  186.             case Customer\Comment\ComplaintComment::class:
  187.                 $commentMethod $this->getInterviewNoteMethod('Complaint');
  188.                 $objectId $object->getObject()->getId();
  189.                 // no break
  190.             case Customer\Comment\TaxReturnComment::class:
  191.                 $commentMethod $commentMethod ?? $this->getInterviewNoteMethod('Returns');
  192.                 $objectId $objectId ?? sprintf('%d %d %s %s',
  193.                     $object->getObject()->getId(),
  194.                     $object->getObject()->getYear(),
  195.                     $object->getObject()->getType()
  196.                         ? TaxReturnType::getReadableValue($object->getObject()->getType())
  197.                         : '—',
  198.                     $object->getObject()->getForm()
  199.                         ? TaxReturnFormType::getReadableValue($object->getObject()->getForm())
  200.                         : '—'
  201.                 );
  202.                 // no break
  203.             case Customer\Comment\UpsellRequestComment::class:
  204.                 $commentMethod $commentMethod ?? $this->getInterviewNoteMethod('Upsell Req.');
  205.                 $objectId $objectId ?? $object->getObject()->getId();
  206.                 $objectName $object->getType()
  207.                     ? CustomerCommentType::getReadableValue($object->getType())
  208.                     : '—';
  209.                 $commentBody $this->translator->trans('notification.object_commented', [
  210.                     '%actor%' => $this->getUser() ?: '—',
  211.                     '%article%' => $this->getIndefiniteArticle($objectName),
  212.                     '%object_id%' => $objectId,
  213.                     '%object_url%' => $this->urlGenerator->generate(
  214.                         'admin_customers_view',
  215.                         [
  216.                             'id' => $object->getObject()->getCustomer()->getId(),
  217.                             'tab' => 'upsells',
  218.                             'filters' => json_encode(['id' => $objectId]),
  219.                         ]
  220.                     ),
  221.                     '%object_name%' => $objectName,
  222.                     '%object_body%' => $object->getBody(),
  223.                 ]);
  224.                 $this->addInterviewNote(
  225.                     $object->getCustomer(),
  226.                     $commentBody,
  227.                     null,
  228.                     $commentMethod
  229.                 );
  230.                 /** @var User $user */
  231.                 $user method_exists('getTargetUser'$object->getObject())
  232.                     ? $object->getObject()->getTargetUser()
  233.                     : $object->getObject()->getUser();
  234.                 if ($user
  235.                     && $user->isEnabled()
  236.                     && $user->isNotifyOnAssignedCustomer()
  237.                     && $user->hasEntityAccess('customer')
  238.                 ) {
  239.                     $this->addNotification($objectNotificationCommandType::CREATED_COMMENT$user);
  240.                 }
  241.                 break;
  242.             case Customer\TaxReturn::class:
  243.                 $commentBody $this->translator->trans('notification.object_created', [
  244.                     '%actor%' => $this->getUser() ?: '—',
  245.                     '%article%' => $this->getIndefiniteArticle($this->formatObjectName($object)),
  246.                     '%object_id%' => $object->getId(),
  247.                     '%object_url%' => $this->urlGenerator->generate(
  248.                         'admin_customers_view',
  249.                         [
  250.                             'id' => $object->getCustomer()->getId(),
  251.                             'tab' => 'returns',
  252.                             'filters' => json_encode(['id' => $object->getId()]),
  253.                         ]
  254.                     ),
  255.                     '%object_name%' => $this->formatObjectName($object),
  256.                     '%object_extra%' => null,
  257.                     '%object_properties%' => null,
  258.                     '%object_body%' => sprintf("Year: %d\nType: %s\nForm: %s",
  259.                         $object->getYear(),
  260.                         $object->getType()
  261.                             ? TaxReturnType::getReadableValue($object->getType())
  262.                             : '—',
  263.                         $object->getForm()
  264.                             ? TaxReturnFormType::getReadableValue($object->getForm())
  265.                             : '—'
  266.                     ),
  267.                 ]);
  268.                 $this->addInterviewNote(
  269.                     $object->getCustomer(),
  270.                     $commentBody,
  271.                     null,
  272.                     $this->getInterviewNoteMethod('Returns')
  273.                 );
  274.                 break;
  275.             case Customer\Complaint::class:
  276.                 $commentBody $this->translator->trans('notification.object_created', [
  277.                     '%actor%' => $this->getUser() ?: '—',
  278.                     '%article%' => $this->getIndefiniteArticle($this->formatObjectName($object)),
  279.                     '%object_id%' => $object->getId(),
  280.                     '%object_url%' => $this->urlGenerator->generate(
  281.                         'admin_customers_view',
  282.                         [
  283.                             'id' => $object->getCustomer()->getId(),
  284.                             'tab' => 'complaints',
  285.                             'filters' => json_encode(['id' => $object->getId()]),
  286.                         ]
  287.                     ),
  288.                     '%object_name%' => $this->formatObjectName($object),
  289.                     '%object_extra%' => null,
  290.                     '%object_properties%' => null,
  291.                     '%object_body%' => $object->getMessage(),
  292.                 ]);
  293.                 $this->addInterviewNote(
  294.                     $object->getCustomer(),
  295.                     $commentBody,
  296.                     null,
  297.                     $this->getInterviewNoteMethod('Complaint')
  298.                 );
  299.                 break;
  300.             case Customer\GoalTime::class:
  301.                 $serviceCodes = [];
  302.                 foreach ($object->getServiceCodes() as $serviceCode) {
  303.                     $serviceCodes[] = (string) $serviceCode;
  304.                 }
  305.                 $properties = \count($serviceCodes)
  306.                     ? $this->translator->trans('notification.customer_goal_properties', [
  307.                         '%object_service_codes%' => implode(', '$serviceCodes),
  308.                     ])
  309.                     : null;
  310.                 $commentBody $this->translator->trans('notification.object_created', [
  311.                     '%actor%' => $this->getUser() ?: '—',
  312.                     '%article%' => $this->getIndefiniteArticle($this->formatObjectName($object)),
  313.                     '%object_id%' => $object->getId(),
  314.                     '%object_url%' => $this->urlGenerator->generate(
  315.                         'admin_customers_view',
  316.                         [
  317.                             'id' => $object->getCustomer()->getId(),
  318.                             'tab' => 'goals',
  319.                             'filters' => json_encode(['id' => $object->getId()]),
  320.                         ]
  321.                     ),
  322.                     '%object_name%' => $this->formatObjectName($object),
  323.                     '%object_extra%' => null,
  324.                     '%object_properties%' => $properties,
  325.                     '%object_body%' => $object->getMessage(),
  326.                 ]);
  327.                 $this->addInterviewNote(
  328.                     $object->getCustomer(),
  329.                     $commentBody,
  330.                     null,
  331.                     $this->getInterviewNoteMethod('Goal')
  332.                 );
  333.                 break;
  334.             case Customer\FaxLog::class:
  335.                 $commentBody $this->translator->trans('notification.customer_file_faxed', [
  336.                     '%actor%' => $this->getUser() ?: '—',
  337.                     '%value%' => $object->getFile()->getFile(),
  338.                 ]);
  339.                 $this->addInterviewNote(
  340.                     $object->getCustomer(),
  341.                     $commentBody,
  342.                     InterviewNoteType::FAXED_CLIENT,
  343.                     $this->getInterviewNoteMethod('Fax')
  344.                 );
  345.                 break;
  346.             case Customer\RatingLog::class:
  347.                 $commentBody $this->translator->trans('notification.customer_rating_log', [
  348.                     '%value%' => $object->getValue() ?: 0,
  349.                     '%stars%' => preg_replace(
  350.                         '/[\n|\r]/',
  351.                         '',
  352.                         $this->twig->load('@SymfonyAdmin/form/rating.html.twig')->render([
  353.                             'rating' => $object->getValue() ?: 0,
  354.                             'class' => 'star-success star-1x',
  355.                         ])
  356.                     ),
  357.                     '%message%' => $object->getMessage(),
  358.                 ]);
  359.                 $this->addInterviewNote(
  360.                     $object->getCustomer(),
  361.                     $commentBody,
  362.                     null,
  363.                     $this->getInterviewNoteMethod('Rating')
  364.                 );
  365.                 $users = new ArrayCollection();
  366.                 if ($user $object->getCaseManagerUser()) {
  367.                     $users->add($user);
  368.                 }
  369.                 if ($user $object->getLeadAgentUser()) {
  370.                     $users->add($user);
  371.                 }
  372.                 if ($user $object->getFinancialUser()) {
  373.                     $users->add($user);
  374.                 }
  375.                 if ($user $object->getProcessorUser()) {
  376.                     $users->add($user);
  377.                 }
  378.                 if ($user $object->getResolutionSpecialistUser()) {
  379.                     $users->add($user);
  380.                 }
  381.                 $adminUsers $this->managerRegistry
  382.                     ->getRepository(User::class)
  383.                     ->searchAll(['allowSystemAdmin' => true]);
  384.                 foreach ($adminUsers as $user) {
  385.                     if (!$users->contains($user)) {
  386.                         $users->add($user);
  387.                     }
  388.                 }
  389.                 $this->addNotification($objectNotificationCommandType::CREATED_OBJECT$users);
  390.                 break;
  391.             case Customer\Request::class:
  392.                 // map embed fields with customer data
  393.                 $data $this->mergeFields->mapValues($object->getCustomer(), nulltrue);
  394.                 // embed data/values
  395.                 $body $object->getBody();
  396.                 foreach ($data as $key => $value) {
  397.                     $body preg_replace(sprintf('/<span class="text-editor-merge-field-container"><span class="text-editor-merge-field is-recipient"[^>]*data-name="%s"[^>]*># %s<\/span><\/span>/isU'$key$key), str_replace('$''\$'$value), $body);
  398.                 }
  399.                 $commentBody $this->translator->trans('notification.object_created', [
  400.                     '%actor%' => $this->getUser() ?: '—',
  401.                     '%article%' => $this->getIndefiniteArticle($this->formatObjectName($object)),
  402.                     '%object_id%' => $object->getId(),
  403.                     '%object_url%' => $this->urlGenerator->generate(
  404.                         'admin_customers_view',
  405.                         [
  406.                             'id' => $object->getCustomer()->getId(),
  407.                             'tab' => 'requests',
  408.                             'filters' => json_encode(['id' => $object->getId()]),
  409.                         ]
  410.                     ),
  411.                     '%object_name%' => $this->formatObjectName($object),
  412.                     '%object_extra%' => null,
  413.                     '%object_properties%' => null,
  414.                     '%object_body%' => $body,
  415.                 ]);
  416.                 $this->addInterviewNote(
  417.                     $object->getCustomer(),
  418.                     $commentBody,
  419.                     InterviewNoteType::EMAILED_CLIENT,
  420.                     $this->getInterviewNoteMethod('Info. Request')
  421.                 );
  422.                 break;
  423.             case Customer\StatusLog::class:
  424.                 $properties $this->translator->trans('notification.customer_status_properties', [
  425.                     '%object_status%' => $object->getStatus(),
  426.                 ]);
  427.                 $commentBody $this->translator->trans('notification.object_created', [
  428.                     '%actor%' => $this->getUser() ?: '—',
  429.                     '%article%' => $this->getIndefiniteArticle($this->formatObjectName($object)),
  430.                     '%object_id%' => $object->getId(),
  431.                     '%object_url%' => $this->urlGenerator->generate(
  432.                         'admin_customers_view',
  433.                         [
  434.                             'id' => $object->getCustomer()->getId(),
  435.                             'tab' => 'statuses',
  436.                             'filters' => json_encode(['id' => $object->getId()]),
  437.                         ]
  438.                     ),
  439.                     '%object_name%' => $this->formatObjectName($object),
  440.                     '%object_extra%' => null,
  441.                     '%object_properties%' => $properties,
  442.                     '%object_body%' => $object->getMessage(),
  443.                 ]);
  444.                 // only apply to opportunities
  445.                 if (!$object->getCustomer()->isCustomer()
  446.                     && $interviewTime $object->getCustomer()->getInterviewTime()
  447.                 ) {
  448.                     // get default status
  449.                     $defaultStatus $this->managerRegistry
  450.                         ->getRepository(Customer\Status::class)
  451.                         ->findOneBy(['type' => StatusType::DEFAULT]);
  452.                     if ($defaultStatus
  453.                         && $object->getStatus()
  454.                         && $object->getStatus()->getId() === $defaultStatus->getId()
  455.                     ) {
  456.                         // convert seconds
  457.                         $hours intdiv($interviewTime3600);
  458.                         $minutes intdiv($interviewTime 360060);
  459.                         // reset interview time
  460.                         $object->getCustomer()->setInterviewTime(null);
  461.                     }
  462.                 }
  463.                 $this->addInterviewNote(
  464.                     $object->getCustomer(),
  465.                     $commentBody,
  466.                     null,
  467.                     $this->getInterviewNoteMethod('Status'),
  468.                     false,
  469.                     false,
  470.                     $hours ?? null,
  471.                     $minutes ?? null
  472.                 );
  473.                 break;
  474.             case Customer\UpsellRequest::class:
  475.                 $properties $this->translator->trans('notification.customer_upsell_properties', [
  476.                     '%object_category%' => $object->getCategory() ?: '—',
  477.                     '%object_level%' => $object->getLevel() ?: '—',
  478.                 ]);
  479.                 $commentBody $this->translator->trans('notification.object_created', [
  480.                     '%actor%' => $this->getUser() ?: '—',
  481.                     '%article%' => $this->getIndefiniteArticle($this->formatObjectName($object)),
  482.                     '%object_id%' => $object->getId(),
  483.                     '%object_url%' => $this->urlGenerator->generate(
  484.                         'admin_customers_view',
  485.                         [
  486.                             'id' => $object->getCustomer()->getId(),
  487.                             'tab' => 'upsells',
  488.                             'filters' => json_encode(['id' => $object->getId()]),
  489.                         ]
  490.                     ),
  491.                     '%object_name%' => $this->formatObjectName($object),
  492.                     '%object_extra%' => null,
  493.                     '%object_properties%' => $properties,
  494.                     '%object_body%' => $object->getMessage(),
  495.                 ]);
  496.                 $this->addInterviewNote(
  497.                     $object->getCustomer(),
  498.                     $commentBody,
  499.                     null,
  500.                     $this->getInterviewNoteMethod('Upsell Req.')
  501.                 );
  502.                 /** @var User $user */
  503.                 $user $object->getTargetUser();
  504.                 if ($user
  505.                     && $user->isEnabled()
  506.                     && $user->isNotifyOnAssignedCustomer()
  507.                     && $user->hasEntityAccess('customer')
  508.                 ) {
  509.                     $this->addNotification($objectNotificationCommandType::CREATED_OBJECT$user);
  510.                 }
  511.                 break;
  512.             case Customer\File::class:
  513.                 if (preg_match('/\/signatures\//'$object->getFile())) {
  514.                     return;
  515.                 }
  516.                 /** @var User $assignedUser */
  517.                 $assignedUser $this->getAssignedUser($object->getCustomer());
  518.                 if ($assignedUser
  519.                     && $assignedUser->isEnabled()
  520.                     && $assignedUser->isNotifyOnAssignedCustomer()
  521.                     && $assignedUser->hasEntityAccess('customer')
  522.                 ) {
  523.                     $this->addNotification($objectNotificationCommandType::CREATED_OBJECT$assignedUser);
  524.                 }
  525.                 break;
  526.             case Customer\Customer::class:
  527.                 $this->addInterviewNote($object$commentBody);
  528.                 /** @var User $user */
  529.                 $user $object->getUser();
  530.                 if ($user
  531.                     && $user->isEnabled()
  532.                     && $user->isNotifyOnAssignedCustomer()
  533.                     && $user->hasEntityAccess('customer')
  534.                 ) {
  535.                     $this->addNotification($objectNotificationCommandType::CREATED_OBJECT$user);
  536.                 }
  537.                 break;
  538.             case Lead\Lead::class:
  539.                 $comment = new Lead\Comment();
  540.                 $comment->setLead($object);
  541.                 $comment->setUser($this->getUser());
  542.                 $comment->setBody($commentBody);
  543.                 $object->addComment($comment);
  544.                 /** @var User $user */
  545.                 $user $object->getUser();
  546.                 if ($user
  547.                     && $user->isEnabled()
  548.                     && $user->isNotifyOnAssignedLead()
  549.                     && $user->hasEntityAccess('lead')
  550.                 ) {
  551.                     $this->addNotification($objectNotificationCommandType::CREATED_OBJECT$user);
  552.                 }
  553.                 break;
  554.             case Event\Event::class:
  555.                 if ($object->getCustomer()) {
  556.                     $extra null;
  557.                     if ($object->getTargetUser()
  558.                         && $this->getUser()->getId() !== $object->getTargetUser()->getId()
  559.                     ) {
  560.                         $extra $this->translator->trans('notification.customer_event_extra', [
  561.                             '%target%' => $object->getTargetUser(),
  562.                         ]);
  563.                     }
  564.                     $properties $this->translator->trans('notification.customer_event_properties', [
  565.                         '%object_priority%' => $object->getPriority()
  566.                             ? EventPriorityType::getReadableValue($object->getPriority())
  567.                             : '—',
  568.                         '%object_tag%' => $object->getTag() ?: '—',
  569.                     ]);
  570.                     $commentBody $this->translator->trans('notification.object_created', [
  571.                         '%actor%' => $this->getUser() ?: '—',
  572.                         '%article%' => $this->getIndefiniteArticle($this->formatObjectName($object)),
  573.                         '%object_id%' => $object->getId(),
  574.                         '%object_url%' => $this->urlGenerator->generate(
  575.                             'admin_customers_view',
  576.                             [
  577.                                 'id' => $object->getCustomer()->getId(),
  578.                                 'tab' => 'events',
  579.                                 'filters' => json_encode(['id' => $object->getId()]),
  580.                             ]
  581.                         ),
  582.                         '%object_name%' => $this->formatObjectName($object),
  583.                         '%object_extra%' => $extra,
  584.                         '%object_properties%' => $properties,
  585.                         '%object_body%' => $object->getDescription(),
  586.                     ]);
  587.                     $this->addInterviewNote(
  588.                         $object->getCustomer(),
  589.                         $commentBody,
  590.                         null,
  591.                         EventType::REMINDER === $object->getType()
  592.                             ? $this->getInterviewNoteMethod('Reminder')
  593.                             : $this->getInterviewNoteMethod('Task')
  594.                     );
  595.                 }
  596.                 /** @var User $user */
  597.                 $user $object->getTargetUser();
  598.                 if ($user
  599.                     && $user->isEnabled()
  600.                     && $user->isNotifyOnAssignedEvent()
  601.                     && $user->hasEntityAccess('event')
  602.                 ) {
  603.                     $this->addNotification($objectNotificationCommandType::CREATED_OBJECT$user);
  604.                 }
  605.                 break;
  606.             case Event\Comment::class:
  607.                 if ($object->getEvent()->getCustomer()) {
  608.                     $commentBody $this->translator->trans('notification.object_commented', [
  609.                         '%actor%' => $this->getUser() ?: '—',
  610.                         '%article%' => $this->getIndefiniteArticle($this->formatObjectName($object->getEvent())),
  611.                         '%object_id%' => $object->getEvent()->getId(),
  612.                         '%object_url%' => $this->urlGenerator->generate(
  613.                             'admin_customers_view',
  614.                             [
  615.                                 'id' => $object->getEvent()->getCustomer()->getId(),
  616.                                 'tab' => 'events',
  617.                                 'filters' => json_encode(['id' => $object->getEvent()->getId()]),
  618.                             ]
  619.                         ),
  620.                         '%object_name%' => $this->formatObjectName($object->getEvent()),
  621.                         '%object_body%' => $object->getBody(),
  622.                     ]);
  623.                     $this->addInterviewNote(
  624.                         $object->getEvent()->getCustomer(),
  625.                         $commentBody,
  626.                         null,
  627.                         $this->getInterviewNoteMethod('Task')
  628.                     );
  629.                 }
  630.                 $event $object->getEvent();
  631.                 $users = new ArrayCollection([
  632.                     $event->getUser(),
  633.                 ]);
  634.                 if ($event->getTargetUser()) {
  635.                     $users->add($event->getTargetUser());
  636.                 }
  637.                 foreach ($event->getComments() as $comment) {
  638.                     $user $comment->getUser();
  639.                     if ($user && !$users->contains($user)) {
  640.                         $users->add($user);
  641.                     }
  642.                 }
  643.                 foreach ($users as $user) {
  644.                     if (!$user->isEnabled()
  645.                         || !$user->isNotifyOnAssignedEvent()
  646.                         || !$user->hasEntityAccess('event')
  647.                     ) {
  648.                         $users->removeElement($user);
  649.                     }
  650.                 }
  651.                 $this->addNotification($eventNotificationCommandType::CREATED_COMMENT$users, [
  652.                     'comment_id' => $object->getId(),
  653.                 ]);
  654.                 break;
  655.             case Feed\Comment::class:
  656.                 $feed $object->getFeed();
  657.                 $users = new ArrayCollection();
  658.                 $isMentioned false;
  659.                 if (false !== preg_match_all('/<span class="text-editor-merge-field-container">\s*<span class="text-editor-merge-field is-mention"[^>]*data-name="([^"]+)"[^>]*>@\s*([^<]+)<\/span><\/span>/isU'$object->getBody(), $matches)) {
  660.                     $matchedUsers = !empty($matches[1])
  661.                         ? $this->managerRegistry
  662.                             ->getRepository(User::class)
  663.                             ->searchAll(['username' => $matches[1]])
  664.                         : [];
  665.                     foreach ($matchedUsers as $user) {
  666.                         if (!$users->contains($user)) {
  667.                             $users->add($user);
  668.                         }
  669.                     }
  670.                     if ($users->count()) {
  671.                         $isMentioned true;
  672.                     }
  673.                 }
  674.                 if (!$isMentioned) {
  675.                     $users $feed->getUsers();
  676.                 }
  677.                 foreach ($users as $user) {
  678.                     if (!$user->isEnabled()
  679.                         || !$user->isNotifyOnAssignedFeed()
  680.                         || !$user->hasEntityAccess('feed')
  681.                     ) {
  682.                         $users->removeElement($user);
  683.                     }
  684.                 }
  685.                 if (=== $feed->getComments()->count()) {
  686.                     $this->addNotification($feedNotificationCommandType::CREATED_OBJECT$users);
  687.                 } else {
  688.                     $this->addNotification($feedNotificationCommandType::CREATED_COMMENT$users, [
  689.                         'comment_id' => $object->getId(),
  690.                         'mentioned' => $isMentioned,
  691.                     ]);
  692.                 }
  693.                 return;
  694.             case Project\Project::class:
  695.                 $users $object->getUsers();
  696.                 foreach ($users as $user) {
  697.                     if (!$user->isEnabled()
  698.                         || !$user->isNotifyOnAssignedProject()
  699.                         || !$user->hasEntityAccess('project')
  700.                     ) {
  701.                         $users->removeElement($user);
  702.                     }
  703.                 }
  704.                 $this->addNotification($objectNotificationCommandType::CREATED_OBJECT$users);
  705.                 return;
  706.             case Project\Comment::class:
  707.                 $project $object->getProject();
  708.                 $item $object->getItem();
  709.                 $users = new ArrayCollection();
  710.                 $isMentioned false;
  711.                 if (false !== preg_match_all('/<span class="text-editor-merge-field-container">\s*<span class="text-editor-merge-field is-mention"[^>]*data-name="([^"]+)"[^>]*>@\s*([^<]+)<\/span><\/span>/isU'$object->getBody(), $matches)) {
  712.                     $matchedUsers = !empty($matches[1])
  713.                         ? $this->managerRegistry
  714.                             ->getRepository(User::class)
  715.                             ->searchAll(['username' => $matches[1]])
  716.                         : [];
  717.                     foreach ($matchedUsers as $user) {
  718.                         if (!$users->contains($user)) {
  719.                             $users->add($user);
  720.                         }
  721.                     }
  722.                     if ($users->count()) {
  723.                         $isMentioned true;
  724.                     }
  725.                 }
  726.                 if (!$isMentioned) {
  727.                     $users $item $item->getUsers() : $project->getUsers();
  728.                 }
  729.                 foreach ($users as $user) {
  730.                     if (!$user->isEnabled()
  731.                         || !$user->isNotifyOnAssignedProject()
  732.                         || !$user->hasEntityAccess('project')
  733.                     ) {
  734.                         $users->removeElement($user);
  735.                     }
  736.                 }
  737.                 if ($item) {
  738.                     $this->addNotification($projectNotificationCommandType::CREATED_COMMENT$users, [
  739.                         'item_id' => $item->getId(),
  740.                         'comment_id' => $object->getId(),
  741.                         'mentioned' => $isMentioned,
  742.                     ]);
  743.                 } else {
  744.                     $this->addNotification($projectNotificationCommandType::CREATED_COMMENT$users, [
  745.                         'parent_id' => $object->getParent() ? $object->getParent()->getId() : null,
  746.                         'comment_id' => $object->getId(),
  747.                         'mentioned' => $isMentioned,
  748.                     ]);
  749.                 }
  750.                 return;
  751.             case Stream\Comment::class:
  752.                 $stream $object->getStream();
  753.                 $users = new ArrayCollection();
  754.                 $isMentioned false;
  755.                 if (false !== preg_match_all('/<span class="text-editor-merge-field-container">\s*<span class="text-editor-merge-field is-mention"[^>]*data-name="([^"]+)"[^>]*>@\s*([^<]+)<\/span><\/span>/isU'$object->getBody(), $matches)) {
  756.                     $matchedUsers = !empty($matches[1])
  757.                         ? $this->managerRegistry
  758.                             ->getRepository(User::class)
  759.                             ->searchAll(['username' => $matches[1]])
  760.                         : [];
  761.                     foreach ($matchedUsers as $user) {
  762.                         if (!$users->contains($user)) {
  763.                             $users->add($user);
  764.                         }
  765.                     }
  766.                     if ($users->count()) {
  767.                         $isMentioned true;
  768.                     }
  769.                 }
  770.                 if (!$isMentioned) {
  771.                     $users $stream->getParticipantUsers();
  772.                 }
  773.                 foreach ($users as $user) {
  774.                     if (!$user->isEnabled()
  775.                         || !$user->isNotifyOnAssignedStream()
  776.                         || !$user->hasEntityAccess('stream')
  777.                     ) {
  778.                         $users->removeElement($user);
  779.                     }
  780.                 }
  781.                 if (=== $stream->getComments()->count() && === $object->getChildren()->count()) {
  782.                     $this->addNotification($streamNotificationCommandType::CREATED_OBJECT$users);
  783.                 } else {
  784.                     $this->addNotification($streamNotificationCommandType::CREATED_COMMENT$users, [
  785.                         'parent_id' => $object->getParent() ? $object->getParent()->getId() : null,
  786.                         'comment_id' => $object->getId(),
  787.                         'mentioned' => $isMentioned,
  788.                     ]);
  789.                 }
  790.                 return;
  791.             case Issue\Comment::class:
  792.                 $issue $object->getIssue();
  793.                 $users = new ArrayCollection();
  794.                 foreach ($issue->getParticipants() as $participant) {
  795.                     $user $participant->getUser();
  796.                     if ($user && !$users->contains($user)) {
  797.                         $users->add($user);
  798.                     }
  799.                 }
  800.                 foreach ($users as $user) {
  801.                     if (!$user->isEnabled()
  802.                         || !$user->isNotifyOnAssignedIssue()
  803.                         || !$user->hasEntityAccess('issue')
  804.                     ) {
  805.                         $users->removeElement($user);
  806.                     }
  807.                 }
  808.                 if (=== $issue->getComments()->count()) {
  809.                     $this->addNotification($issueNotificationCommandType::CREATED_OBJECT$users);
  810.                 } else {
  811.                     $this->addNotification($issueNotificationCommandType::CREATED_COMMENT$users, [
  812.                         'comment_id' => $object->getId(),
  813.                     ]);
  814.                 }
  815.                 return;
  816.             default:
  817.                 return;
  818.         }
  819.         $repository $this->managerRegistry->getRepository(\get_class($object));
  820.         $repository->update($object);
  821.     }
  822.     /**
  823.      * @param ObjectEvent $event
  824.      */
  825.     public function onUserAssigned(ObjectEvent $event): void
  826.     {
  827.         $object $event->getObject();
  828.         $method 'getUser';
  829.         switch (\get_class($object)) {
  830.             case Customer\Customer::class:
  831.                 if ($event->hasChangedField('caseManagerUser')) {
  832.                     $method 'getCaseManagerUser';
  833.                 } elseif ($event->hasChangedField('leadAgentUser')) {
  834.                     $method 'getLeadAgentUser';
  835.                 } elseif ($event->hasChangedField('financialUser')) {
  836.                     $method 'getFinancialUser';
  837.                 } elseif ($event->hasChangedField('serviceUser')) {
  838.                     $method 'getServiceUser';
  839.                 } elseif ($event->hasChangedField('processorUser')) {
  840.                     $method 'getProcessorUser';
  841.                 } elseif ($event->hasChangedField('resolutionSpecialistUser')) {
  842.                     $method 'getResolutionSpecialistUser';
  843.                 }
  844.                 break;
  845.             case Customer\UpsellRequest::class:
  846.             case Event\Event::class:
  847.                 $method 'getTargetUser';
  848.                 break;
  849.         }
  850.         /** @var User $user */
  851.         if (!method_exists($object$method) || !$user $object->$method()) {
  852.             return;
  853.         }
  854.         // set comment body
  855.         $commentBody $this->translator->trans('notification.user_assigned', [
  856.             '%actor%' => $this->getUser() ?: '—',
  857.             '%target%' => $user,
  858.         ]);
  859.         switch (\get_class($object)) {
  860.             case Customer\Customer::class:
  861.                 break;
  862.             case Event\Event::class:
  863.             case Lead\Lead::class:
  864.                 $class sprintf('App\Entity\%s\Comment'$this->getObjectName($object));
  865.                 $method sprintf('set%s'$this->getObjectName($object));
  866.                 $comment = new $class();
  867.                 $comment->$method($object);
  868.                 $comment->setUser($this->getUser());
  869.                 $comment->setBody($commentBody);
  870.                 $object->addComment($comment);
  871.                 break;
  872.             case Customer\UpsellRequest::class:
  873.                 $comment = new Customer\Comment\UpsellRequestComment($object);
  874.                 $comment->setUser($this->getUser());
  875.                 $comment->setBody($commentBody);
  876.                 $object->addComment($comment);
  877.                 break;
  878.             default:
  879.                 return;
  880.         }
  881.         $repository $this->managerRegistry->getRepository(\get_class($object));
  882.         $repository->update($object);
  883.         $method sprintf('isNotifyOnAssigned%s'$this->getObjectName($object));
  884.         if ($user && (Customer\UpsellRequest::class === \get_class($object) || $user->$method())) {
  885.             $command sprintf('App\DBAL\Types\NotificationCommandType::ASSIGNED_%s'strtoupper(StringUtil::tableize($this->getObjectName($object))));
  886.             $this->addNotification($object, \constant($command), $user);
  887.         }
  888.     }
  889.     /**
  890.      * @param ObjectEvent $event
  891.      */
  892.     public function onEventPoked(ObjectEvent $event): void
  893.     {
  894.         /** @var Event\Event $event */
  895.         $event $event->getObject();
  896.         /** @var User $user */
  897.         $user $event->getTargetUser();
  898.         if ($user
  899.             && $user->isEnabled()
  900.             && $user->isNotifyOnAssignedEvent()
  901.             && $user->hasEntityAccess('event')
  902.         ) {
  903.             $this->addNotification($eventNotificationCommandType::EVENT_POKED$user);
  904.         }
  905.     }
  906.     /**
  907.      * @param ObjectEvent $event
  908.      */
  909.     public function onEventDue(ObjectEvent $event): void
  910.     {
  911.         /** @var Event\Event $event */
  912.         $event $event->getObject();
  913.         /** @var User $user */
  914.         $user $event->getTargetUser();
  915.         if ($user
  916.             && $user->isEnabled()
  917.             && $user->isNotifyWhenEventIsDue()
  918.             && $user->hasEntityAccess('event')
  919.         ) {
  920.             $this->addNotification($eventNotificationCommandType::EVENT_IS_DUE$user);
  921.         }
  922.     }
  923.     /**
  924.      * @param ObjectEvent $event
  925.      */
  926.     public function onEventUpcoming(ObjectEvent $event): void
  927.     {
  928.         /** @var Event\Event $event */
  929.         $event $event->getObject();
  930.         /** @var User $user */
  931.         $user $event->getTargetUser();
  932.         if ($user
  933.             && $user->isEnabled()
  934.             && $user->isNotifyUpcomingEvent()
  935.             && $user->hasEntityAccess('event')
  936.         ) {
  937.             $this->addNotification($eventNotificationCommandType::EVENT_REMINDER$user);
  938.         }
  939.     }
  940.     /**
  941.      * @param ObjectEvent $event
  942.      */
  943.     public function onUpsellRequestPoked(ObjectEvent $event): void
  944.     {
  945.         /** @var Customer\UpsellRequest $upsellRequest */
  946.         $upsellRequest $event->getObject();
  947.         /** @var User $user */
  948.         $user $upsellRequest->getTargetUser();
  949.         if ($user
  950.             && $user->isEnabled()
  951.             && $user->isNotifyOnAssignedCustomer()
  952.             && $user->hasEntityAccess('customer')
  953.         ) {
  954.             $this->addNotification($upsellRequestNotificationCommandType::UPSELL_REQUEST_POKED$user);
  955.         }
  956.     }
  957.     /**
  958.      * @param ObjectEvent $event
  959.      */
  960.     public function onProjectPoked(ObjectEvent $event): void
  961.     {
  962.         /** @var Project\Item $item */
  963.         $item $event->getObject();
  964.         $users $item->getUsers();
  965.         foreach ($users as $user) {
  966.             if (!$user->isEnabled()
  967.                 || !$user->isNotifyOnAssignedProject()
  968.                 || !$user->hasEntityAccess('project')
  969.             ) {
  970.                 $users->removeElement($user);
  971.             }
  972.         }
  973.         $this->addNotification($item->getProject(), NotificationCommandType::PROJECT_POKED$users, [
  974.             'item_id' => $item->getId(),
  975.         ]);
  976.     }
  977.     /**
  978.      * @param ObjectEvent $event
  979.      */
  980.     public function onMessageSent(ObjectEvent $event): void
  981.     {
  982.         try {
  983.             /** @var SessionInterface $session */
  984.             $session $this->requestStack->getSession();
  985.             if ($session->get('ignore_create_note')) {
  986.                 $session->remove('ignore_create_note');
  987.                 return;
  988.             }
  989.         } catch (SessionNotFoundException $e) {
  990.             // do nothing
  991.         }
  992.         /** @var Customer\Message $message */
  993.         $message $event->getObject();
  994.         // map embed fields with customer data
  995.         $data $this->mergeFields->mapValues($message->getCustomer(), nulltrue);
  996.         // embed data/values
  997.         $body $message->getBody();
  998.         foreach ($data as $key => $value) {
  999.             $body preg_replace(sprintf('/<span class="text-editor-merge-field-container"><span class="text-editor-merge-field is-recipient"[^>]*data-name="%s"[^>]*># %s<\/span><\/span>/isU'$key$key), str_replace('$''\$'$value), $body);
  1000.         }
  1001.         $commentBody $this->translator->trans('notification.object_created', [
  1002.             '%actor%' => $this->getUser() ?: $this->getCustomerUser() ?: '—',
  1003.             '%article%' => $this->getIndefiniteArticle($this->formatObjectName($message)),
  1004.             '%object_id%' => $message->getId(),
  1005.             '%object_url%' => $this->urlGenerator->generate(
  1006.                 'admin_customers_view',
  1007.                 [
  1008.                     'id' => $message->getCustomer()->getId(),
  1009.                     'tab' => 'messages',
  1010.                     'filters' => json_encode([
  1011.                         'id' => $message->getId(),
  1012.                         'mailbox' => $message->getMailbox(),
  1013.                     ]),
  1014.                 ]
  1015.             ),
  1016.             '%object_name%' => $this->formatObjectName($message),
  1017.             '%object_extra%' => null,
  1018.             '%object_properties%' => null,
  1019.             '%object_body%' => $body,
  1020.         ]);
  1021.         $hasEmails false;
  1022.         $hasPhones false;
  1023.         foreach ($message->getContacts() as $contact) {
  1024.             if ($message->isEmailContact($contact)) {
  1025.                 $hasEmails true;
  1026.             }
  1027.             if ($message->isPhoneContact($contact)) {
  1028.                 $hasPhones true;
  1029.             }
  1030.         }
  1031.         if ($hasEmails) {
  1032.             $this->addInterviewNote(
  1033.                 $message->getCustomer(),
  1034.                 $commentBody,
  1035.                 InterviewNoteType::EMAILED_CLIENT,
  1036.                 $this->getInterviewNoteMethod('Email'),
  1037.                 $this->getUser() && $this->getUser()->isAllowSalesItems() && !$this->getUser()->isAllowServiceItems(),
  1038.                 MailboxType::SENT === $message->getMailbox()
  1039.             );
  1040.         }
  1041.         if ($hasPhones) {
  1042.             $this->addInterviewNote(
  1043.                 $message->getCustomer(),
  1044.                 $commentBody,
  1045.                 InterviewNoteType::TEXTED_CLIENT,
  1046.                 $this->getInterviewNoteMethod('Text'),
  1047.                 $this->getUser() && $this->getUser()->isAllowSalesItems() && !$this->getUser()->isAllowServiceItems(),
  1048.                 MailboxType::SENT === $message->getMailbox()
  1049.             );
  1050.         }
  1051.         if ($hasEmails || $hasPhones) {
  1052.             $repository $this->managerRegistry->getRepository(\get_class($message));
  1053.             $repository->update($message);
  1054.         }
  1055.         if (MailboxType::INBOX !== $message->getMailbox()) {
  1056.             return;
  1057.         }
  1058.         /** @var User $assignedUser */
  1059.         $assignedUser $this->getAssignedUser($message->getCustomer());
  1060.         if ($assignedUser
  1061.             && $assignedUser->isEnabled()
  1062.             && $assignedUser->isNotifyOnAssignedCustomer()
  1063.             && $assignedUser->hasEntityAccess('customer')
  1064.         ) {
  1065.             $this->addNotification($messageNotificationCommandType::MESSAGE_SENT$assignedUser);
  1066.         }
  1067.     }
  1068.     /**
  1069.      * @param ObjectEvent $event
  1070.      */
  1071.     public function onCustomerQASigned(ObjectEvent $event): void
  1072.     {
  1073.         /** @var Customer $customer */
  1074.         $customer $event->getObject();
  1075.         /** @var User|null $caseManagerUser */
  1076.         $caseManagerUser $customer->getCaseManagerUser();
  1077.         if ($caseManagerUser
  1078.             && (
  1079.                 ($event->hasChangedField('pqaReceivedAt') && $customer->getPqaReceivedAt())
  1080.                 || ($event->hasChangedField('bqaReceivedAt') && $customer->getBqaReceivedAt())
  1081.             )
  1082.         ) {
  1083.             $this->addNotification($customerNotificationCommandType::QA_SIGNED$caseManagerUser);
  1084.         }
  1085.     }
  1086.     /**
  1087.      * @param ObjectEvent $event
  1088.      */
  1089.     public function onCustomerStageItemCompleted(ObjectEvent $event): void
  1090.     {
  1091.         /** @var Customer\Stage $stage */
  1092.         $stage $event->getObject();
  1093.         $commentBody $this->translator->trans('notification.customer_stage_item_completed', [
  1094.             '%actor%' => $this->getUser() ?: '—',
  1095.             '%value%' => $stage->getStageItem(),
  1096.         ]);
  1097.         $this->addInterviewNote(
  1098.             $stage->getCustomer(),
  1099.             $commentBody,
  1100.             null,
  1101.             $this->getInterviewNoteMethod('Stage')
  1102.         );
  1103.     }
  1104.     /**
  1105.      * @param ObjectEvent $event
  1106.      */
  1107.     public function onCustomerStageCompleted(ObjectEvent $event): void
  1108.     {
  1109.         /** @var Customer\Customer $customer */
  1110.         $customer $event->getObject();
  1111.         $commentBody $this->translator->trans('notification.customer_stage_completed', [
  1112.             '%actor%' => $this->getUser() ?: '—',
  1113.             '%value%' => $customer->getStage(),
  1114.         ]);
  1115.         $this->addInterviewNote(
  1116.             $customer,
  1117.             $commentBody,
  1118.             null,
  1119.             $this->getInterviewNoteMethod('Stage')
  1120.         );
  1121.     }
  1122.     /**
  1123.      * @param ObjectEvent $event
  1124.      */
  1125.     public function onCustomerTransactionUpdated(ObjectEvent $event): void
  1126.     {
  1127.         /** @var Customer\Transaction $transaction */
  1128.         $transaction $event->getObject();
  1129.         if (TransactionType::PAID !== $transaction->getType()) {
  1130.             return;
  1131.         }
  1132.         $extra $this->translator->trans('notification.customer_payment_extra', [
  1133.             '%amount%' => number_format($transaction->getAmount()),
  1134.         ]);
  1135.         switch ($transaction->getMethod()) {
  1136.             case TransactionMethodType::CREDIT_CARD:
  1137.                 $paymentDetails json_encode([
  1138.                     'cardBrand' => $transaction->getCardBrand()
  1139.                         ? CardBrandType::getReadableValue($transaction->getCardBrand())
  1140.                         : '-',
  1141.                     'cardOwner' => $transaction->getCardOwner() ?: '-',
  1142.                     'cardNumber' => $transaction->getCardNumber() ?: '-',
  1143.                     'cardExpMonth' => $transaction->getCardExpMonth() ?: '-',
  1144.                     'cardExpYear' => $transaction->getCardExpYear() ?: '-',
  1145.                     'cardCvc' => $transaction->getCardCvc() ?: '-',
  1146.                 ]);
  1147.                 break;
  1148.             case TransactionMethodType::ECHECK:
  1149.                 $paymentDetails json_encode([
  1150.                     'eCheckOwner' => $transaction->getECheckOwner() ?: '-',
  1151.                     'eCheckBankName' => $transaction->getECheckBankName() ?: '-',
  1152.                     'eCheckAccountNumber' => $transaction->getECheckAccountNumber() ?: '-',
  1153.                     'eCheckRouting' => $transaction->getECheckRouting() ?: '-',
  1154.                     'eCheckNumber' => $transaction->getECheckNumber() ?: '-',
  1155.                     'eCheckPhoneNumber' => $transaction->getECheckPhoneNumber() ?: '-',
  1156.                 ]);
  1157.                 break;
  1158.             case TransactionMethodType::CASHIER_CHECK:
  1159.                 $paymentDetails json_encode([
  1160.                     'cashierCheckNumber' => $transaction->getCashierCheckNumber() ?: '-',
  1161.                 ]);
  1162.                 break;
  1163.             case TransactionMethodType::LOAN:
  1164.                 $paymentDetails json_encode([
  1165.                     'loanMerchantNumber' => $transaction->getLoanMerchantNumber() ?: '-',
  1166.                 ]);
  1167.                 break;
  1168.             case TransactionMethodType::PERSONAL_CHECK:
  1169.                 $paymentDetails json_encode([
  1170.                     'payableTo' => $this->personalCheckTo ?: '-',
  1171.                 ]);
  1172.                 break;
  1173.             case TransactionMethodType::CASH_ON_DELIVERY:
  1174.                 break;
  1175.         }
  1176.         $properties $this->translator->trans('notification.customer_payment_properties', [
  1177.             '%object_method%' => $transaction->getMethod()
  1178.                 ? TransactionMethodType::getReadableValue($transaction->getMethod())
  1179.                 : '—',
  1180.             '%object_details%' => $paymentDetails ?? '—',
  1181.         ]);
  1182.         $body $transaction->getMessage();
  1183.         if ($transaction->getRanAt()) {
  1184.             $ranMessage $this->translator->trans('notification.customer_payment_ran_message', [
  1185.                 '%object_ran_by%' => $transaction->getRanBy(),
  1186.                 '%object_ran_message%' => $transaction->getRanMessage(),
  1187.             ]);
  1188.             $body .= sprintf("\n\n%s"$ranMessage);
  1189.         }
  1190.         $commentBody $this->translator->trans('notification.object_created', [
  1191.             '%actor%' => $this->getUser() ?: '—',
  1192.             '%article%' => $this->getIndefiniteArticle($this->formatObjectName($transaction)),
  1193.             '%object_id%' => $transaction->getId(),
  1194.             '%object_url%' => $this->urlGenerator->generate(
  1195.                 'admin_customers_view',
  1196.                 [
  1197.                     'id' => $transaction->getCustomer()->getId(),
  1198.                     'tab' => 'transactions',
  1199.                     'filters' => json_encode(['id' => $transaction->getId()]),
  1200.                 ]
  1201.             ),
  1202.             '%object_name%' => $this->formatObjectName($transaction),
  1203.             '%object_extra%' => $extra,
  1204.             '%object_properties%' => $properties,
  1205.             '%object_body%' => $body,
  1206.         ]);
  1207.         $this->addInterviewNote(
  1208.             $transaction->getCustomer(),
  1209.             $commentBody,
  1210.             null,
  1211.             $this->getInterviewNoteMethod('Payment'),
  1212.             true
  1213.         );
  1214.         // require when updating
  1215.         $repository $this->managerRegistry->getRepository(\get_class($transaction));
  1216.         $repository->update($transaction);
  1217.     }
  1218.     /**
  1219.      * @param GenericEvent $event
  1220.      */
  1221.     public function onChatMessageCreated(GenericEvent $event): void
  1222.     {
  1223.         /** @var Chat\Message $message */
  1224.         $newMessage $event->getSubject();
  1225.         /** @var Chat\Chat $chat */
  1226.         $chat $newMessage->getChat();
  1227.         /** @var bool $ignoreInsert */
  1228.         $ignoreInsert $event->getArguments()['ignoreInsert'] ?? false;
  1229.         /** @var bool $autoOpen */
  1230.         $autoOpen $event->getArguments()['autoOpen'] ?? false;
  1231.         $photoIds = [];
  1232.         $photoUsers = [];
  1233.         foreach (array_reverse($chat->getMessages()->toArray()) as $message) {
  1234.             if ($message->getUser()->getId() !== $this->getUser()->getId()
  1235.                 && !\in_array($message->getUser()->getId(), $photoIds)
  1236.             ) {
  1237.                 $photoIds[] = $message->getUser()->getId();
  1238.                 $photoUsers[] = [
  1239.                     'user_id' => $message->getUser()->getId(),
  1240.                     'user_name' => (string) $message->getUser(),
  1241.                     'user_photo' => $message->getUser()->getPhoto(),
  1242.                 ];
  1243.             }
  1244.             if (=== \count($photoIds)) {
  1245.                 break;
  1246.             }
  1247.         }
  1248.         $participants = [];
  1249.         foreach ($chat->getParticipants() as $participant) {
  1250.             $participants[$participant->getUser()->getId()] = (string) $participant->getUser();
  1251.             if ($participant->getUser()->getId() !== $this->getUser()->getId()
  1252.                 && !\in_array($participant->getUser()->getId(), $photoIds)
  1253.                 && \count($photoIds) < 6
  1254.             ) {
  1255.                 $photoIds[] = $participant->getUser()->getId();
  1256.                 $photoUsers[] = [
  1257.                     'user_id' => $participant->getUser()->getId(),
  1258.                     'user_name' => (string) $participant->getUser(),
  1259.                     'user_photo' => $participant->getUser()->getPhoto(),
  1260.                 ];
  1261.             }
  1262.         }
  1263.         $files = [];
  1264.         foreach ($newMessage->getFiles() as $file) {
  1265.             $files[] = [
  1266.                 'name' => (string) $file,
  1267.                 'file' => $file->getFile(),
  1268.             ];
  1269.         }
  1270.         $data = [
  1271.             'ignore_insert' => $ignoreInsert,
  1272.             'auto_open' => false,
  1273.             'chat_id' => $chat->getId(),
  1274.             'creator_id' => $chat->getUser()->getId(),
  1275.             'message_id' => $newMessage->getId(),
  1276.             'participants' => $participants,
  1277.             'photos' => $photoUsers,
  1278.             'user_id' => $newMessage->getUser()->getId(),
  1279.             'user_name' => (string) $newMessage->getUser(),
  1280.             'user_photo' => $newMessage->getUser()->getPhoto(),
  1281.             'body' => (string) $newMessage,
  1282.             'files' => $files,
  1283.             'created_at' => $newMessage->getCreatedAt()
  1284.                         ? $newMessage->getCreatedAt()->format($this->getUser()->getTimeFormat())
  1285.                         : null,
  1286.         ];
  1287.         foreach ($chat->getParticipants() as $participant) {
  1288.             $config $data;
  1289.             $config['unread'] = $participant->getUnread();
  1290.             if ($participant->getUser()->getId() === $this->getUser()->getId()) {
  1291.                 // automatically open chat for creator on first message
  1292.                 if ($autoOpen && === $chat->getMessages()->count()) {
  1293.                     $config['auto_open'] = true;
  1294.                 }
  1295.             }
  1296.             $this->fayeClient->send(sprintf('/%d/chat/message'$participant->getUser()->getId()), $config);
  1297.         }
  1298.     }
  1299.     /**
  1300.      * @param GenericEvent $event
  1301.      */
  1302.     public function onChatMessageReacted(GenericEvent $event): void
  1303.     {
  1304.         /** @var Chat\Message $message */
  1305.         $message $event->getSubject();
  1306.         /** @var Chat $chat */
  1307.         $chat $message->getChat();
  1308.         $config array_merge(
  1309.             $event->getArguments(),
  1310.             [
  1311.                 'chat_id' => $chat->getId(),
  1312.                 'message_id' => $message->getId(),
  1313.                 'user_id' => $this->getUser()->getId(),
  1314.                 'read_url' => $this->urlGenerator->generate(
  1315.                     'admin_api_chat_read',
  1316.                     [
  1317.                         'id' => $chat->getId(),
  1318.                     ],
  1319.                     UrlGeneratorInterface::ABSOLUTE_URL
  1320.                 ),
  1321.             ]
  1322.         );
  1323.         foreach ($chat->getParticipants() as $participant) {
  1324.             $this->fayeClient->send(sprintf('/%s/chat/reaction'$participant->getUser()->getId()), $config);
  1325.         }
  1326.     }
  1327.     /**
  1328.      * @param GenericEvent $event
  1329.      */
  1330.     public function onChatDeleted(GenericEvent $event): void
  1331.     {
  1332.         /** @var int $chatId */
  1333.         $chatId $event->getSubject();
  1334.         /** @var int|null $userId */
  1335.         $userId $event->getArguments()['userId'] ?? null;
  1336.         $config = [
  1337.             'chat_id' => $chatId,
  1338.             'user_id' => $userId,
  1339.         ];
  1340.         $this->fayeClient->send(sprintf('/all/chat/delete'$userId), $config);
  1341.     }
  1342.     /**
  1343.      * @return User|null
  1344.      */
  1345.     private function getUser(): ?User
  1346.     {
  1347.         if ($token $this->tokenStorage->getToken()) {
  1348.             $user $token->getUser();
  1349.             if ($user instanceof User) {
  1350.                 return $user;
  1351.             }
  1352.         }
  1353.         return null;
  1354.     }
  1355.     /**
  1356.      * @return Customer\Customer|null
  1357.      */
  1358.     private function getCustomerUser(): ?Customer\Customer
  1359.     {
  1360.         if ($token $this->tokenStorage->getToken()) {
  1361.             $user $token->getUser();
  1362.             if ($user instanceof Customer\Customer) {
  1363.                 return $user;
  1364.             }
  1365.         }
  1366.         return null;
  1367.     }
  1368.     /**
  1369.      * @param mixed    $object
  1370.      * @param int      $offset
  1371.      * @param int|null $length
  1372.      *
  1373.      * @return string
  1374.      */
  1375.     private function getObjectName($objectint $offset = -1int $length null): string
  1376.     {
  1377.         return implode('', \array_slice(explode('\\', \get_class($object)), $offset$length));
  1378.     }
  1379.     /**
  1380.      * @param mixed $object
  1381.      * @param bool  $lowerCase
  1382.      *
  1383.      * @return string
  1384.      */
  1385.     private function formatObjectName($objectbool $lowerCase true): string
  1386.     {
  1387.         $objectName $this->getObjectName($object);
  1388.         if ('StatusLog' === $objectName) {
  1389.             $objectName 'Status';
  1390.         }
  1391.         $tableized StringUtil::tableize($objectName);
  1392.         $clean str_replace('_'' '$tableized);
  1393.         return $lowerCase strtolower($clean) : ucwords($clean);
  1394.     }
  1395.     /**
  1396.      * @param Customer\Customer $customer
  1397.      *
  1398.      * @return User|null
  1399.      */
  1400.     private function getAssignedUser(Customer\Customer $customer): ?User
  1401.     {
  1402.         if (!$customer->getConvertedAt()) {
  1403.             return $customer->getCaseManagerUser();
  1404.         }
  1405.         return $customer->getUser();
  1406.     }
  1407.     /**
  1408.      * @param string $name
  1409.      *
  1410.      * @return InterviewNoteMethod|null
  1411.      */
  1412.     private function getInterviewNoteMethod(string $name): ?InterviewNoteMethod
  1413.     {
  1414.         return $this->managerRegistry->getRepository(InterviewNoteMethod::class)->findOneBy(['name' => $name]);
  1415.     }
  1416.     /**
  1417.      * @param Customer\Customer        $customer
  1418.      * @param string                   $body
  1419.      * @param string|null              $type
  1420.      * @param string|null              $section
  1421.      * @param InterviewNoteMethod|null $method
  1422.      * @param bool                     $isSales
  1423.      * @param int|null                 $hours
  1424.      * @param int|null                 $minutes
  1425.      */
  1426.     private function addInterviewNote(
  1427.         Customer\Customer $customer,
  1428.         string $body,
  1429.         string $type null,
  1430.         InterviewNoteMethod $method null,
  1431.         bool $isSales false,
  1432.         bool $isSent false,
  1433.         int $hours null,
  1434.         int $minutes null
  1435.     ): void {
  1436.         $body trim(preg_replace('/\n{2,}/'"\n\n"$body1));
  1437.         if (=== \strlen($body)) {
  1438.             $body null;
  1439.         }
  1440.         $interviewNote = new Customer\InterviewNote();
  1441.         $interviewNote->setCustomer($customer);
  1442.         $interviewNote->setUser($this->getUser());
  1443.         $interviewNote->setBody($body);
  1444.         $interviewNote->setType($type);
  1445.         $interviewNote->setMethod($method);
  1446.         $interviewNote->setSales($isSales);
  1447.         $interviewNote->setSent($isSent);
  1448.         $interviewNote->setHours($hours ?: 0);
  1449.         $interviewNote->setMinutes($minutes ?: 5);
  1450.         if ($this->getCustomerUser() || (!$this->getCustomerUser() && !$this->getUser())) {
  1451.             $interviewNote->setClient(true);
  1452.         }
  1453.         $customer->addInterviewNote($interviewNote);
  1454.     }
  1455.     /**
  1456.      * @param mixed           $object
  1457.      * @param string          $command
  1458.      * @param User|array|null $user
  1459.      * @param array           $params
  1460.      * @param bool            $alert
  1461.      */
  1462.     private function addNotification($objectstring $command$users null, array $params = [], bool $alert true): void
  1463.     {
  1464.         // ignore when no user is defined
  1465.         if (empty($users)) {
  1466.             return;
  1467.         }
  1468.         if (!\is_array($users) && !$users instanceof Collection) {
  1469.             $users = [$users];
  1470.         }
  1471.         $userIds = [];
  1472.         foreach ($users as $user) {
  1473.             $userIds[] = $user->getId();
  1474.         }
  1475.         // @hack
  1476.         $_SERVER['argv'] = [
  1477.             sprintf('%s/bin/console'$this->kernel->getProjectDir()),
  1478.         ];
  1479.         CommandRunner::runCommand(
  1480.             'app:notification:create',
  1481.             array_merge(
  1482.                 [
  1483.                     $object->getId(),
  1484.                     str_replace('\\''\\\\', \get_class($object)),
  1485.                     implode(','$userIds),
  1486.                     $command,
  1487.                     \count($params)
  1488.                         ? urlencode(http_build_query($params))
  1489.                         : '-',
  1490.                     $alert,
  1491.                     $this->getUser()
  1492.                         ? $this->getUser()->getId()
  1493.                         : null,
  1494.                 ],
  1495.                 ['--env' => $this->kernel->getEnvironment()]
  1496.             )
  1497.         );
  1498.     }
  1499.     /**
  1500.      * @param string $work
  1501.      *
  1502.      * @return string
  1503.      */
  1504.     private function getIndefiniteArticle(string $word): string
  1505.     {
  1506.         return preg_match('/^[aeiouAEIOU]/'$word) ? 'an' 'a';
  1507.     }
  1508. }