src/Controller/ApiController.php line 706

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\Answer;
  4. use App\Entity\Resource;
  5. use Symfony\Component\HttpFoundation\BinaryFileResponse;
  6. use App\Entity\ResourceMessage;
  7. use App\Entity\User;
  8. use App\Helpers\CalCommons;
  9. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  10. use Symfony\Component\HttpFoundation\JsonResponse;
  11. use Symfony\Component\HttpFoundation\Request;
  12. use Symfony\Component\HttpFoundation\Response;
  13. use Symfony\Component\Routing\Annotation\Route;
  14. use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
  15. use Ramsey\Uuid\Uuid;
  16. use Doctrine\Common\Collections\Criteria;
  17. class ApiController extends AbstractController {
  18.     use ApiTraits\AppControllerTrait,
  19.         ApiTraits\ClientControllerTrait;
  20.     protected function getCurrentUser() {
  21.         if (!$this->container->has('security.token_storage')) {
  22.             throw new \LogicException('The Security Bundle is not registered in your application.');
  23.         }
  24.         if (null === $token $this->container->get('security.token_storage')->getToken()) {
  25.             return;
  26.         }
  27.         if (!is_object($user $token->getUser())) {
  28.             // e.g. anonymous authentication
  29.             return;
  30.         }
  31.         return $user;
  32.     }
  33.     /**
  34.      * @Route("/password")
  35.      *
  36.      * @param Request $request
  37.      *
  38.      * @return JsonResponse
  39.      *
  40.      * @throws \Exception
  41.      *
  42.     public function setPW(Request $request, UserPasswordEncoderInterface $passwordEncoder): JsonResponse {
  43.         $uamn = $this->getDoctrine()->getManager()->getRepository(User::class);
  44.         $user = $uamn->find(1);
  45.         $user->setPassword($passwordEncoder->encodePassword($user, "1234"));
  46.         $uamn->save($user);
  47.         return new JsonResponse(["none"]);
  48.     }*/
  49.     /**
  50.      * @Route("/save", methods={"POST"})
  51.      *
  52.      * @param Request $request
  53.      *
  54.      * @return JsonResponse
  55.      *
  56.      * @throws \Exception
  57.      */
  58.     public function saveAction(Request $request) {
  59.         $user $this->getUser();
  60.         if (is_null($user)) {
  61.             $response = new JsonResponse(["message" => "invalid token"]);
  62.             $response->setStatusCode(401);
  63.             return $response;
  64.         }
  65.         $data json_decode($request->getContent(), true);
  66.         $version $request->query->get("v""last");
  67.         $debug $this->saveAll($data$user"browser"$version);
  68.         return new JsonResponse($debug);
  69.     }
  70.     /**
  71.      * @Route("/loadFile", methods={"POST"})
  72.      *
  73.      * @param Request $request
  74.      *
  75.      * @return JsonResponse
  76.      *
  77.      * @throws \Exception
  78.      */
  79.     public function serveFileAction(Request $request) {
  80.         $user $this->getUser();
  81.         if (is_null($user)) {
  82.             $response = new JsonResponse(["message" => "invalid token"]);
  83.             $response->setStatusCode(401);
  84.             return $response;
  85.         }
  86.         $data json_decode($request->getContent(), true);
  87.         $message $this->getDoctrine()
  88.                         ->getRepository(ResourceMessage::class)->findOneBy(["uuid" => $data["messageId"]]);
  89.         if (is_null($message)) {
  90.             $response = new JsonResponse(["message" => "invalid msgid"]);
  91.             $response->setStatusCode(404);
  92.             return $response;
  93.         }
  94.         $userGroupIds = [];
  95.         foreach ($user->getGroups() as $group) {
  96.             $userGroupIds[$group->getId()] = true;
  97.         }
  98.         $fileName "/var/www/prod-01-infra-server/files/" $message->getAttachment();
  99.         if (file_exists($fileName)) {
  100.             foreach ($message->getUser()->getGroups() as $foreignGroup) {
  101.                 if (isset($userGroupIds[$foreignGroup->getId()]))
  102.                     return new BinaryFileResponse($fileName);
  103.             }
  104.         }
  105.         $response = new JsonResponse(["message" => "not allowed""file" => $fileName]);
  106.         $response->setStatusCode(403);
  107.         return $response;
  108.     }
  109.     /**
  110.      * @Route("/saveFeed", methods={"POST"})
  111.      *
  112.      * @param Request $request
  113.      *
  114.      * @return JsonResponse
  115.      *
  116.      * @throws \Exception
  117.      */
  118.     public function saveFeedAction(Request $request) {
  119.         $user $this->getUser();
  120.         if (is_null($user)) {
  121.             $response = new JsonResponse(["message" => "invalid token"]);
  122.             $response->setStatusCode(401);
  123.             return $response;
  124.         }
  125.         $resourceId $request->get("resource");
  126.         $resource $this->getDoctrine()
  127.                         ->getRepository(Resource::class)->findOneBy(["uuid" => $resourceId]);
  128.         if (is_null($resource)) {
  129.             $response = new JsonResponse(["message" => "invalid resource"]);
  130.             $response->setStatusCode(400);
  131.             return $response;
  132.         }
  133.         if (($file $request->files->get('image'))) {
  134.             $originalFilename pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
  135.             $safeFilename preg_replace("/[^A-Za-z0-9]/"""$originalFilename);
  136.             $fileName $safeFilename '-' uniqid() . '.' $file->getClientOriginalExtension();
  137.             try {
  138.                 $file->move("/var/www/prod-01-infra-server/files/"$fileName);
  139.                 $fileUrl $fileName;
  140.             } catch (FileException $e) {
  141.                 $fileUrl null;
  142.             }
  143.         } else
  144.             $fileUrl null;
  145.         $data = ["resourceMessages" => [
  146.                 [
  147.                     "type" => $request->get("type"),
  148.                     "message" => $request->get("text"),
  149.                     "_resource" => $resourceId,
  150.                     "attachment" => $fileUrl,
  151.                     "id" => Uuid::uuid4(),
  152.                     "updatedAt" => (new \DateTime())->getTimestamp() * 1000
  153.                 ]
  154.         ]];
  155.         $this->saveAll($data$user"browser""1");
  156.         $version $request->query->get("v""last");
  157.         $resourceMessages $this->export($this->getDoctrine()->getRepository(ResourceMessage::class)->findAllWithResourceIn([$resource]), "browser"$version);
  158.         return new JsonResponse($resourceMessages);
  159.     }
  160.     /**
  161.      * @Route("/getProjectAttachment/{uuid}/{name}", methods={"GET"})
  162.      *
  163.      * @param Request $request
  164.      *
  165.      * @return JsonResponse
  166.      *
  167.      * @throws \Exception
  168.      */
  169.     public function getFileAction($uuid) {
  170.         $message $this->getDoctrine()
  171.                         ->getRepository("App\Entity\ProjectAttachment")->findOneBy(["uuid" => $uuid]);
  172.         if (is_null($message)) {
  173.             $response = new JsonResponse(["message" => "invalid msgid"]);
  174.             $response->setStatusCode(404);
  175.             return $response;
  176.         }
  177.         $fileName "/var/www/prod-01-infra-server/files/" $message->getAttachment();
  178.         if (file_exists($fileName)) {
  179.             return new BinaryFileResponse($fileName);
  180.         }
  181.         $response = new JsonResponse(["message" => "not allowed""file" => $fileName]);
  182.         $response->setStatusCode(403);
  183.         return $response;
  184.     }
  185.     /**
  186.      * @Route("/saveProjectAttachment", methods={"POST"})
  187.      *
  188.      * @param Request $request
  189.      *
  190.      * @return JsonResponse
  191.      *
  192.      * @throws \Exception
  193.      */
  194.     public function saveProjectAttachmentAction(Request $request) {
  195.         $user $this->getUser();
  196.         if (is_null($user)) {
  197.             $response = new JsonResponse(["message" => "invalid token"]);
  198.             $response->setStatusCode(401);
  199.             return $response;
  200.         }
  201.         $projectId $request->get("project");
  202.         $project $this->getDoctrine()
  203.                         ->getRepository("App\Entity\Project")->findOneBy(["uuid" => $projectId]);
  204.         if (is_null($project)) {
  205.             $response = new JsonResponse(["message" => "invalid project"]);
  206.             $response->setStatusCode(400);
  207.             return $response;
  208.         }
  209.         if (($file $request->files->get('file'))) {
  210.             $originalFilename pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
  211.             $safeFilename preg_replace("/[^A-Za-z0-9]/"""$originalFilename);
  212.             $fileName $safeFilename '-' uniqid() . '.' $file->getClientOriginalExtension();
  213.             try {
  214.                 $file->move("/var/www/prod-01-infra-server/files/"$fileName);
  215.                 $fileUrl $fileName;
  216.             } catch (FileException $e) {
  217.                 $fileUrl null;
  218.             }
  219.         } else
  220.             $fileUrl null;
  221.         return new JsonResponse(["attachment" => $fileUrl"user" => $user->getUuid(), "creator" => $user->getName()]);
  222.     }
  223.     /**
  224.      * @Route("/loadReport/{start}/{end}")
  225.      *
  226.      * @param Request $request
  227.      *
  228.      * @return JsonResponse
  229.      *
  230.      * @throws \Exception
  231.      */
  232.     public function loadReportAction($start$end)
  233.     {
  234.         $user $this->getUser();
  235.         if (is_null($user)) {
  236.             $response = new JsonResponse(["message" => "invalid token"]);
  237.             $response->setStatusCode(401);
  238.             return $response;
  239.         }
  240.         $d $this->getDoctrine();
  241.         $startTime = (new \DateTime())->setTimestamp(ceil($start 1000));
  242.         $endTime = (new \DateTime())->setTimestamp(ceil($end 1000));
  243.         $projects $this->getDoctrine()->getRepository("App\Entity\Project")->findAllWithRights($user);
  244.         $pids = [];
  245.         $uuids = [];
  246.         $bpo = [];
  247.         foreach ($projects as $project) {
  248.             $id $project->getId();
  249.             $uuid $project->getUuid();
  250.             $bpokey $project->getBpokey();
  251.             $pids[] = $id;
  252.             $uuids[$id] = $uuid;
  253.             $bpo[$id] = $bpokey;
  254.         }
  255.         // $pids = array_map(function ($e) {
  256.         //     return $e->getId();
  257.         // }, $projects);
  258.         $projectCrit Criteria::expr()->in('project'$pids);
  259.         $endCrit Criteria::expr()->lt('start'$endTime);
  260.         $startCrit Criteria::expr()->gt('end'$startTime);
  261.         $deletedCrit Criteria::expr()->eq('deletedAt'NULL);
  262.         $jobCriteria Criteria::create()->where(Criteria::expr()->andX($projectCrit$deletedCrit$startCrit$endCrit));
  263.         $jobs $d->getRepository("App\Entity\Job")->matching($jobCriteria);
  264.         $reports $d->getRepository("App\Entity\Answer")->getReports($jobs);
  265.         return new JsonResponse(array_map(function ($x) use ($uuids$bpo) {
  266.             $resourceUuid '';
  267.             if (isset($x['answer']) && $x['answer'] instanceof Answer) {
  268.                 $answeredBy $x['answer']->getAnsweredBy();
  269.                 if ($answeredBy instanceof Resource) {
  270.                     $resourceUuid $answeredBy->getUuid();
  271.                 }
  272.             }
  273.             return [
  274.                 "project" => $uuids[$x["pproject"]] ?? "",
  275.                 "bpokey" => $bpo[$x["pproject"]] ?? "",
  276.                 "date" => $x["pdate"]->format("Y-m-d"),
  277.                 "answeredBy" => $resourceUuid
  278.             ];
  279.         }, $reports));
  280.     }
  281.     /**
  282.      * @Route("/loadReportAnswers/{projectId}/{date}")
  283.      *
  284.      * @param Request $request
  285.      *
  286.      * @return JsonResponse
  287.      *
  288.      * @throws \Exception
  289.      */
  290.     public function loadReportAnswersAction(Request $request$projectId$date) {
  291.         $user $this->getUser();
  292.         if (is_null($user)) {
  293.             $response = new JsonResponse(["message" => "invalid token"]);
  294.             $response->setStatusCode(401);
  295.             return $response;
  296.         }
  297.         $company $user->getCompany();
  298.         $companyId $company->getId() ?? 0;
  299.         $d $this->getDoctrine();
  300.         $project $d->getRepository("App\Entity\Project")->findOneBy(["uuid" => $projectId]);
  301.         $reports $d->getRepository("App\Entity\Answer")->getAnswers($project$date);
  302.         $images $d->getRepository("App\Entity\JobMessage")->getMessages($project$date);
  303.         $version $request->query->get("v""last");
  304.         return new JsonResponse([
  305.             "answers" => $this->export($reports"browser"$version),
  306.             "images" => $this->export($images"browser"$version),
  307.             "company_id" => $companyId,
  308.         ]);
  309.     }
  310.     /**
  311.      * @Route("/load/{type}/{since}/{month}")
  312.      *
  313.      * @param Request $request
  314.      *
  315.      * @return JsonResponse
  316.      *
  317.      * @throws \Exception
  318.      */
  319.     public function loadAction(Request $request$type "default"$since 0$month "2021-0") {
  320.         $user $this->getUser();
  321.         if (is_null($user)) {
  322.             $response = new JsonResponse(["message" => "invalid token"]);
  323.             $response->setStatusCode(401);
  324.             return $response;
  325.         }
  326.         $version $request->query->get("v""last");
  327.         return new JsonResponse($this->loadData($user$type$since$version$month));
  328.     }
  329.     /**
  330.      * @Route("/encode/{code}")
  331.      *
  332.      * @param Request $request
  333.      *
  334.      * @return JsonResponse
  335.      *
  336.      * @throws \Exception
  337.      */
  338.     public function encodeAction($code) {
  339.         $out CalCommons::encodeRes($code);
  340.         return new JsonResponse($out);
  341.     }
  342.     public function decodeRes($code) {
  343.         $out CalCommons::decodeRes($code);
  344.         if ($out === FALSE) {
  345.             return [falsefalse];
  346.         }
  347.         if ($out 0) {
  348.             $entity $this->getDoctrine()->getRepository("App\Entity\User")->findOneBy(["bpo_id" => $out * -1]);
  349.             $type "USER";
  350.         } else {
  351.             $type "RESOURCE";
  352.             $entity $this->getDoctrine()->getRepository("App\Entity\Resource")->findOneBy(["bpo_id" => $out]);
  353.         }
  354.         return [$type$entity];
  355.     }
  356.     /**
  357.      * @Route("/appAuth/{code}")
  358.      *
  359.      * @param Request $request
  360.      *
  361.      * @return JsonResponse
  362.      *
  363.      * @throws \Exception
  364.      */
  365.     public function appAuthAction($code) {
  366.         list($type$entity) = $this->decodeRes(preg_replace("/[^0-9]/"""$code));
  367.         if ($type === FALSE) {
  368.             $response = new JsonResponse(["message" => "invalid token"]);
  369.             $response->setStatusCode(401);
  370.             return $response;
  371.         }
  372.         if ($entity->isDeleted()) {
  373.             $response = new JsonResponse(["message" => "invalid token"]);
  374.             $response->setStatusCode(401);
  375.             return $response;
  376.         }
  377.         return new JsonResponse(["name" => $entity->getName(), "image" => $type === "RESOURCE" $entity->getImage() : """id" => $entity->getUuid(), "type" => $type"code" => trim($code)]);
  378.     }
  379.     /**
  380.      * @Route("/appBaseData/{code}/{group}/{since}")
  381.      *
  382.      * @param Request $request
  383.      *
  384.      * @return JsonResponse
  385.      *
  386.      * @throws \Exception
  387.      */
  388.     public function appBaseDataAction(Request $request$code$group$since 0) {
  389.         $out CalCommons::decodeRes($code);
  390.         if ($out === FALSE) {
  391.             $response = new JsonResponse(["message" => "invalid token"]);
  392.             $response->setStatusCode(401);
  393.             return $response;
  394.         }
  395.         $entity $this->getDoctrine()->getRepository("App\Entity\UserGroup")->findOneBy(["uuid" => $group]);
  396.         if ($entity->isDeleted())
  397.             $entity null;
  398.         if (is_null($entity)) {
  399.             $response = new JsonResponse(["message" => "invalid entity"]);
  400.             $response->setStatusCode(404);
  401.             return $response;
  402.         }
  403.         $utime $since 1000;
  404.         if ($utime strtotime("-1 year"))
  405.             $sinceTime null;
  406.         else
  407.             $sinceTime = (new \DateTime())->setTimestamp(ceil($utime));
  408.         $version $request->query->get("v""last");
  409.         return new JsonResponse($this->appGetBaseData($entity$sinceTime"app"$version));
  410.     }
  411.     /**
  412.      * @Route("/loadStatistics/{resourceId}/{startDate}/{startInt}/{endDate}/{endInt}")
  413.      *
  414.      * @param Request $request
  415.      *
  416.      * @return JsonResponse
  417.      *
  418.      * @throws \Exception
  419.      */
  420.     public function getReportStatistics(Request $request$resourceId$startDate$startInt$endDate$endInt) {
  421.         $user $this->getUser();
  422.         if (is_null($user)) {
  423.             $response = new JsonResponse(["message" => "invalid token"]);
  424.             $response->setStatusCode(401);
  425.             return $response;
  426.         }
  427.         $groups $user->getGroups();
  428.         $start = (new \DateTime())->setTimestamp(ceil($startInt/1000));
  429.         $end = (new \DateTime())->setTimestamp(ceil($endInt/1000));
  430.         $startDate = new \DateTime($startDate);
  431.         $endDate = new \DateTime($endDate);
  432.         $checking = [];
  433.         $d $this->getDoctrine();
  434.         $resource $d->getRepository("App\Entity\Resource")->findOneBy(["uuid" => $resourceId]);
  435.         if (is_null($resource) || !$groups->contains($resource->getOwner())) {
  436.             $response = new JsonResponse(["message" => "invalid resource"]);
  437.             $response->setStatusCode(400);
  438.             return $response;
  439.         }
  440.         $betweenDateExpression Criteria::expr()->andX(
  441.                 Criteria::expr()->gte('date'$startDate),
  442.                 Criteria::expr()->lte('date'$endDate)
  443.         );
  444.         $resExpression Criteria::expr()->eq('resource'$resource);
  445.         $xExpression Criteria::expr()->eq('deletedAt'NULL);
  446.         $groupExpression Criteria::expr()->in('owner'$groups->map(function ($e) {
  447.                     return $e->getId();
  448.                 })->toArray());
  449.         $deployments $d->getRepository("App\Entity\Deployment")->findNotDeletedByResInTimespan([$resource->getId()], $start$end);
  450.         $absences $d->getRepository("App\Entity\Absence")->matching(Criteria::create()->where($resExpression)->andWhere($xExpression)->andWhere($betweenDateExpression));
  451.         $exceptions $d->getRepository("App\Entity\WorkDay")->matching(Criteria::create()->where($groupExpression)->andWhere($xExpression)->andWhere(Criteria::expr()->eq('holiday'false))->andWhere($betweenDateExpression))->map(function($x) {
  452.                     return $x->getDateExport();
  453.                 });
  454.         $holidays $d->getRepository("App\Entity\WorkDay")->matching(Criteria::create()->where($groupExpression)->andWhere($xExpression)->andWhere(Criteria::expr()->eq('holiday'true))->andWhere($betweenDateExpression))->map(function($x) {
  455.             return $x->getDateExport();
  456.         });
  457.         $absenceList = [];
  458.         foreach ($absences as $absence) {
  459.             $name $absence->getType()->getName();
  460.             $date $absence->getDate()->format("Y-m-d");
  461.             if (!isset($absenceList[$name]))
  462.                 $absenceList[$name] = [];
  463.             if (!isset($absenceList[$name][$date]))
  464.                 $absenceList[$name][$date] = true;
  465.         }
  466.         $absenceList array_map(function ($x) {
  467.             return count($x);
  468.         }, $absenceList);
  469.         $useDays = [];
  470.         foreach ($deployments as $deployment) {
  471.             $startX $deployment->getJobStart();
  472.             if (!$startX)
  473.                 continue;
  474.             $useDays[(new \DateTime())->setTimestamp(ceil($startX/1000))->format("Y-m-d")] = true;
  475.         }
  476.         $isWorkingDay = function ($momentObj) use ($holidays$exceptions) {
  477.             $workDays = [truetruetruetruetruefalsefalse];
  478.             $date $momentObj->format("Y-m-d");
  479.             $isHoliday $holidays->contains($date);
  480.             $isException $exceptions->contains($date);
  481.             $isWorkingDay $workDays[$momentObj->format("N") - 1] ?? true;
  482.             $key = ($isWorkingDay 0) +
  483.                     ($isHoliday 0) +
  484.                     ($isException 0);
  485.             switch ($key) {
  486.                 case 4:
  487.                 case 6:
  488.                 case 7:
  489.                 case 5:
  490.                 case 1:
  491.                     return true;
  492.                 default:
  493.                     return false;
  494.             }
  495.         };
  496.         $calDays 0;
  497.         $workDays 0;
  498.         for (
  499.         $currentDay = clone $startDate$currentDay <= $endDate$currentDay->modify("+1 day")
  500.         ) {
  501.             $key $currentDay->format("Y-m-d");
  502.             $calDays++;
  503.             if (
  504.                     isset($useDays[$key]) ||
  505.                     $isWorkingDay($currentDay)
  506.             ){
  507.                 $workDays++;
  508.             }
  509.         }
  510.         return new JsonResponse([
  511.             "calDays" => $calDays,
  512.             "checking" => $checking,
  513.             "workDays" => $workDays,
  514.             "useDays" => count($useDays),
  515.             "absences" => $absenceList
  516.         ]);
  517.     }
  518.     /**
  519.      * @Route("/app/{code}/{start}/{end}/{since}")
  520.      *
  521.      * @param Request $request
  522.      *
  523.      * @return JsonResponse
  524.      *
  525.      * @throws \Exception
  526.      */
  527.     public function appAction(Request $request$code$start$end$since 0) {
  528.         $out CalCommons::decodeRes($code);
  529.         $data json_decode($request->getContent(), true);
  530.         if ($out === FALSE) {
  531.             $response = new JsonResponse(["message" => "invalid token"]);
  532.             $response->setStatusCode(401);
  533.             return $response;
  534.         }
  535.         if ($out 0) {
  536.             $type "appUser";
  537.             $entity $this->getDoctrine()->getRepository("App\Entity\User")->findOneBy(["bpo_id" => $out * -1]);
  538.         } else {
  539.             $type "appResource";
  540.             $entity $this->getDoctrine()->getRepository("App\Entity\Resource")->findOneBy(["bpo_id" => $out]);
  541.         }
  542.         if ($entity->isDeleted())
  543.             $entity null;
  544.         if (is_null($entity)) {
  545.             $response = new JsonResponse(["message" => "invalid entity"]);
  546.             $response->setStatusCode(404);
  547.             return $response;
  548.         }
  549.         $version $request->query->get("v""last");
  550.         if (!empty($data["save"]))
  551.             $this->saveAll($data["save"], null"app"$version);
  552.         $utime $since 1000;
  553.         if ($utime strtotime("-1 year"))
  554.             $sinceTime null;
  555.         else
  556.             $sinceTime = (new \DateTime())->setTimestamp(ceil($utime));
  557.         try {
  558.             $startTime = (new \DateTime())->setTimestamp(ceil($start 1000));
  559.             $endTime = (new \DateTime())->setTimestamp(ceil($end 1000));
  560.         } catch (Exception $e) {
  561.             $response = new JsonResponse(["message" => "invalid dates"]);
  562.             $response->setStatusCode(400);
  563.             return $response;
  564.         }
  565.         if ($type === "appUser") {
  566.             return new JsonResponse($this->getAppUser($entity$startTime$endTime$sinceTime$version));
  567.         } else if ($type === "appResource") {
  568.             return new JsonResponse($this->getAppResource($entity$startTime$endTime$sinceTime$version));
  569.         }
  570.     }
  571.     /**
  572.      * @Route("/")
  573.      *
  574.      * @param mixed $string
  575.      *
  576.      * @return JsonResponse
  577.      */
  578.     public function noAction($string ''): Response {
  579.         return new JsonResponse([]);
  580.     }
  581.     protected function filterByTime($collection$since) {
  582.         return array_values(array_filter($collection, function ($x) use ($since) {
  583.                     return $x->getUpdatedAt() >= $since;
  584.                 }));
  585.     }
  586.     protected function filterDeleted($collection) {
  587.         return array_values(array_filter($collection, function ($x) {
  588.                     return !$x->isDeleted();
  589.                 }));
  590.     }
  591.     protected function export(array $x$context "browser"$version "last") {
  592.         return array_values(array_map(function ($e) use ($context$version) {
  593.                     return $e->getExport($context$version);
  594.                 }, $x));
  595.     }
  596.     protected function exportColl($x$context "browser"$version "last") {
  597.         return array_values($x->map(function ($e) use ($context$version) {
  598.                     return $e->getExport($context$version);
  599.                 })->toArray());
  600.     }
  601. }