src/WPXToolsBundle/Services/UserNotification.php line 60

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace WPXToolsBundle\Services;
  4. use Doctrine\ORM\EntityManager;
  5. use Symfony\Component\DependencyInjection\ContainerInterface;
  6. use Symfony\Component\HttpFoundation\RedirectResponse;
  7. use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
  8. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  9. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  10. use Validator\Validator;
  11. use WPXToolsBundle\Entity\NotificationsPriority;
  12. use WPXToolsBundle\Entity\NotificationsType;
  13. use WPXToolsBundle\Entity\PlRequests;
  14. use WPXToolsBundle\Entity\User;
  15. class UserNotification
  16. {
  17.     private $em;
  18.     private $container;
  19.     private $datetime;
  20.     private $datetimeString;
  21.     private $flashBag;
  22.     private $userId;
  23.     private $departmentId;
  24.     private $registerDate;
  25.     public $notifications = array();
  26.     public $notificationsUnseen = array();
  27.     public $notificationFeedbackChats 0;
  28.     public $notificationFeedbackTickets 0;
  29.     public $notificationAssignedIssues 0;
  30.     public $notificationsWidget = array();
  31.     public $notificationsUnseenCount 0;
  32.     /**
  33.      * UserNotification constructor.
  34.      * @param EntityManager $em
  35.      * @param TokenStorageInterface $token
  36.      * @param ContainerInterface $container
  37.      * @param FlashBag $flashBag
  38.      */
  39.     public function __construct(EntityManager $emTokenStorageInterface $tokenContainerInterface $containerFlashBag $flashBag){
  40.         $this->em $em;
  41.         $this->container $container;
  42.         $this->flashBag $flashBag;
  43.         if($token->getToken() && $token->getToken()->getRoles()){
  44.             // Token needs to be checked if exists or not because of security firewall which prevents using token outside of container.
  45.             $this->userId $token->getToken()->getUser()->getId();
  46.             $this->departmentId = ($token->getToken()->getUser()->getPosition()) ? $token->getToken()->getUser()->getPosition()->getDepartment()->getId() : null ;
  47.             $this->registerDate = ($token->getToken()->getUser()->getRegisterDate()) ? $token->getToken()->getUser()->getRegisterDate() : null ;
  48.         }
  49.         $this->datetime = new \DateTime();
  50.         $this->datetimeString $this->datetime->format('Y-m-d H:i:s');
  51.         $this->getNotifications();
  52.         $this->getFeedbackChatsCount();
  53.         $this->getFeedbackTicketsCount();
  54.         $this->getAssignedIssuesCount();
  55.     }
  56.     /**
  57.      * @param int $id
  58.      * @return array
  59.      */
  60.     private function getNotifications(int $id 0){
  61.         $sql "SELECT n.id, n.notificationSubject, n.notificationBody, n.notificationDate, n.isFromSystem, nt.typeName, np.priorityName, np.priorityLabel, np.isFlashable, u.realName as author,
  62.                 IF((SELECT count(nv.id) FROM notifications_viewed as nv WHERE nv.notification_id = n.id AND nv.user_id = :userId) > 0, 1, 0) as viewed -- Check which is unread
  63.                 FROM notifications as n
  64.                 LEFT JOIN users as u ON u.id = n.author_id
  65.                 LEFT JOIN notifications_type as nt ON nt.id = n.notification_type_id
  66.                 LEFT JOIN notifications_priority as np ON np.id = n.notification_priority_id
  67.                 WHERE ((n.notification_type_id = 3 AND FIND_IN_SET(:userId, n.targetId)) -- Get User (type id: 3) notifications
  68.                 OR (n.notification_type_id = 2 AND FIND_IN_SET(:departmentId, n.targetId)) -- Get Department (type id: 2) notifications
  69.                 OR (n.notification_type_id = 1)) -- And all System (type id: 1) notifications
  70.                 AND ((n.startFrom <= :dateNow) AND (n.isEnabled = 1))
  71.                 AND n.id LIKE :notificationId
  72.                 ORDER BY n.id DESC, n.startFrom";
  73.         $query $this->em->getConnection()->prepare($sql);
  74.         $query->execute([
  75.             'userId' => $this->userId,
  76.             'departmentId' => $this->departmentId,
  77.             'dateNow' => $this->datetimeString,
  78.             'notificationId' => ($id) ? $id '%'
  79.         ]);
  80.         $this->notifications = ($id) ? $query->fetch() : $query->fetchAll();
  81.         if($id === 0){
  82.             $i 0;
  83.             foreach ($this->notifications as $notification){
  84.                 $notificationDate = new \DateTime($notification['notificationDate']);
  85.                 if((int)$notification['viewed'] === && isset($this->registerDate) && $notificationDate >= $this->registerDate){
  86.                     $this->notificationsUnseen[] = $notification;
  87.                     $this->notificationsUnseenCount++;
  88.                 }
  89.                 if($i<10 && (int)$notification['isFromSystem'] === 0){
  90.                     $this->notificationsWidget[] = $notification;
  91.                     $i++;
  92.                 }
  93.             }
  94.         }
  95.         return $this->notifications;
  96.     }
  97.     /**
  98.      * @param int $id
  99.      * @return array
  100.      */
  101.     public function viewNotification(int $id){
  102.         $notification $this->getNotifications($id);
  103.         if(!$notification){
  104.             $this->flashBag->add('warning''Unfortunately there is no such notification ID - '.$id.'.');
  105.             return [];
  106.         }elseif((int)$notification['viewed'] === 0){
  107.             $this->setSeenNotification($notification['id']);
  108.         }
  109.         return $notification;
  110.     }
  111.     /**
  112.      * @param $notificationId
  113.      * @return bool
  114.      */
  115.     public function setSeenNotification($notificationId): bool{
  116.         $validator = new Validator([
  117.             'notificationId' => $notificationId
  118.         ], false);
  119.         $validator->validateAll([
  120.             'notificationId' => [
  121.                 'type' => 'int'
  122.             ]
  123.         ]);
  124.         if($validator->hasErrors()){
  125.             foreach ($validator->errors as $error){
  126.                 $this->flashBag->add('warning'$error);
  127.             }
  128.             return false;
  129.         }
  130.         $sql "INSERT INTO notifications_viewed (notification_id, user_id) VALUES (:notificationId, :userId)";
  131.         $query $this->em->getConnection()->prepare($sql);
  132.         $result $query->execute([
  133.             'notificationId' => $notificationId,
  134.             'userId' => $this->userId
  135.         ]);
  136.         return ($result 0) ? true false;
  137.     }
  138.     /**
  139.      * @param array $data
  140.      * @return bool
  141.      */
  142.     public function createNotification(array $data):bool {
  143.         $defaultPriority $this->em->getRepository(NotificationsPriority::class)->findOneBy(['isDefault' => 1]);
  144.         $defaultType $this->em->getRepository(NotificationsType::class)->findOneBy(['isDefault' => 1]);
  145.         $validator = new Validator($datafalse);
  146.         $validator->validateAll([
  147.             'notificationSubject' => [
  148.                 'filter' => 'trim'
  149.             ],
  150.             'notificationDescription' => [
  151.                 'filter' => 'trim'
  152.             ],
  153.             'startFrom' => [
  154.                 'required' => false,
  155.                 'default' => $this->datetimeString,
  156.                 'type' => 'datetime'
  157.             ],
  158.             'notificationPriority' => [
  159.                 'required' => false,
  160.                 'default' => $defaultPriority->getId(),
  161.                 'type' => 'int'
  162.             ],
  163.             'notificationType' => [
  164.                 'required' => false,
  165.                 'default' => $defaultType->getId(),
  166.                 'type' => 'int'
  167.             ],
  168.             'target' => [
  169.                 'required' => false,
  170.                 'type' => 'json',
  171.                 'multiple' => 1
  172.             ],
  173.             'targetById' => [
  174.                 'required' => false,
  175.                 'filter' => 'trim'
  176.             ],
  177.             'isFromSystem' => [
  178.                 'type' => 'int',
  179.                 'required' => false,
  180.                 'default' => 0
  181.             ],
  182.             'isEnabled' => [
  183.                 'required' => false,
  184.                 'match' => ['on''off']
  185.             ]
  186.         ]);
  187.         if($defaultType->getId() !== (int)$validator->valid['notificationType'] && empty($validator->valid['target']) && empty($validator->valid['targetById'])){
  188.             $validator->setInvalid('notificationType''When notification type isn\'t System, there should be at least one target selected.');
  189.         }
  190.         if($validator->hasErrors()){
  191.             foreach ($validator->errors as $error){
  192.                 $this->flashBag->add('warning'$error);
  193.             }
  194.             return false;
  195.         }
  196.         $targets false;
  197.         $type $this->em->getRepository(NotificationsType::class)->findOneBy(['id' => $validator->valid['notificationType']]);
  198.         if($type->getTypeRepository() !== null && $validator->valid['target']){
  199.             $targets json_decode($validator->valid['target']);
  200.             $getTarget $this->em->getRepository('WPXToolsBundle:'.$type->getTypeRepository())->findByName($targets);
  201.             $targets = array();
  202.             foreach ($getTarget as $target){
  203.                 $targets[] = $target->getId();
  204.                 if($type->getTypeRepository() === 'User'){
  205.                     $this->isUserInLeave((int)$target->getId());
  206.                 }
  207.             }
  208.             $targets implode(','$targets);
  209.         }else if($validator->valid['targetById']){
  210.             $targets $validator->valid['targetById'];
  211.             if($type->getTypeRepository() === 'User'){
  212.                 foreach (explode(','$targets) as $target){
  213.                     $this->isUserInLeave((int)$target);
  214.                 }
  215.             }
  216.         }
  217.         $sql "INSERT INTO notifications (notificationSubject, notificationBody, notificationDate, notification_priority_id, notification_type_id, targetId, author_id, startFrom, isFromSystem, isEnabled) 
  218.                 VALUES (:notificationSubject, :notificationBody, :notificationDate, :notificationPriority, :notificationType, :targetId, :authorId, :startFrom, :isFromSystem, :isEnabled)";
  219.         $query $this->em->getConnection()->prepare($sql);
  220.         $query->execute([
  221.             'notificationSubject' => $validator->valid['notificationSubject'],
  222.             'notificationBody' => $validator->valid['notificationDescription'],
  223.             'notificationDate' => $this->datetimeString,
  224.             'notificationPriority' => (int)$validator->valid['notificationPriority'],
  225.             'notificationType' => (int)$validator->valid['notificationType'],
  226.             'targetId' => ($targets) ? $targets 0,
  227.             'authorId' => (int)$this->userId,
  228.             'startFrom' => $validator->valid['startFrom'],
  229.             'isFromSystem' => (int)$validator->valid['isFromSystem'],
  230.             'isEnabled' => ($validator->valid['isEnabled'] === 'on' || (int)$validator->valid['isFromSystem']) ? 0
  231.         ]);
  232.         return true;
  233.     }
  234.     /**
  235.      * @param int $userId
  236.      * @return bool
  237.      */
  238.     private function isUserInLeave(int $userId):bool {
  239.         $target $this->em->getRepository(User::class)->findOneBy(['id' => $userId]);
  240.         $sql "SELECT pr.dateFrom, pr.dateTo, plt.leaveName FROM pl_requests as pr 
  241.                             JOIN pl_leave_types as plt ON plt.id = pr.leave_type_id
  242.                             WHERE user_requested_id = :userId AND isApproved = '1' AND now() BETWEEN dateFrom AND dateTo
  243.                             ORDER BY pr.dateFrom DESC
  244.                             LIMIT 1";
  245.         $query $this->em->getConnection()->prepare($sql);
  246.         $query->execute([
  247.             'userId' => $userId
  248.         ]);
  249.         $result $query->fetch();
  250.         if($query->rowCount() > 0){
  251.             $this->flashBag->add('warning''Please note that '.$target->getRealName().' is currently '.$result['leaveName'].' from '.$result['dateFrom'].' to '.$result['dateTo'].' and will review their notification as soon as they\'re available.');
  252.         }
  253.         return (bool)$query->rowCount();
  254.     }
  255.     /**
  256.      * Writes directly a value to a public variable to provide the frontend with data.
  257.      */
  258.     private function getFeedbackChatsCount(){
  259.         $sql "SELECT count(qc.id) as unseenChats FROM qa_chats as qc 
  260.                 JOIN qa_chat_participants as qcp ON qcp.chatId = qc.chatId 
  261.                 WHERE qcp.agentId = :userId AND qcp.seenByAgent = '0' AND qc.toUserFeedback = '1'";
  262.         $query $this->em->getConnection()->prepare($sql);
  263.         $query->execute([
  264.             'userId' => $this->userId
  265.         ]);
  266.         $result $query->fetch();
  267.         $this->notificationFeedbackChats = (int)$result['unseenChats'];
  268.     }
  269.     /**
  270.      * Writes directly a value to a public variable to provide the frontend with data.
  271.      */
  272.     private function getAssignedIssuesCount(){
  273.         $sql "SELECT count(qc.id) as unseenChats FROM qa_chats as qc
  274.                 WHERE qc.assign_to_id = :userId AND qc.chatSeen = '0'";
  275.         $query $this->em->getConnection()->prepare($sql);
  276.         $query->execute([
  277.             'userId' => $this->userId
  278.         ]);
  279.         $countChats $query->fetch();
  280.         $sql "SELECT count(qt.id) as unseenTickets FROM qa_tickets as qt
  281.                 WHERE qt.assign_to_id = :userId AND qt.ticketSeen = '0'";
  282.         $query $this->em->getConnection()->prepare($sql);
  283.         $query->execute([
  284.             'userId' => $this->userId
  285.         ]);
  286.         $countTickets $query->fetch();
  287.         $this->notificationAssignedIssues = (int)$countChats['unseenChats']+$countTickets['unseenTickets'];
  288.     }
  289.     /**
  290.      * Writes directly a value to a public variable to provide the frontend with data.
  291.      */
  292.     private function getFeedbackTicketsCount(){
  293.         $sql "SELECT count(qttf.id) as unseenTickets FROM qa_tickets_to_feedbacks as qttf 
  294.                 WHERE qttf.user_id = :userId AND qttf.isSeen = 0";
  295.         $query $this->em->getConnection()->prepare($sql);
  296.         $query->execute([
  297.             'userId' => $this->userId
  298.         ]);
  299.         $result $query->fetch();
  300.         $this->notificationFeedbackTickets = (int)$result['unseenTickets'];
  301.     }
  302.     /**
  303.      * @param PlRequests $request
  304.      * @param string $actionType Possible values: ['notify_approvers', 'notify_requester', 'changing_approver', 'editing_approved_request', 'send_request']
  305.      * @param int $response
  306.      * @param array $recipients If not empty, the method will send notification only to listed recipients in the variable.
  307.      * @return bool
  308.      */
  309.     public function sendLeaveRequestNotification(PlRequests $requeststring $actionTypeint $response null, array $recipients = []):bool {
  310.         if(!in_array($actionType, ['notify_approvers''notify_requester''changing_approver''editing_approved_request''send_request'])) return false;
  311.         $currentUser $this->container->get('security.token_storage')->getToken()->getUser();
  312.         $answerString = ($response) ? 'APPROVED' 'DENIED';
  313.         if($actionType === 'notify_requester'){
  314.             array_push($recipients$request->getUserRequested()->getId());
  315.         }else{
  316.             if(empty($recipients)){
  317.                 if($request->getUserApproved()) array_push($recipients$request->getUserApproved()->getId());
  318.                 if($request->getUserApprovedSecond()) array_push($recipients$request->getUserApprovedSecond()->getId());
  319.             }
  320.         }
  321.         $data = array();
  322.         switch ($actionType){
  323.             case 'notify_approvers':
  324.                 $data['subject'] = sprintf('[%s] %s is %s to have a %s (%s to %s)',
  325.                     $answerString,
  326.                     $request->getUserRequested()->getRealName(),
  327.                     $answerString,
  328.                     $request->getLeaveType()->getLeaveName(),
  329.                     $request->getDateFrom()->format('d.m.Y'),
  330.                     $request->getDateTo()->format('d.m.Y'));
  331.                 $data['body'] = sprintf("Hello,\r\n\r\n%s is %s to have %s day(s) of %s\r\n\r\nFrom: %s\r\n\r\nTo: %s\r\n\r\nReason (optional): %s",
  332.                     $request->getUserRequested()->getRealName(),
  333.                     $answerString,
  334.                     $request->getLeaveDays(),
  335.                     $request->getLeaveType()->getLeaveName(),
  336.                     $request->getDateFrom()->format('d.m.Y'),
  337.                     $request->getDateTo()->format('d.m.Y'),
  338.                     $request->getLeaveReason());
  339.                 break;
  340.             case 'notify_requester':
  341.                 $data['subject'] = sprintf('%s %s your leave request.'$currentUser->getRealName(), $answerString);
  342.                 $data['body'] = sprintf("Requested leave: \r\n%s - %s days of %s from %s to %s\r\n Reason (optional): %s\r\nYour request has been %s. You can review your requests here - %s",
  343.                     $request->getUserRequested()->getRealName(),
  344.                     $request->getLeaveDays(),
  345.                     $request->getLeaveType()->getLeaveName(),
  346.                     $request->getDateFrom()->format('d.m.Y'),
  347.                     $request->getDateTo()->format('d.m.Y'),
  348.                     $request->getLeaveReason(),
  349.                     $answerString,
  350.                     $this->container->get('router')->getGenerator()->generate('leave_requests_my', [], UrlGeneratorInterface::ABSOLUTE_URL));
  351.                 break;
  352.             case 'send_request':
  353.             case 'changing_approver':
  354.                 $data['subject'] = sprintf('[REQUEST] %s has requested a %s',
  355.                     $request->getUserRequested()->getRealName(),
  356.                     $request->getLeaveType()->getLeaveName());
  357.                 $data['body'] = sprintf("Hello,\r\n\r\n%s has requested a %s.\r\n\r\nYou are one of the assigned people to review the request.\r\n\r\nPlease review it as soon as possible in Alice:\r\n\r\n%s",
  358.                     $request->getUserRequested()->getRealName(),
  359.                     $request->getLeaveType()->getLeaveName(),
  360.                     $this->container->get('router')->getGenerator()->generate('leave_requests_exact', [
  361.                         'year' => $request->getDateFrom()->format('Y'),
  362.                         'month' => $request->getDateFrom()->format('m'),
  363.                         'department' => 0
  364.                     ], UrlGeneratorInterface::ABSOLUTE_URL));
  365.                 break;
  366.             case 'editing_approved_request':
  367.                 $data['subject'] = sprintf('[EDITED] %s\'s %s request has been edited',
  368.                     $request->getUserRequested()->getRealName(),
  369.                     $request->getLeaveType()->getLeaveName());
  370.                 $data['body'] = sprintf("Hello,\r\n\r\n%s's %s request for %s day(s) [Dates: %s - %s] has been edited.\r\n\r\nPlease consider checking the changes to the request as soon as possible to avoid mistakes.\r\n\r\nYou check check it out in Alice here:\r\n\r\n%s",
  371.                     $request->getUserRequested()->getRealName(),
  372.                     $request->getLeaveType()->getLeaveName(),
  373.                     $request->getLeaveDays(),
  374.                     $request->getDateFrom()->format('d.m.Y'),
  375.                     $request->getDateTo()->format('d.m.Y'),
  376.                     $this->container->get('router')->getGenerator()->generate('leave_requests_exact', [
  377.                         'year' => $request->getDateFrom()->format('Y'),
  378.                         'month' => $request->getDateFrom()->format('m'),
  379.                         'department' => 0
  380.                     ], UrlGeneratorInterface::ABSOLUTE_URL));
  381.                 break;
  382.         }
  383.         return $this->createNotification([
  384.             'notificationSubject'        => $data['subject'],
  385.             'notificationDescription'    => $data['body'],
  386.             'notificationType'            => 3// User type
  387.             'targetById'                => implode(','$recipients),
  388.             'isFromSystem'                => 1
  389.         ]);
  390.     }
  391. }