'.$string.'>');
+ $this->filesystem->appendToFile($this->rootlog.'cron.log', $string."\n");
+ }
+
+ private function writeln($string) {
+ $this->output->writeln($string);
+ $this->filesystem->appendToFile($this->rootlog.'cron.log', $string."\n");
+ }
+
+ protected function addUser($niveau01,$username,$firstname,$lastname,$email,$usersadmin) {
+ $user = new User();
+
+ $user->setUsername($username);
+ $user->setLastname($lastname);
+ $user->setFirstname($firstname);
+ $user->setEmail($email);
+ $user->setNiveau01($niveau01);
+ $user->setSiren($niveau01->getSiren());
+ $user->setPassword("PASSWORDFROMEXTERNE");
+ $user->setVisible(true);
+ $user->setAuthlevel("simple");
+ $user->setBelongingpopulation("agent");
+
+ if(in_array($username,$usersadmin))
+ $user->setRole("ROLE_ADMIN");
+ else {
+ $user->setRole("ROLE_USER");
+
+ // Si modèle scribe
+ $ldap_template = $this->container->getParameter('ldap_template');
+ if($ldap_template=="scribe") {
+ $ldapfilter="(|(&(uid=".$user->getUsername().")(ENTPersonProfils=enseignant))(&(uid=".$user->getUsername().")(typeadmin=0))(&(uid=".$user->getUsername().")(typeadmin=2)))";
+ $results = $this->ldap->search($ldapfilter, ['uid'], $this->ldap_basedn);
+ if($results) $user->setRole("ROLE_ANIM");
+ }
+ }
+
+ $this->em->persist($user);
+ $this->em->flush();
+ }
+
+ protected function modUser($user,$username,$firstname,$lastname,$email,$usersadmin) {
+ $user->setLastname($lastname);
+ $user->setFirstname($firstname);
+ $user->setEmail($email);
+
+ if(in_array($username,$usersadmin))
+ $user->setRole("ROLE_ADMIN");
+
+ $this->em->persist($user);
+ $this->em->flush();
+ }
+
+}
diff --git a/src/Controller/.gitignore b/src/Controller/.gitignore
new file mode 100644
index 0000000..e69de29
diff --git a/src/Controller/ConfigController.php b/src/Controller/ConfigController.php
new file mode 100644
index 0000000..bbf5ffa
--- /dev/null
+++ b/src/Controller/ConfigController.php
@@ -0,0 +1,85 @@
+render($this->twig.'list.html.twig',[
+ "useheader"=>true,
+ "usemenu"=>false,
+ "usesidebar"=>true,
+ ]);
+ }
+
+ public function listrender($access,$category,ManagerRegistry $em): Response
+ {
+ $datas = $em->getRepository($this->entity)->findBy(["visible"=>true,"category"=>$category]);
+
+ return $this->render($this->twig.'render.html.twig',[
+ $this->data."s" => $datas,
+ ]);
+ }
+
+ public function update($access,$id,Request $request,ManagerRegistry $em): Response
+ {
+ // Initialisation de l'enregistrement
+ $data=$em->getRepository($this->entity)->find($id);
+ if(!$data->getValue())
+ $data->setValue($request->getSession()->get($data->getId()));
+
+ // Création du formulaire
+ $form = $this->createForm(Form::class,$data,array("mode"=>"update","id"=>$data->getId(),"type"=>$data->getType(),"required"=>$data->isRequired()));
+
+ // Récupération des data du formulaire
+ $form->handleRequest($request);
+
+ // Sur validation
+ if ($form->get('submit')->isClicked() && $form->isValid()) {
+ $data = $form->getData();
+ $em->getManager()->flush();
+
+ // Retour à la liste
+ return $this->redirectToRoute($this->route);
+ }
+
+ // Affichage du formulaire
+ return $this->render($this->twig.'edit.html.twig', [
+ 'useheader' => true,
+ 'usemenu' => false,
+ 'usesidebar' => true,
+ $this->data => $data,
+ 'mode' => 'update',
+ 'form' => $form->createView()
+ ]);
+ }
+
+ public function delete($access,$id,Request $request,ManagerRegistry $em): Response
+ {
+ // Récupération de l'enregistrement courant
+ $config=$em->getRepository($this->entity)->find($id);
+ if(!$config->isRequired()) {
+ $config->setValue("");
+ $em->getManager()->flush();
+ }
+ return $this->redirectToRoute($this->route);
+ }
+
+ public function logo($access): Response
+ {
+ return $this->render($this->twig.'logo.html.twig');
+ }
+}
\ No newline at end of file
diff --git a/src/Controller/CronController.php b/src/Controller/CronController.php
new file mode 100644
index 0000000..3a3848f
--- /dev/null
+++ b/src/Controller/CronController.php
@@ -0,0 +1,184 @@
+render($this->twig.'list.html.twig',[
+ "useheader"=>true,
+ "usemenu"=>false,
+ "usesidebar"=>true,
+ ]);
+ }
+
+ public function tablelist(Request $request,ManagerRegistry $em): Response
+ {
+ $query=$request->query->all();
+ $start=$query['start'];
+ $length=$query['length'];
+ $search=$query['search'];
+ $draw= $query['draw'];
+ $ordercolumn=$query['order'][0]['column'];
+ $orderdir=$query['order'][0]['dir'];
+
+ // Nombre total d'enregistrement
+ $total = $em->getManager()->createQueryBuilder()->select('COUNT(entity)')->from($this->entity,'entity')->getQuery()->getSingleScalarResult();
+
+ // Nombre d'enregistrement filtré
+ if(!$search||$search["value"]=="")
+ $totalf = $total;
+ else {
+ $totalf= $em->getManager()->createQueryBuilder()
+ ->select('COUNT(entity)')
+ ->from($this->entity,'entity')
+ ->where('entity.command LIKE :value OR entity.description LIKE :value')
+ ->setParameter("value", "%".$search["value"]."%")
+ ->getQuery()
+ ->getSingleScalarResult();
+ }
+
+ // Construction du tableau de retour
+ $output = array(
+ 'draw' => $draw,
+ 'recordsFiltered' => $totalf,
+ 'recordsTotal' => $total,
+ 'data' => array(),
+ );
+
+ // Parcours des Enregistrement
+ $qb = $em->getManager()->createQueryBuilder();
+ $qb->select('entity')->from($this->entity,'entity');
+ if($search&&$search["value"]!="") {
+ $qb ->andWhere('entity.command LIKE :value OR entity.description LIKE :value')
+ ->setParameter("value", "%".$search["value"]."%");
+ }
+
+ if($ordercolumn) {
+ switch($ordercolumn) {
+ case 1 :
+ $qb->orderBy('entity.nextexecdate',$orderdir);
+ break;
+
+ case 2 :
+ $qb->orderBy('entity.command',$orderdir);
+ break;
+ }
+ }
+
+ $datas=$qb->setFirstResult($start)->setMaxResults($length)->getQuery()->getResult();
+
+ foreach($datas as $data) {
+ // Action
+ $action = "";
+ $action.="$data->getId()))."'>";
+
+ $tmp=array();
+ array_push($tmp,$action);
+ array_push($tmp,$data->getNextexecdate()->format("d/m/Y H:i"));
+ array_push($tmp,$data->getCommand());
+ array_push($tmp,$data->getDescription());
+ array_push($tmp,$data->getStatutLabel());
+
+ array_push($output["data"],$tmp);
+ }
+
+ // Retour
+ return new JsonResponse($output);
+ }
+
+ public function update($id,Request $request,ManagerRegistry $em): Response
+ {
+ // Initialisation de l'enregistrement
+ $data=$em->getRepository($this->entity)->find($id);
+ if (!$data) throw $this->createNotFoundException('Unable to find entity.');
+
+ // Création du formulaire
+ $form = $this->createForm(Form::class,$data,array(
+ "mode"=>"update",
+ ));
+
+ // Récupération des data du formulaire
+ $form->handleRequest($request);
+
+ // Sur validation
+ if ($form->get('submit')->isClicked() && $form->isValid()) {
+ $data = $form->getData();
+ $em->getManager()->flush();
+
+ // Retour à la liste
+ return $this->redirectToRoute($this->route);
+ }
+
+ // Affichage du formulaire
+ return $this->render($this->twig.'edit.html.twig', [
+ 'useheader' => true,
+ 'usemenu' => false,
+ 'usesidebar' => true,
+ $this->data => $data,
+ 'mode' => 'update',
+ 'form' => $form->createView()
+ ]);
+ }
+
+ public function log()
+ {
+ return $this->render($this->render.'logs.html.twig', [
+ 'useheader' => true,
+ 'usesidebar' => true,
+ ]);
+ }
+
+ public function getlog(Request $request, $id)
+ {
+
+ $path = $this->getParameter('kernel.project_dir');
+ if($id=="dump")
+ $file = $path . '/var/log/' . $this->getParameter("appAlias") . '.sql';
+ else
+ $file = $path . '/var/log/'.$id.'.log';
+
+ $fs = new Filesystem();
+ if($fs->exists($file)) {
+ $response = new BinaryFileResponse($file);
+ $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT);
+ return $response;
+ }
+ else return $this->redirectToRoute($this->route."_log");
+ }
+
+
+ protected function getErrorForm($id,$form,$request,$data,$mode) {
+ if ($form->get('submit')->isClicked()&&$mode=="delete") {
+ }
+
+ if ($form->get('submit')->isClicked() && $mode=="submit") {
+ }
+
+ if ($form->get('submit')->isClicked() && !$form->isValid()) {
+ $this->get('session')->getFlashBag()->clear();
+
+ $errors = $form->getErrors();
+ foreach( $errors as $error ) {
+ $request->getSession()->getFlashBag()->add("error", $error->getMessage());
+ }
+ }
+ }
+}
diff --git a/src/Controller/CropController.php b/src/Controller/CropController.php
new file mode 100644
index 0000000..c6c95f5
--- /dev/null
+++ b/src/Controller/CropController.php
@@ -0,0 +1,244 @@
+appKernel = $appKernel;
+ }
+
+ // Etape 01 - Téléchargement de l'image
+ public function crop01($type,$reportinput): Response
+ {
+ return $this->render('Crop/crop01.html.twig',[
+ 'useheader' => false,
+ 'usesidebar' => false,
+ 'type' => $type,
+ 'reportinput' => $reportinput
+ ]);
+ }
+
+ // Etape 02 - Couper votre l'image
+ public function crop02($type,$reportinput,Request $request)
+ {
+ // Récupération de l'image à cropper
+ $file=$request->query->get('file');
+ $large_image_location = "uploads/$type/$file";
+
+ // Récupérer les tailles de l'image
+ $width = $this->getWidth($large_image_location);
+ $height = $this->getHeight($large_image_location);
+
+ // Définir le pourcentage de réduction de l'image
+ switch ($type) {
+ case "illustration":
+ $max_height=0;
+ $ratio="1:1";
+ break;
+
+ case "avatar":
+ $max_height=900;
+ $max_width=900;
+ $ratio="1:1";
+ break;
+ case "header":
+ $max_height=1600;
+ $max_width=1600;
+ $ratio="16:2";
+ break;
+ case "hero":
+ $max_height=1600;
+ $max_width=1600;
+ $ratio="16:9";
+ break;
+ case "image":
+ $max_height=1600;
+ $max_width=1600;
+ $ratio="1:1";
+ break;
+ }
+
+ if($max_height>0) {
+ $scale = $max_height/$height;
+ if(($width*$scale)>$max_width) {
+ $scale = $max_width/$width;
+ }
+ $this->resizeImage($large_image_location,$width,$height,$scale);
+ }
+ else $scale=1;
+
+ // Construction du formulaire
+ $submited=false;
+ $form = $this->createFormBuilder()
+ ->add('submit',SubmitType::class,array("label" => "Valider","attr" => array("class" => "btn btn-success")))
+ ->add('x',HiddenType::class)
+ ->add('y',HiddenType::class)
+ ->add('w',HiddenType::class)
+ ->add('h',HiddenType::class)
+ ->add('xs',HiddenType::class)
+ ->add('ys',HiddenType::class)
+ ->add('ws',HiddenType::class)
+ ->add('hs',HiddenType::class)
+ ->getForm();
+
+ // Récupération des data du formulaire
+ $form->handleRequest($request);
+
+ // Sur validation on généère la miniature croppée
+ if ($form->get('submit')->isClicked() && $form->isValid()) {
+ // Récupération des valeurs du formulaire
+ $data = $form->getData();
+ $thumb_image_location = "uploads/$type/thumb_".$file;
+ $cropped = $this->resizeThumbnailImage($thumb_image_location, $large_image_location,$data["ws"],$data["hs"],$data["xs"],$data["ys"],$scale);
+ $submited=true;
+ }
+
+ return $this->render('Crop/crop02.html.twig', [
+ 'useheader' => false,
+ 'usesidebar' => false,
+ 'form' => $form->createView(),
+ 'type' => $type,
+ 'file' => $file,
+ 'ratio' => $ratio,
+ "reportinput" => $reportinput,
+ "submited" => $submited
+ ]);
+ }
+
+ // Upload ckeditor
+ public function ckupload(Request $request) {
+ // Fichier temporaire uploadé
+ $tmpfile = $request->files->get('upload');
+ $extention = $tmpfile->getClientOriginalExtension();
+
+ // Répertoire de Destination
+ $fs = new Filesystem();
+ $rootdir = $this->appKernel->getProjectDir()."/public";
+ $fs->mkdir($rootdir."/uploads/ckeditor");
+
+ // Fichier cible
+ $targetName = uniqid().".".$extention;
+ $targetFile = $rootdir."/uploads/ckeditor/".$targetName;
+ $targetUrl = "/".$this->getParameter('appAlias')."/uploads/ckeditor/".$targetName;
+ $message = "";
+
+ move_uploaded_file($tmpfile,$targetFile);
+
+ $output["uploaded"]=1;
+ $output["fileName"]=$targetName;
+ $output["url"]=$targetUrl;
+
+ return new Response(json_encode($output));
+ }
+
+ // Calcul de la hauteur
+ protected function getHeight($image) {
+ $size = getimagesize($image);
+ $height = $size[1];
+ return $height;
+ }
+
+ // Cacul de la largeur
+ protected function getWidth($image) {
+ $size = getimagesize($image);
+ $width = $size[0];
+ return $width;
+ }
+
+ protected function resizeImage($image,$width,$height,$scale) {
+ list($imagewidth, $imageheight, $imageType) = getimagesize($image);
+ $imageType = image_type_to_mime_type($imageType);
+ $newImageWidth = ceil($width * $scale);
+ $newImageHeight = ceil($height * $scale);
+ $newImage = imagecreatetruecolor($newImageWidth,$newImageHeight);
+ switch($imageType) {
+ case "image/gif":
+ $source=imagecreatefromgif($image);
+ break;
+ case "image/pjpeg":
+ case "image/jpeg":
+ case "image/jpg":
+ $source=imagecreatefromjpeg($image);
+ break;
+ case "image/png":
+ case "image/x-png":
+ $source=imagecreatefrompng($image);
+ break;
+ }
+ imagecopyresampled($newImage,$source,0,0,0,0,$newImageWidth,$newImageHeight,$width,$height);
+
+ switch($imageType) {
+ case "image/gif":
+ imagegif($newImage,$image);
+ break;
+ case "image/pjpeg":
+ case "image/jpeg":
+ case "image/jpg":
+ imagejpeg($newImage,$image,90);
+ break;
+ case "image/png":
+ case "image/x-png":
+ imagepng($newImage,$image);
+ break;
+ }
+
+ chmod($image, 0640);
+ return $image;
+ }
+
+ protected function resizeThumbnailImage($thumb_image_name, $image, $width, $height, $start_width, $start_height, $scale){
+ $fs = new Filesystem();
+ $fs->remove($thumb_image_name);
+
+ list($imagewidth, $imageheight, $imageType) = getimagesize($image);
+ $imageType = image_type_to_mime_type($imageType);
+
+ $newImageWidth = ceil($width * $scale);
+ $newImageHeight = ceil($height * $scale);
+ $newImage = imagecreatetruecolor($newImageWidth,$newImageHeight);
+ switch($imageType) {
+ case "image/gif":
+ $source=imagecreatefromgif($image);
+ break;
+ case "image/pjpeg":
+ case "image/jpeg":
+ case "image/jpg":
+ $source=imagecreatefromjpeg($image);
+ break;
+ case "image/png":
+ case "image/x-png":
+ $source=imagecreatefrompng($image);
+ break;
+ }
+ imagecopyresampled($newImage,$source,0,0,$start_width,$start_height,$newImageWidth,$newImageHeight,$width,$height);
+ switch($imageType) {
+ case "image/gif":
+ imagegif($newImage,$thumb_image_name);
+ break;
+ case "image/pjpeg":
+ case "image/jpeg":
+ case "image/jpg":
+ imagejpeg($newImage,$thumb_image_name,90);
+ break;
+ case "image/png":
+ case "image/x-png":
+ imagepng($newImage,$thumb_image_name);
+ break;
+ }
+ chmod($thumb_image_name, 0640);
+ return $thumb_image_name;
+ }
+
+}
diff --git a/src/Controller/GroupController.php b/src/Controller/GroupController.php
new file mode 100644
index 0000000..c2fcce8
--- /dev/null
+++ b/src/Controller/GroupController.php
@@ -0,0 +1,827 @@
+render($this->twig.'list.html.twig',[
+ "useheader"=>true,
+ "usemenu"=>false,
+ "usesidebar"=>($access!="user"),
+ "access"=>$access,
+ ]);
+ }
+
+ public function tablelist($access,Request $request,ManagerRegistry $em): Response
+ {
+ $query=$request->query->all();
+ $start=$query['start'];
+ $length=$query['length'];
+ $search=$query['search'];
+ $draw= $query['draw'];
+ $ordercolumn=$query['order'][0]['column'];
+ $orderdir=$query['order'][0]['dir'];
+ $user=$this->getUser();
+
+ // Nombre total d'enregistrement
+ $qb = $em->getManager()->createQueryBuilder();
+ $qb->select('COUNT(entity)')->from($this->entity,'entity')->getQuery()->getSingleScalarResult();
+ if($access=="user") {
+ $qb ->from("App:UserGroup","usergroup")
+ ->andWhere(("entity.isworkgroup=:flag"))
+ ->andWhere("entity.id=usergroup.group")
+ ->andWhere("usergroup.user=:user")
+ ->setParameter("flag", true)
+ ->setParameter("user", $user);
+
+ }
+ $total = $qb->getQuery()->getSingleScalarResult();
+
+ // Nombre d'enregistrement filtré
+ if(!$search||$search["value"]=="")
+ $totalf = $total;
+ else {
+ $qb= $em->getManager()->createQueryBuilder();
+
+ $qb ->select('COUNT(entity)')
+ ->from($this->entity,'entity')
+ ->where('entity.label LIKE :value')
+ ->leftJoin('App:User', 'user','WITH','entity.owner = user.id AND user.username LIKE :value')
+ ->setParameter("value", "%".$search["value"]."%")
+ ->getQuery()
+ ->getSingleScalarResult();
+ if($access=="user") {
+ $qb ->from("App:UserGroup","usergroup")
+ ->andWhere(("entity.isworkgroup=:flag"))
+ ->andWhere("entity.id=usergroup.group")
+ ->andWhere("usergroup.user=:user")
+ ->setParameter("flag", true)
+ ->setParameter("user", $user);
+ }
+ $totalf= $qb->getQuery()->getSingleScalarResult();
+ }
+
+ // Construction du tableau de retour
+ $output = array(
+ 'draw' => $draw,
+ 'recordsFiltered' => $totalf,
+ 'recordsTotal' => $total,
+ 'data' => array(),
+ );
+
+ // Parcours des Enregistrement
+ $qb = $em->getManager()->createQueryBuilder();
+ $qb ->select('entity')
+ ->from($this->entity,'entity');
+ if($access=="user") {
+ $qb ->from("App:UserGroup","usergroup")
+ ->andWhere(("entity.isworkgroup=:flag"))
+ ->andWhere("entity.id=usergroup.group")
+ ->andWhere("usergroup.user=:user")
+ ->setParameter("flag", true)
+ ->setParameter("user", $user);
+ }
+
+ if($search&&$search["value"]!="") {
+ $qb ->andWhere('entity.label LIKE :value')
+ ->setParameter("value", "%".$search["value"]."%");
+ }
+
+ if($ordercolumn) {
+ switch($ordercolumn) {
+ case 1 :
+ $qb->orderBy('entity.label',$orderdir);
+ break;
+ case 2 :
+ $qb->orderBy('entity.isworkgroup',$orderdir);
+ break;
+ case 3 :
+ $qb->orderBy('entity.isopen',$orderdir);
+ break;
+ case 4 :
+ $qb->orderBy('entity.owner',$orderdir);
+ break;
+ }
+ }
+
+ $datas=$qb->setFirstResult($start)->setMaxResults($length)->getQuery()->getResult();
+
+ foreach($datas as $data) {
+ // Action
+ $action = "";
+ switch($access) {
+ case "admin":
+ if($this->canupdate($access,$data,$em,false))
+ $action.="$data->getId()])."'>";
+
+ if($this->canseemember($access,$data,$em,false))
+ $action.="$data->getId()])."'>";
+ break;
+
+ case "modo":
+ if($this->canupdate($access,$data,$em,false))
+ $action.="$data->getId()])."'>";
+
+ if($this->canseemember($access,$data,$em,false))
+ $action.="$data->getId()])."'>";
+ break;
+
+ case "user":
+ if($this->canupdate($access,$data,$em,false))
+ $action.="$data->getId()])."'>";
+
+ if($this->canseemember($access,$data,$em,false))
+ $action.="$data->getId()])."'>";
+
+
+ // On ne peut se désinscrire que si le groupe est ouvert et qu'il n'est pas lié à un groupe ldap ou sso
+ if($data->getOwner()!=$this->getUser()&&($data->isIsOpen()||$this->canupdatemember($access,$data,$em,false)))
+ $action.="$data->getId()])."'>";
+ break;
+ }
+
+ $userinfo="";
+ if($data->getOwner()) {
+ if(stripos($data->getOwner()->getAvatar(),"http")===0)
+ $userinfo.="";
+ else
+ $userinfo.="getOwner()->getAvatar()."' class='avatar'>";
+ $userinfo.="
".$data->getOwner()->getUsername();
+ }
+
+ $visitecpt=0;
+ $visitelast=null;
+ foreach($data->getUsers() as $usergroup) {
+ $visitecpt+=intval($usergroup->getVisitecpt());
+ $visitelast=($usergroup->getVisitedate()>$visitelast?$usergroup->getVisitedate():$visitelast);
+ }
+
+ $tmp=array();
+ array_push($tmp,$action);
+ array_push($tmp,$data->getLabel());
+ array_push($tmp,($data->isIsworkgroup()?"oui":"non"));
+ array_push($tmp,($data->isIsopen()?"oui":"non"));
+ array_push($tmp,$userinfo);
+ array_push($tmp,($visitelast?$visitelast->format("d/m/Y H:i")."
":"")."nb = ".$visitecpt);
+ array_push($output["data"],$tmp);
+ }
+
+ // Retour
+ return new JsonResponse($output);
+ }
+
+
+ public function submit($access,Request $request,ManagerRegistry $em): Response
+ {
+ // Initialisation de l'enregistrement
+ $data = new Entity();
+ $data->setApikey(Uuid::uuid4());
+ if($access=="user") {
+ $data->setOwner($this->getUser());
+ $data->setIsworkgroup(true);
+ }
+
+ // Controler les permissions
+ $this->cansubmit($access,$em);
+
+ // Création du formulaire
+ $form = $this->createForm(Form::class,$data,array(
+ "mode"=>"submit",
+ "appMasteridentity"=>$this->GetParameter("appMasteridentity"),
+ "access"=>$access,
+ ));
+
+ // Récupération des data du formulaire
+ $form->handleRequest($request);
+
+ // Sur validation
+ if ($form->get('submit')->isClicked() && $form->isValid()) {
+ $data = $form->getData();
+
+ // Les groupes opé ne sont pas ouvert
+ if(!$data->isIsworkgroup()) $data->setIsopen(false);
+
+ // Sauvegarde
+ $em->getManager()->persist($data);
+ $em->getManager()->flush();
+
+ // Retour à la liste
+ return $this->redirectToRoute(str_replace("_admin_","_".$access."_",$this->route));
+ }
+
+ // Affichage du formulaire
+ return $this->render($this->twig.'edit.html.twig', [
+ "useheader"=>true,
+ "usemenu"=>false,
+ "usesidebar"=>($access!="user"),
+ "mode"=>"submit",
+ "access"=>$access,
+ "form"=>$form->createView(),
+ $this->data=>$data,
+ "maxsize"=>($access=="user"?1200:null),
+ ]);
+ }
+
+ public function update($id,$access,Request $request,ManagerRegistry $em): Response
+ {
+ // Initialisation de l'enregistrement
+ $data=$em->getRepository($this->entity)->find($id);
+ if (!$data or $id<0) throw $this->createNotFoundException('Unable to find entity.');
+
+ // Controler les permissions
+ $this->canupdate($access,$data,$em);
+
+ // Création du formulaire
+ $form = $this->createForm(Form::class,$data,array(
+ "mode"=>"update",
+ "appMasteridentity"=>$this->GetParameter("appMasteridentity"),
+ "access"=>$access,
+ ));
+
+ // Récupération des data du formulaire
+ $form->handleRequest($request);
+
+ // Sur validation
+ if ($form->get('submit')->isClicked() && $form->isValid()) {
+ $data = $form->getData();
+
+ // Les groupes opé ne sont pas ouvert
+ if(!$data->isIsworkgroup()) $data->setIsopen(false);
+
+ $em->getManager()->flush();
+
+ // Retour à la liste
+ return $this->redirectToRoute(str_replace("_admin_","_".$access."_",$this->route));
+ }
+
+ // Affichage du formulaire
+ return $this->render($this->twig.'edit.html.twig', [
+ "useheader" => true,
+ "usemenu" => false,
+ "usesidebar" => ($access!="user"),
+ $this->data => $data,
+ "mode" => "update",
+ "access"=>$access,
+ "form" => $form->createView(),
+ "maxsize"=>($access=="user"?1200:null),
+ ]);
+ }
+
+ public function delete($id,$access,Request $request,ManagerRegistry $em): Response
+ {
+ // Récupération de l'enregistrement courant
+ $data=$em->getRepository($this->entity)->find($id);
+ if (!$data) throw $this->createNotFoundException('Unable to find entity.');
+
+ // Controler les permissions
+ $this->canupdate($access,$data,$em);
+
+ // Tentative de suppression
+ try{
+ $em->getManager()->remove($data);
+ $em->getManager()->flush();
+ }
+ catch (\Exception $e) {
+ $request->getSession()->getFlashBag()->add("error", $e->getMessage());
+ return $this->redirectToRoute(str_replace("_admin_","_".$access."_",$this->route)."_update",["id"=>$id]);
+ }
+
+ return $this->redirectToRoute(str_replace("_admin_","_".$access."_",$this->route));
+ }
+
+
+ public function users($id,$access,Request $request,ManagerRegistry $em)
+ {
+ // Récupération de l'enregistrement courant
+ $data=$em->getRepository($this->entity)->find($id);
+ if (!$data) throw $this->createNotFoundException('Unable to find entity.');
+
+
+ // Controler les permissions
+ $this->canseemember($access,$data,$em);
+
+ // Affichage du formulaire
+ return $this->render($this->twig.'users.html.twig', [
+ 'useheader' => true,
+ 'usemenu' => false,
+ 'usesidebar' => ($access!="user"),
+ 'access' => $access,
+ $this->data => $data,
+ ]);
+ }
+
+ public function usersnotin($id,$access,Request $request,ManagerRegistry $em)
+ {
+ // Récupération de l'enregistrement courant
+ $group=$em->getRepository($this->entity)->find($id);
+ if (!$group) throw $this->createNotFoundException('Unable to find entity.');
+
+ // Controler les permissions
+ $this->canseemember($access,$group,$em);
+
+ $sub = $em->getManager()->createQueryBuilder();
+ $sub->select("usergroup");
+ $sub->from("App:UserGroup","usergroup");
+ $sub->andWhere('usergroup.user = user.id');
+ $sub->andWhere('usergroup.group = :groupid');
+
+ $query=$request->query->all();
+ $start=$query['start'];
+ $length=$query['length'];
+ $search=$query['search'];
+ $draw= $query['draw'];
+ $ordercolumn=$query['order'][0]['column'];
+ $orderdir=$query['order'][0]['dir'];
+
+ // Nombre total d'enregistrement
+ $qb = $em->getManager()->createQueryBuilder();
+ switch($access) {
+ case "admin":
+ $qb->select('COUNT(user)')
+ ->from('App:User','user')
+ ->where($qb->expr()->not($qb->expr()->exists($sub->getDQL())))
+ ->setParameter("groupid",$id);
+ break;
+
+ case "modo":
+ $usermodo=$this->getUser()->getId();
+ $qb->select('COUNT(user)')
+ ->from('App:User','user')
+ ->from('App:UserModo','usermodo')
+ ->where($qb->expr()->not($qb->expr()->exists($sub->getDQL())))
+ ->andWhere("usermodo.niveau01 = user.niveau01")
+ ->andWhere("usermodo.user = :userid")
+ ->setParameter("userid", $usermodo)
+ ->setParameter("groupid",$id);
+ break;
+
+ case "user":
+ $niveau01=$this->getUser()->getNiveau01();
+ $niveau02=$this->getUser()->getNiveau02();
+
+ $qb->select('COUNT(user)')
+ ->from('App:User','user')
+ ->where($qb->expr()->not($qb->expr()->exists($sub->getDQL())))
+ ->setParameter("groupid",$id);
+
+ switch($request->getSession()->get("scopeannu")) {
+ case "SAME_NIVEAU01":
+ $qb->andWhere("user.niveau01 = :niveau01")->setParameter("niveau01",$niveau01);
+ break;
+
+ case "SAME_NIVEAU02":
+ $qb->andWhere("user.niveau02 = :niveau02")->setParameter("niveau02",$niveau02);
+ break;
+ }
+ break;
+ }
+ $total=$qb->getQuery()->getSingleScalarResult();
+
+ // Nombre d'enregistrement filtré
+ if($search["value"]=="")
+ $totalf = $total;
+ else {
+ switch($access) {
+ case "admin":
+ $totalf= $em->getManager()->createQueryBuilder()
+ ->select('COUNT(user)')
+ ->from('App:User','user')
+ ->where('user.username LIKE :value OR user.email LIKE :value')
+ ->andWhere($qb->expr()->not($qb->expr()->exists($sub->getDQL())))
+ ->setParameter("value", "%".$search["value"]."%")
+ ->setParameter("groupid",$id)
+ ->getQuery()
+ ->getSingleScalarResult();
+ break;
+
+ case "modo":
+ $totalf = $em->getManager()->createQueryBuilder()
+ ->select('COUNT(user)')
+ ->from('App:User','user')
+ ->from('App:UserModo','usermodo')
+ ->where('user.username LIKE :value OR user.email LIKE :value')
+ ->andWhere($qb->expr()->not($qb->expr()->exists($sub->getDQL())))
+ ->andWhere("usermodo.niveau01 = user.niveau01")
+ ->andWhere("usermodo.user = :userid")
+ ->setParameter("userid", $usermodo)
+ ->setParameter("value", "%".$search["value"]."%")
+ ->setParameter("groupid",$id)
+ ->getQuery()
+ ->getSingleScalarResult();
+ break;
+
+ case "user":
+ $qb = $em->getManager()->createQueryBuilder()
+ ->select('COUNT(user)')
+ ->from('App:User','user')
+ ->where('user.username LIKE :value OR user.email LIKE :value')
+ ->andWhere($qb->expr()->not($qb->expr()->exists($sub->getDQL())))
+ ->setParameter("value", "%".$search["value"]."%")
+ ->setParameter("groupid",$id);
+
+ switch($request->getSession()->get("scopeannu")) {
+ case "SAME_NIVEAU01":
+ $qb->andWhere("user.niveau01 = :niveau01")->setParameter("niveau01",$niveau01);
+ break;
+
+ case "SAME_NIVEAU02":
+ $qb->andWhere("user.niveau02 = :niveau02")->setParameter("niveau02",$niveau02);
+ break;
+ }
+
+ $totalf=$qb->getQuery()->getSingleScalarResult();
+ break;
+ }
+ }
+
+ // Construction du tableau de retour
+ $output = array(
+ 'draw' => $draw,
+ 'recordsFiltered' => $totalf,
+ 'recordsTotal' => $total,
+ 'data' => array(),
+ );
+
+ // Parcours des Enregistrement
+ $qb = $em->getManager()->createQueryBuilder();
+ $qb->select('user')->from("App:User",'user');
+
+ switch($access) {
+ case "admin":
+ $qb->where($qb->expr()->not($qb->expr()->exists($sub->getDQL())));
+ break;
+
+ case "modo":
+ $qb->from('App:UserModo','usermodo')
+ ->where($qb->expr()->not($qb->expr()->exists($sub->getDQL())))
+ ->andWhere("usermodo.niveau01 = user.niveau01")
+ ->andWhere("usermodo.user = :userid")
+ ->setParameter("userid", $usermodo);
+ break;
+
+ case "user":
+ $qb->where($qb->expr()->not($qb->expr()->exists($sub->getDQL())));
+ switch($request->getSession()->get("scopeannu")) {
+ case "SAME_NIVEAU01":
+ $qb->andWhere("user.niveau01 = :niveau01")->setParameter("niveau01",$niveau01);
+ break;
+
+ case "SAME_NIVEAU02":
+ $qb->andWhere("user.niveau02 = :niveau02")->setParameter("niveau02",$niveau02);
+ break;
+ }
+ break;
+ }
+
+ if($search["value"]!="") {
+ $qb ->andWhere('user.username LIKE :value OR user.email LIKE :value')
+ ->setParameter("value", "%".$search["value"]."%");
+ }
+ $qb->setParameter("groupid",$id);
+ switch($ordercolumn) {
+ case 2 :
+ $qb->orderBy('user.username',$orderdir);
+ break;
+
+ case 3 :
+ $qb->orderBy('user.email',$orderdir);
+ break;
+ }
+
+ $datas=$qb->setFirstResult($start)->setMaxResults($length)->getQuery()->getResult();
+ $canupdatemember=$this->canupdatemember($access,$group,$em,false);
+
+ foreach($datas as $data) {
+ // Action
+ $action = "";
+ if($canupdatemember)
+ $action.="";
+
+ // Avatar
+ if(stripos($data->getAvatar(),"http")===0)
+ $avatar="";
+ else
+ $avatar="getAvatar()."' class='avatar'>";
+
+
+ array_push($output["data"],array("DT_RowId"=>"user".$data->getId(),$action,$avatar,$data->getUsername(),$data->getEmail(),"",""));
+ }
+
+ // Retour
+ return new JsonResponse($output);
+ }
+
+
+ public function usersin($id,$access,Request $request,ManagerRegistry $em)
+ {
+ // Récupération de l'enregistrement courant
+ $group=$em->getRepository($this->entity)->find($id);
+ if (!$group) throw $this->createNotFoundException('Unable to find entity.');
+
+ // Controler les permissions
+ $this->canseemember($access,$group,$em);
+
+ $sub = $em->getManager()->createQueryBuilder();
+ $sub->select("usergroup");
+ $sub->from("App:UserGroup","usergroup");
+ $sub->andWhere('usergroup.user = user.id');
+ $sub->andWhere('usergroup.group = :groupid');
+
+ $query=$request->query->all();
+ $start=$query['start'];
+ $length=$query['length'];
+ $search=$query['search'];
+ $draw= $query['draw'];
+ $ordercolumn=$query['order'][0]['column'];
+ $orderdir=$query['order'][0]['dir'];
+
+ // Nombre total d'enregistrement
+ $qb = $em->getManager()->createQueryBuilder();
+ if($access=="admin"||$access=="user")
+ $qb->select('COUNT(user)')
+ ->from('App:User','user')
+ ->where($qb->expr()->exists($sub->getDQL()))
+ ->setParameter("groupid",$id);
+ else {
+ $usermodo=$this->getUser()->getId();
+ $qb->select('COUNT(user)')
+ ->from('App:User','user')
+ ->from('App:UserModo','usermodo')
+ ->where($qb->expr()->exists($sub->getDQL()))
+ ->andWhere("usermodo.niveau01 = user.niveau01")
+ ->andWhere("usermodo.user = :userid")
+ ->setParameter("userid", $usermodo)
+ ->setParameter("groupid",$id);
+ }
+ $total=$qb->getQuery()->getSingleScalarResult();
+
+ // Nombre d'enregistrement filtré
+ if($search["value"]=="")
+ $totalf = $total;
+ else {
+ if($access=="admin"||$access=="user")
+ $totalf= $em->getManager()->createQueryBuilder()
+ ->select('COUNT(user)')
+ ->from('App:User','user')
+ ->where('user.username LIKE :value OR user.email LIKE :value')
+ ->andWhere($qb->expr()->exists($sub->getDQL()))
+ ->setParameter("value", "%".$search["value"]."%")
+ ->setParameter("groupid",$id)
+ ->getQuery()
+ ->getSingleScalarResult();
+ else
+ $totalf= $em->getManager()->createQueryBuilder()
+ ->select('COUNT(user)')
+ ->from('App:User','user')
+ ->from('App:UserModo','usermodo')
+ ->where('user.username LIKE :value OR user.email LIKE :value')
+ ->andWhere($qb->expr()->exists($sub->getDQL()))
+ ->andWhere("usermodo.niveau01 = user.niveau01")
+ ->andWhere("usermodo.user = :userid")
+ ->setParameter("userid", $usermodo)
+ ->setParameter("value", "%".$search["value"]."%")
+ ->setParameter("groupid",$id)
+ ->getQuery()
+ ->getSingleScalarResult();
+
+ }
+
+ // Construction du tableau de retour
+ $output = array(
+ 'draw' => $draw,
+ 'recordsFiltered' => $totalf,
+ 'recordsTotal' => $total,
+ 'data' => array(),
+ );
+
+ // Parcours des Enregistrement
+ $qb = $em->getManager()->createQueryBuilder();
+ $qb->select('user')->from("App:User",'user');
+
+ if($access=="admin"||$access=="user")
+ $qb->where($qb->expr()->exists($sub->getDQL()));
+ else
+ $qb->from('App:UserModo','usermodo')
+ ->where($qb->expr()->exists($sub->getDQL()))
+ ->andWhere("usermodo.niveau01 = user.niveau01")
+ ->andWhere("usermodo.user = :userid")
+ ->setParameter("userid", $usermodo);
+
+ if($search["value"]!="") {
+ $qb ->andWhere('user.username LIKE :value OR user.email LIKE :value')
+ ->setParameter("value", "%".$search["value"]."%");
+ }
+ $qb->setParameter("groupid",$id);
+ switch($ordercolumn) {
+ case 2 :
+ $qb->orderBy('user.username',$orderdir);
+ break;
+
+ case 3 :
+ $qb->orderBy('user.email',$orderdir);
+ break;
+ }
+
+ $datas=$qb->setFirstResult($start)->setMaxResults($length)->getQuery()->getResult();
+
+ foreach($datas as $data) {
+ // Propriétaire
+ $usergroup=$em->getRepository("App\Entity\UserGroup")->findOneBy(["user"=>$data->getId(),"group"=>$id]);
+ $fgproprio=($usergroup->getUser()==$group->getOwner());
+ $fgme=($usergroup->getUser()==$this->getUser()&&$access!="admin");
+
+ // Action
+ $action = "";
+ if($this->canupdatemember($access,$group,$em,false)&&!$fgproprio&&!$fgme)
+ $action.="";
+
+ // Avatar
+ if(stripos($data->getAvatar(),"http")===0)
+ $avatar="";
+ else
+ $avatar="getAvatar()."' class='avatar'>";
+
+ // Flag manager
+ $rolegroup="";
+ if($fgproprio) $rolegroup="Propriétaire du groupe";
+ elseif($this->canupdatemember($access,$group,$em,false)&&!$fgme) {
+ $selectuser=($usergroup->getRolegroup()==0?"selected='selected'":"");
+ $selectwritter=($usergroup->getRolegroup()==50?"selected='selected'":"");
+ $selectmanager=($usergroup->getRolegroup()==90?"selected='selected'":"");
+
+ $rolegroup='';
+ }
+ else $rolegroup=($usergroup->getRolegroup()==0?"Utilisateur":($usergroup->getRolegroup()==50?"Collaborateur":"Gestionnaire"));
+
+ $tmp=array("DT_RowId"=>"user".$data->getId(),$action,$avatar,$data->getUsername(),$data->getEmail(),$rolegroup);
+ array_push($output["data"],$tmp);
+ }
+
+ // Retour
+ return new JsonResponse($output);
+ }
+
+ public function useradd($groupid,$userid,$access,Request $request,ManagerRegistry $em)
+ {
+ // Récupération de l'enregistrement courant
+ $group=$em->getRepository($this->entity)->find($groupid);
+ if (!$group) throw $this->createNotFoundException('Unable to find entity.');
+
+ $user=$em->getRepository("App\Entity\User")->find($userid);
+ if (!$user) throw $this->createNotFoundException('Unable to find entity.');
+
+ $output=array();
+ $this->canupdatemember($access,$group,$em,true);
+
+ $usergroup = $em->getRepository("App\Entity\UserGroup")->findOneBy(array("user"=>$user,"group"=>$group));
+ if($usergroup) return new JsonResponse($output);
+
+ $usergroup=new UserGroup();
+ $usergroup->setUser($user);
+ $usergroup->setGroup($group);
+ $usergroup->setApikey(Uuid::uuid4());
+ $usergroup->setRolegroup(0);
+ $em->getManager()->persist($usergroup);
+ $em->getManager()->flush();
+
+ // Retour
+ return new JsonResponse($output);
+ }
+
+ public function userdel($groupid,$userid,$access,Request $request,ManagerRegistry $em)
+ {
+ // Récupération de l'enregistrement courant
+ $group=$em->getRepository($this->entity)->find($groupid);
+ if (!$group) throw $this->createNotFoundException('Unable to find entity.');
+
+ $user=$em->getRepository("App\Entity\User")->find($userid);
+ if (!$user) throw $this->createNotFoundException('Unable to find entity.');
+
+ $output=array();
+ $this->canupdatemember($access,$group,$em,true);
+ if($user==$group->getOwner()) throw $this->createAccessDeniedException('Permission denied');
+
+ $usergroup = $em->getRepository("App\Entity\UserGroup")->findOneBy(array("user"=>$user,"group"=>$group));
+ if($usergroup) {
+ $em->getManager()->remove($usergroup);
+ $em->getManager()->flush();
+ }
+
+ // Retour
+ return new JsonResponse($output);
+ }
+
+ public function userchangerole($groupid,$userid,$roleid,$access,Request $request,ManagerRegistry $em)
+ {
+ // Récupération de l'enregistrement courant
+ $group=$em->getRepository($this->entity)->find($groupid);
+ if (!$group) throw $this->createNotFoundException('Unable to find entity.');
+
+ $user=$em->getRepository("App\Entity\User")->find($userid);
+ if (!$user) throw $this->createNotFoundException('Unable to find entity.');
+
+ $output=array();
+ $this->canupdatemember($access,$group,$em,true);
+ if($user==$group->getOwner()) throw $this->createAccessDeniedException('Permission denied');
+
+
+ $usergroup = $em->getRepository("App\Entity\UserGroup")->findOneBy(array("user"=>$user,"group"=>$group));
+ if($usergroup) {
+ $usergroup->setRolegroup($roleid);
+ $em->getManager()->persist($usergroup);
+ $em->getManager()->flush();
+ }
+
+ // Retour
+ return new JsonResponse($output);
+ }
+
+ public function userout($id,$access,Request $request,ManagerRegistry $em)
+ {
+ // Récupération de l'enregistrement courant
+ $group=$em->getRepository($this->entity)->find($id);
+ if (!$group) throw $this->createNotFoundException('Unable to find entity.');
+
+ // On ne peut se désinscrire que si le groupe est ouvert et qu'il n'est pas lié à un groupe ldap ou sso
+ if($group->getOwner()!=$this->getUser()&&($group->isIsOpen()||$this->canupdatemember($access,$group,$em,false))) {
+ $usergroup = $em->getRepository("App\Entity\UserGroup")->findOneBy(array("user"=>$this->getUser(),"group"=>$group));
+ if($usergroup) {
+ $em->getManager()->remove($usergroup);
+ $em->getManager()->flush();
+ }
+ }
+
+ return $this->redirectToRoute(str_replace("_admin_","_".$access."_",$this->route));
+ }
+
+ private function cansubmit($access,$em) {
+ switch($access) {
+ case "admin" : return true; break;
+ case "user" : return true; break;
+ }
+ throw $this->createAccessDeniedException('Permission denied');
+ }
+
+
+ private function canupdate($access,$entity,$em,$fgblock=true) {
+ $toreturn=false;
+ switch($access) {
+ case "admin" : $toreturn=($entity->getId()>0); break;
+ case "user":
+ if(!$entity->isIsworkgroup()||$entity->getOwner()!=$this->getUser()) $toreturn=false;
+ else $toreturn=true;
+ break;
+ }
+ if($fgblock&&!$toreturn) throw $this->createAccessDeniedException('Permission denied');
+ return $toreturn;
+ }
+
+ private function canseemember($access,$entity,$em,$fgblock=true) {
+ switch($access) {
+ case "admin" : $toreturn=($entity->getId()>0); break;
+ case "modo" : $toreturn=($entity->getId()>0); break;
+ case "user":
+ $usergroup=$em->getRepository("App\Entity\UserGroup")->findOneBy(["user"=>$this->getUser(),"group"=>$entity]);
+ if(!$usergroup||!$entity->isIsworkgroup()||$entity->getId()<0) $toreturn=false;
+ else $toreturn=true;
+ break;
+ }
+ if($fgblock&&!$toreturn) throw $this->createAccessDeniedException('Permission denied');
+ return $toreturn;
+ }
+
+
+ private function canupdatemember($access,$entity,$em,$fgblock=true) {
+ $toreturn=false;
+ switch($access) {
+ case "admin" : $toreturn=($entity->getId()>0&&!$entity->getLdapfilter()); break;
+ case "modo" : $toreturn=($entity->getId()>0); break;
+ case "user":
+ $usergroup=$em->getRepository("App\Entity\UserGroup")->findOneBy(["user"=>$this->getUser(),"group"=>$entity]);
+ if(!$usergroup||!$entity->isIsworkgroup()||$entity->getId()<0) $toreturn=false;
+ elseif($usergroup->getRolegroup()<90) $toreturn=false;
+ else $toreturn=true;
+ break;
+ }
+ if($fgblock&&!$toreturn) throw $this->createAccessDeniedException('Permission denied');
+ return $toreturn;
+ }
+}
\ No newline at end of file
diff --git a/src/Controller/HomeController.php b/src/Controller/HomeController.php
new file mode 100644
index 0000000..e648f1a
--- /dev/null
+++ b/src/Controller/HomeController.php
@@ -0,0 +1,81 @@
+getSession()->get("fgforceconnect"))
+ return $this->redirectToRoute("app_user_home");
+
+ return $this->render('Home/home.html.twig',[
+ "useheader"=>true,
+ "usemenu"=>true,
+ "usesidebar"=>false,
+ "maxsize"=>1000,
+ ]);
+ }
+
+ public function homeuser($access): Response
+ {
+ return $this->render('Home/home.html.twig',[
+ "useheader"=>true,
+ "usemenu"=>false,
+ "usesidebar"=>false,
+ "maxsize"=>1000,
+ ]);
+ }
+
+ public function homeadmin($access): Response
+ {
+ return $this->redirectToRoute("app_admin_config");
+ }
+
+
+ public function homemodo($access): Response
+ {
+ return $this->redirectToRoute("app_modo_niveau02");
+ }
+
+ public function docrest(): Response
+ {
+ return $this->render('Home/docrest.html.twig',[
+ "useheader"=>true,
+ "usemenu"=>false,
+ "usesidebar"=>true,
+ ]);
+ }
+
+
+ public function upload($access,Request $request): Response
+ {
+ // Fichier temporaire uploadé
+ $tmpfile = $request->files->get('upload');
+ $extention = $tmpfile->getClientOriginalExtension();
+
+ // Répertoire de Destination
+ $fs = new Filesystem();
+ $rootdir = $this->getParameter('kernel.project_dir') . '/public';
+ $fs->mkdir($rootdir."/uploads/ckeditor");
+
+ // Fichier cible
+ $targetName = uniqid().".".$extention;
+ $targetFile = $rootdir."/uploads/ckeditor/".$targetName;
+ $targetUrl = $this->getParameter('appAlias')."uploads/ckeditor/".$targetName;
+ $message = "";
+
+ move_uploaded_file($tmpfile,$targetFile);
+
+ $output["uploaded"]=1;
+ $output["fileName"]=$targetName;
+ $output["url"]=$targetUrl;
+
+ return new Response(json_encode($output));
+ }
+
+}
\ No newline at end of file
diff --git a/src/Controller/Niveau01Controller.php b/src/Controller/Niveau01Controller.php
new file mode 100644
index 0000000..45202c2
--- /dev/null
+++ b/src/Controller/Niveau01Controller.php
@@ -0,0 +1,198 @@
+render($this->twig.'list.html.twig',[
+ "useheader"=>true,
+ "usemenu"=>false,
+ "usesidebar"=>true,
+ ]);
+ }
+
+ public function tablelist(Request $request,ManagerRegistry $em): Response
+ {
+ $query=$request->query->all();
+ $start=$query['start'];
+ $length=$query['length'];
+ $search=$query['search'];
+ $draw= $query['draw'];
+ $ordercolumn=$query['order'][0]['column'];
+ $orderdir=$query['order'][0]['dir'];
+
+ // Nombre total d'enregistrement
+ $total = $em->getManager()->createQueryBuilder()->select('COUNT(entity)')->from($this->entity,'entity')->getQuery()->getSingleScalarResult();
+
+ // Nombre d'enregistrement filtré
+ if(!$search||$search["value"]=="")
+ $totalf = $total;
+ else {
+ $totalf= $em->getManager()->createQueryBuilder()
+ ->select('COUNT(entity)')
+ ->from($this->entity,'entity')
+ ->where('entity.label LIKE :value')
+ ->setParameter("value", "%".$search["value"]."%")
+ ->getQuery()
+ ->getSingleScalarResult();
+ }
+
+ // Construction du tableau de retour
+ $output = array(
+ 'draw' => $draw,
+ 'recordsFiltered' => $totalf,
+ 'recordsTotal' => $total,
+ 'data' => array(),
+ );
+
+ // Parcours des Enregistrement
+ $qb = $em->getManager()->createQueryBuilder();
+ $qb->select('entity')->from($this->entity,'entity');
+ if($search&&$search["value"]!="") {
+ $qb ->andWhere('entity.label LIKE :value')
+ ->setParameter("value", "%".$search["value"]."%");
+ }
+
+ if($ordercolumn) {
+ switch($ordercolumn) {
+ case 1 :
+ $qb->orderBy('entity.label',$orderdir);
+ break;
+ }
+ }
+
+ $datas=$qb->setFirstResult($start)->setMaxResults($length)->getQuery()->getResult();
+
+ foreach($datas as $data) {
+ // Action
+ $action = "";
+ $action.="$data->getId()))."'>";
+
+ $tmp=array();
+ array_push($tmp,$action);
+ array_push($tmp,$data->getLabel());
+
+ if($this->getParameter("appMasteridentity")=="LDAP"||$this->getParameter("appSynchro")=="LDAP2NINE") array_push($tmp,$data->getLdapfilter());
+ if($this->getParameter("appMasteridentity")=="SSO") array_push($tmp,$data->getAttributes());
+
+ array_push($output["data"],$tmp);
+ }
+
+ // Retour
+ return new JsonResponse($output);
+ }
+
+
+ public function submit(Request $request,ManagerRegistry $em): Response
+ {
+ // Initialisation de l'enregistrement
+ $data = new Entity();
+ $data->setApikey(Uuid::uuid4());
+
+ // Création du formulaire
+ $form = $this->createForm(Form::class,$data,array(
+ "mode"=>"submit",
+ "appMasteridentity"=>$this->GetParameter("appMasteridentity"),
+ "appSynchro"=>$this->GetParameter("appSynchro"),
+ "appNiveau01label"=>$this->GetParameter("appNiveau01label"),
+ ));
+
+ // Récupération des data du formulaire
+ $form->handleRequest($request);
+
+ // Sur validation
+ if ($form->get('submit')->isClicked() && $form->isValid()) {
+ $data = $form->getData();
+
+ // Sauvegarde
+ $em->getManager()->persist($data);
+ $em->getManager()->flush();
+
+ // Retour à la liste
+ return $this->redirectToRoute($this->route);
+ }
+
+ // Affichage du formulaire
+ return $this->render($this->twig.'edit.html.twig', [
+ "useheader"=>true,
+ "usemenu"=>false,
+ "usesidebar"=>true,
+ "mode"=>"submit",
+ "form"=>$form->createView(),
+ $this->data=>$data,
+ ]);
+ }
+
+ public function update($id,Request $request,ManagerRegistry $em): Response
+ {
+ // Initialisation de l'enregistrement
+ $data=$em->getRepository($this->entity)->find($id);
+ if (!$data) throw $this->createNotFoundException('Unable to find entity.');
+
+ // Création du formulaire
+ $form = $this->createForm(Form::class,$data,array(
+ "mode"=>"update",
+ "appMasteridentity"=>$this->GetParameter("appMasteridentity"),
+ "appSynchro"=>$this->GetParameter("appSynchro"),
+ "appNiveau01label"=>$this->GetParameter("appNiveau01label"),
+ ));
+
+ // Récupération des data du formulaire
+ $form->handleRequest($request);
+
+ // Sur validation
+ if ($form->get('submit')->isClicked() && $form->isValid()) {
+ $data = $form->getData();
+ $em->getManager()->flush();
+
+ // Retour à la liste
+ return $this->redirectToRoute($this->route);
+ }
+
+ // Affichage du formulaire
+ return $this->render($this->twig.'edit.html.twig', [
+ 'useheader' => true,
+ 'usemenu' => false,
+ 'usesidebar' => true,
+ $this->data => $data,
+ 'mode' => 'update',
+ 'form' => $form->createView()
+ ]);
+ }
+
+ public function delete($id,Request $request,ManagerRegistry $em): Response
+ {
+ // Récupération de l'enregistrement courant
+ $data=$em->getRepository($this->entity)->find($id);
+ if (!$data) throw $this->createNotFoundException('Unable to find entity.');
+
+ // Tentative de suppression
+ try{
+ $em->getManager()->remove($data);
+ $em->getManager()->flush();
+ }
+ catch (\Exception $e) {
+ $request->getSession()->getFlashBag()->add("error", $e->getMessage());
+ return $this->redirectToRoute($this->route."_update",["id"=>$id]);
+ }
+
+ return $this->redirectToRoute($this->route);
+ }
+}
\ No newline at end of file
diff --git a/src/Controller/Niveau02Controller.php b/src/Controller/Niveau02Controller.php
new file mode 100644
index 0000000..84ff5d1
--- /dev/null
+++ b/src/Controller/Niveau02Controller.php
@@ -0,0 +1,326 @@
+render($this->twig.'list.html.twig',[
+ "useheader"=>true,
+ "usemenu"=>false,
+ "usesidebar"=>true,
+ "access"=>$access,
+ ]);
+ }
+
+ public function tablelist($access,Request $request,ManagerRegistry $em): Response
+ {
+ $query=$request->query->all();
+ $start=$query['start'];
+ $length=$query['length'];
+ $search=$query['search'];
+ $draw= $query['draw'];
+ $ordercolumn=$query['order'][0]['column'];
+ $orderdir=$query['order'][0]['dir'];
+
+ // Nombre total d'enregistrement
+ switch($access) {
+ case "admin":
+ $total = $em->getManager()->createQueryBuilder()->select('COUNT(entity)')->from($this->entity,'entity')->getQuery()->getSingleScalarResult();
+ break;
+
+ case "modo":
+ $total = $em->getManager()->createQueryBuilder()
+ ->select('COUNT(entity)')
+ ->from($this->entity,'entity')
+ ->from("App\Entity\UserModo",'usermodo')
+ ->where("usermodo.niveau01 = entity.niveau01")
+ ->andWhere("usermodo.user = :user")
+ ->setParameter("user", $this->getUser())
+ ->getQuery()->getSingleScalarResult();
+ break;
+ }
+
+
+
+ // Nombre d'enregistrement filtré
+ if(!$search||$search["value"]=="")
+ $totalf = $total;
+ else {
+ switch($access) {
+ case "admin":
+ $totalf= $em->getManager()->createQueryBuilder()
+ ->select('COUNT(entity)')
+ ->from($this->entity,'entity')
+ ->from("App\Entity\Niveau01",'niveau01')
+ ->where('entity.niveau01=niveau01.id')
+ ->andwhere('entity.label LIKE :value OR niveau01.label LIKE :value')
+ ->setParameter("value", "%".$search["value"]."%")
+ ->getQuery()
+ ->getSingleScalarResult();
+ break;
+
+ case "modo":
+ $totalf= $em->getManager()->createQueryBuilder()
+ ->select('COUNT(entity)')
+ ->from($this->entity,'entity')
+ ->from("App\Entity\Niveau01",'niveau01')
+ ->from("App\Entity\UserModo",'usermodo')
+ ->where('entity.niveau01=niveau01.id')
+ ->andwhere('entity.label LIKE :value OR niveau01.label LIKE :value')
+ ->andWhere("usermodo.niveau01 = entity.niveau01")
+ ->andWhere("usermodo.user = :user")
+ ->setParameter("value", "%".$search["value"]."%")
+ ->setParameter("user", $this->getUser())
+ ->getQuery()
+ ->getSingleScalarResult();
+ break;
+ }
+ }
+
+ // Construction du tableau de retour
+ $output = array(
+ 'draw' => $draw,
+ 'recordsFiltered' => $totalf,
+ 'recordsTotal' => $total,
+ 'data' => array(),
+ );
+
+ // Parcours des Enregistrement
+ $qb = $em->getManager()->createQueryBuilder();
+ switch($access) {
+ case "admin":
+ $qb->select('entity')
+ ->from($this->entity,'entity')
+ ->from("App:Niveau01",'niveau01')
+ ->where('entity.niveau01=niveau01.id');
+ break;
+
+ case "modo":
+ $qb->select('entity')
+ ->from($this->entity,'entity')
+ ->from("App:Niveau01",'niveau01')
+ ->from("App\Entity\UserModo",'usermodo')
+ ->where('entity.niveau01=niveau01.id')
+ ->andWhere("usermodo.niveau01 = entity.niveau01")
+ ->andWhere("usermodo.user = :user")
+ ->setParameter("user", $this->getUser());
+ break;
+ }
+
+ if($search&&$search["value"]!="") {
+ $qb ->andwhere('entity.label LIKE :value OR niveau01.label LIKE :value')
+ ->setParameter("value", "%".$search["value"]."%");
+ }
+
+ if($ordercolumn) {
+ switch($ordercolumn) {
+ case 1 :
+ $qb->orderBy('niveau01.label',$orderdir);
+ break;
+
+ case 2 :
+ $qb->orderBy('entity.label',$orderdir);
+ break;
+ }
+ }
+
+ $datas=$qb->setFirstResult($start)->setMaxResults($length)->getQuery()->getResult();
+
+ foreach($datas as $data) {
+ // Action
+ $action = "";
+ switch($access) {
+ case "admin":
+ $action.="$data->getId()))."'>";
+ break;
+ case "modo":
+ $action.="$data->getId()))."'>";
+ break;
+ }
+
+ $tmp=array();
+ array_push($tmp,$action);
+ array_push($tmp,$data->getNiveau01()->getLabel());
+ array_push($tmp,$data->getLabel());
+
+ array_push($output["data"],$tmp);
+ }
+
+ // Retour
+ return new JsonResponse($output);
+ }
+
+ public function selectlist(Request $request,ManagerRegistry $em): Response
+ {
+ $output=array();
+ $page_limit=$request->query->get('page_limit');
+ $q=$request->query->get('q');
+ $niveau01id=$request->get('niveau01');
+
+ $qb = $em->getManager()->createQueryBuilder();
+ $qb->select('entity')
+ ->from($this->entity,'entity')
+ ->where('entity.label LIKE :value')
+ ->andwhere('entity.niveau01=:niveau01')
+ ->setParameter("value", "%".$q."%")
+ ->setParameter("niveau01", $niveau01id)
+ ->orderBy('entity.label');
+
+ $datas=$qb->setFirstResult(0)->setMaxResults($page_limit)->getQuery()->getResult();
+ foreach($datas as $data) {
+ array_push($output,array("id"=>$data->getId(),"text"=>$data->getLabel()));
+ }
+
+ $ret_string["results"]=$output;
+ $response = new Response(json_encode($ret_string));
+ $response->headers->set('Content-Type', 'application/json');
+ return $response;
+ }
+
+ public function submit($access,Request $request,ManagerRegistry $em): Response
+ {
+ // Initialisation de l'enregistrement
+ $data = new Entity();
+ $data->setApikey(Uuid::uuid4());
+
+ // Controler les permissions
+ $this->cansubmit($access,$em);
+
+ // Création du formulaire
+ $form = $this->createForm(Form::class,$data,array(
+ "mode"=>"submit",
+ "access"=>$access,
+ "userid"=>$this->getUser()->getId(),
+ "appMasteridentity"=>$this->GetParameter("appMasteridentity"),
+ "appNiveau01label"=>$this->GetParameter("appNiveau01label"),
+ "appNiveau02label"=>$this->GetParameter("appNiveau02label"),
+ ));
+
+ // Récupération des data du formulaire
+ $form->handleRequest($request);
+
+ // Sur validation
+ if ($form->get('submit')->isClicked() && $form->isValid()) {
+ $data = $form->getData();
+
+ // Sauvegarde
+ $em->getManager()->persist($data);
+ $em->getManager()->flush();
+
+ // Retour à la liste
+ return $this->redirectToRoute(str_replace("_admin_","_".$access."_",$this->route));
+ }
+
+ // Affichage du formulaire
+ return $this->render($this->twig.'edit.html.twig', [
+ "useheader"=>true,
+ "usemenu"=>false,
+ "usesidebar"=>true,
+ "mode"=>"submit",
+ "access"=>$access,
+ "form"=>$form->createView(),
+ $this->data=>$data,
+ ]);
+ }
+
+ public function update($id,$access,Request $request,ManagerRegistry $em): Response
+ {
+ // Initialisation de l'enregistrement
+ $data=$em->getRepository($this->entity)->find($id);
+ if (!$data) throw $this->createNotFoundException('Unable to find entity.');
+
+ // Controler les permissions
+ $this->canupdate($access,$data,$em);
+
+ // Création du formulaire
+ $form = $this->createForm(Form::class,$data,array(
+ "mode"=>"update",
+ "appMasteridentity"=>$this->GetParameter("appMasteridentity"),
+ "appNiveau01label"=>$this->GetParameter("appNiveau01label"),
+ "appNiveau02label"=>$this->GetParameter("appNiveau02label"),
+ ));
+
+ // Récupération des data du formulaire
+ $form->handleRequest($request);
+
+ // Sur validation
+ if ($form->get('submit')->isClicked() && $form->isValid()) {
+ $data = $form->getData();
+ $em->getManager()->flush();
+
+ // Retour à la liste
+ return $this->redirectToRoute(str_replace("_admin_","_".$access."_",$this->route));
+ }
+
+ // Affichage du formulaire
+ return $this->render($this->twig.'edit.html.twig', [
+ 'useheader' => true,
+ 'usemenu' => false,
+ 'usesidebar' => true,
+ $this->data => $data,
+ 'mode' => 'update',
+ 'access' => $access,
+ 'form' => $form->createView()
+ ]);
+ }
+
+ public function delete($id,$access,Request $request,ManagerRegistry $em): Response
+ {
+ // Récupération de l'enregistrement courant
+ $data=$em->getRepository($this->entity)->find($id);
+ if (!$data) throw $this->createNotFoundException('Unable to find entity.');
+
+ // Controler les permissions
+ $this->canupdate($access,$data,$em);
+
+ // Tentative de suppression
+ try{
+ $em->getManager()->remove($data);
+ $em->getManager()->flush();
+ }
+ catch (\Exception $e) {
+ $request->getSession()->getFlashBag()->add("error", $e->getMessage());
+ return $this->redirectToRoute(str_replace("_admin_","_".$access."_",$this->route)."_update",["id"=>$id]);
+ }
+
+ return $this->redirectToRoute(str_replace("_admin_","_".$access."_",$this->route));
+ }
+
+ private function cansubmit($access,$em) {
+ switch($access) {
+ case "admin" : return true; break;
+ case "modo" : return true; break;
+ }
+ throw $this->createAccessDeniedException('Permission denied');
+ }
+
+
+ private function canupdate($access,$entity,$em) {
+ switch($access) {
+ case "admin" : return true; break;
+ case "modo" :
+ $usermodo=$em->getRepository("App\Entity\UserModo")->findOneBy(["user"=>$this->getUser(),"niveau01"=>$entity->getNiveau01()]);
+ if(!$usermodo) throw $this->createAccessDeniedException('Permission denied');
+ return true;
+ break;
+ }
+ throw $this->createAccessDeniedException('Permission denied');
+ }
+}
\ No newline at end of file
diff --git a/src/Controller/PublishController.php b/src/Controller/PublishController.php
new file mode 100644
index 0000000..4393df8
--- /dev/null
+++ b/src/Controller/PublishController.php
@@ -0,0 +1,27 @@
+get("msg");
+ $ret["from"]=[];
+ $ret["from"]["id"]="tot";
+ $update = new Update(
+ $channel."-".$id,
+ json_encode(
+ ['ret' => $ret])
+ );
+
+ $hub->publish($update);
+
+ return new Response('published!');
+ }
+}
\ No newline at end of file
diff --git a/src/Controller/RegistrationController.php b/src/Controller/RegistrationController.php
new file mode 100755
index 0000000..1eb0561
--- /dev/null
+++ b/src/Controller/RegistrationController.php
@@ -0,0 +1,737 @@
+mail = $mail;
+ }
+
+ public function list($access)
+ {
+ $appmoderegistration = $this->getParameter('appModeregistration');
+ $appMasteridentity = $this->getParameter('appMasteridentity');
+ if($appmoderegistration=="none"||$appMasteridentity!="SQL")
+ throw $this->createAccessDeniedException('Permission denied');
+
+ return $this->render($this->twig.'list.html.twig',[
+ 'useheader' => true,
+ 'usemenu' => false,
+ 'usesidebar' => true,
+ 'access' => $access,
+ ]);
+ }
+
+ public function tablelist($access, Request $request,ManagerRegistry $em): Response
+ {
+
+ $query=$request->query->all();
+ $start=$query['start'];
+ $length=$query['length'];
+ $search=$query['search'];
+ $draw= $query['draw'];
+ $ordercolumn=$query['order'][0]['column'];
+ $orderdir=$query['order'][0]['dir'];
+
+ // Nombre total d'enregistrement
+ if($access=="admin")
+ $total = $em->getManager()->createQueryBuilder()->select('COUNT(entity)')->from($this->entity,'entity')->getQuery()->getSingleScalarResult();
+ else {
+ $usermodo=$this->getUser();
+ $total = $em->getManager()->createQueryBuilder()
+ ->select('COUNT(entity)')
+ ->from($this->entity,'entity')
+ ->from("App:UserModo",'usermodo')
+ ->where("usermodo.niveau01 = entity.niveau01")
+ ->andWhere("usermodo.user = :user")
+ ->setParameter("user", $usermodo)
+ ->getQuery()->getSingleScalarResult();
+ }
+
+ // Nombre d'enregistrement filtré
+ if($search["value"]=="")
+ $totalf = $total;
+ else {
+ if($access=="admin")
+ $totalf= $em->getManager()->createQueryBuilder()
+ ->select('COUNT(entity)')
+ ->from($this->entity,'entity')
+ ->where('entity.username LIKE :value')
+ ->orWhere('entity.email LIKE :value')
+ ->setParameter("value", "%".$search["value"]."%")
+ ->getQuery()
+ ->getSingleScalarResult();
+ else
+ $totalf= $em->getManager()->createQueryBuilder()
+ ->select('COUNT(entity)')
+ ->from($this->entity,'entity')
+ ->from("App:UserModo",'usermodo')
+ ->where('entity.username LIKE :value OR entity.email LIKE :value')
+ ->andWhere("usermodo.niveau01 = entity.niveau01")
+ ->andWhere("usermodo.user = :user")
+ ->setParameter("value", "%".$search["value"]."%")
+ ->setParameter("user", $usermodo)
+ ->getQuery()
+ ->getSingleScalarResult();
+ }
+
+ // Construction du tableau de retour
+ $output = array(
+ 'draw' => $draw,
+ 'recordsFiltered' => $totalf,
+ 'recordsTotal' => $total,
+ 'data' => array(),
+ );
+
+ // Parcours des Enregistrement
+ $qb = $em->getManager()->createQueryBuilder();
+ if($this->isGranted('ROLE_ADMIN')) {
+ $qb->select('entity')->from($this->entity,'entity')->from('App:Niveau01','niveau01');
+ $qb->where('entity.niveau01=niveau01.id');
+ }
+ else{
+ $qb->select('entity')->from($this->entity,'entity')->from('App:Niveau01','niveau01')->from("App:UserModo",'usermodo');
+ $qb->where('entity.niveau01=niveau01.id')
+ ->andWhere("usermodo.niveau01 = entity.niveau01")
+ ->andWhere("usermodo.user = :user")
+ ->setParameter("user", $usermodo);
+ }
+
+
+ if($search["value"]!="") {
+ $qb ->andWhere('entity.username LIKE :value OR entity.email LIKE :value OR niveau01.label LIKE :value')
+ ->setParameter("value", "%".$search["value"]."%");
+ }
+ switch($ordercolumn) {
+ case 1 :
+ $qb->orderBy('entity.username',$orderdir);
+ break;
+
+ case 2 :
+ $qb->orderBy('entity.email',$orderdir);
+ break;
+
+ case 3 :
+ $qb->orderBy('entity.label',$orderdir);
+ break;
+
+ case 4 :
+ $qb->orderBy('entity.statut',$orderdir);
+ break;
+
+ case 5 :
+ $qb->orderBy('entity.keyexpire',$orderdir);
+ break;
+ }
+
+ $datas=$qb->setFirstResult($start)->setMaxResults($length)->getQuery()->getResult();
+
+ foreach($datas as $data) {
+ $action ="";
+ // Si inscription non périmée
+ if($data->getStatut()<=2) {
+ $action.="$data->getId()))."'>";
+ }
+
+ $statut="";
+ switch($data->getStatut()) {
+ case 1: $statut='En attente validation Administration'; break;
+ case 2: $statut='En attente validation Utilisateur'; break;
+ case 3: $statut='Inscription expirée'; break;
+ }
+
+ array_push($output["data"],array(
+ $action,
+ $data->getUsername(),
+ $data->getEmail(),
+ $data->getNiveau01()->getLabel(),
+ $statut,
+ (is_null($data->getKeyexpire())?"":$data->getKeyexpire()->format('d/m/Y H:i:s'))
+ ));
+ }
+
+ // Retour
+ return new JsonResponse($output);
+ }
+
+
+
+ public function submit(Request $request,ManagerRegistry $em): Response
+ {
+ $appmoderegistration = $this->getParameter('appModeregistration');
+ $appMasteridentity = $this->getParameter('appMasteridentity');
+
+ if($appmoderegistration=="none"||$appMasteridentity!="SQL")
+ throw $this->createAccessDeniedException('Permission denied');
+
+ $data = new Registration();
+ $data->setIsvisible(true);
+
+ // Création du formulaire
+ $form = $this->createForm(Form::class,$data,array(
+ "mode"=>"submit",
+ "access"=>"user",
+ "userid"=>null,
+ "appMasteridentity"=>$this->GetParameter("appMasteridentity"),
+ "appNiveau01label"=>$this->GetParameter("appNiveau01label"),
+ "appNiveau02label"=>$this->GetParameter("appNiveau02label"),
+ ));
+
+ // Récupération des data du formulaire
+ $form->handleRequest($request);
+
+ // si mode de registration byuser
+ if($appmoderegistration=="byuser") {
+ $idstatut=2;
+ }
+ else {
+ // On recherche le domaine du mail dans la liste blanche
+ $email=explode("@",$data->getEmail());
+ $domaine=end($email);
+ $whitelist = $em->getRepository("App\Entity\Whitelist")->findBy(["label"=>$domaine]);
+ $idstatut=(!$whitelist?1:2);
+ }
+ $data->setStatut($idstatut);
+
+ // Sur erreur
+ $this->getErrorForm(null,$form,$request,$data,"submit",$idstatut);
+
+ // Sur validation
+ if ($form->get('submit')->isClicked() && $form->isValid()) {
+ $data = $form->getData();
+
+ $appname = $request->getSession()->get('appname');
+ $noreply = $this->getParameter('appMailnoreply');
+ $appModeregistrationterme = $this->getParameter('appModeregistrationterme');
+
+ // si non : validation par administrateur
+ if($idstatut==1) {
+ // Email à destination de l'inscript pour le prévenir qu'un administrateur doit valider
+ $subject=$appname." : Inscription en cours de validation";
+ $body="Votre inscription a bien été enregistrée.
Cependant, un administrateur doit encore valider votre inscription avant que celle-ci ne devienne effective.
Vous recevrez un mail quand votre inscription sera validée";
+ $info=$body;
+ $to = $data->getEmail();
+ $from = $noreply;
+ $fromName = $appname;
+ $this->mail->sendEmail($subject, $body, $to, $from, $fromName);
+
+ // Email à l'ensemble administrateurs pour les prévenir qu'il y a une personne à valider
+ $url = $this->generateUrl('app_admin_registration', [], UrlGeneratorInterface::ABSOLUTE_URL);
+ $to=array();
+ $from = $noreply;
+ $fromName = $appname;
+ $subject=$appname." : Inscription à valider";
+ $motivation = "Login = ".$data->getUsername()."
";
+ $motivation.= "Nom = ".$data->getLastname()."
";
+ $motivation.= "Prénom = ".$data->getFirstname()."
";
+ $motivation.= "Mail = ".$data->getEmail()."
";
+ $motivation.= $this->getParameter("appNiveau01label")." = ".$data->getNiveau01()->getLabel();
+ $motivation.= $data->getMotivation();
+ $body="Un utilisateur dont le mail n’est pas en liste blanche souhaite s’inscrire à ".$appname.".\nMerci d’approuver son inscription pour finaliser celle-ci.
Veuillez vérifier cette inscription à cette adresse:
$url
".$motivation;
+ $emailadmins= $em ->getManager()->createQueryBuilder()
+ ->select('table.email')
+ ->from("App:User",'table')
+ ->where('table.roles LIKE :value')
+ ->setParameter("value", "%ROLE_ADMIN%")
+ ->getQuery()
+ ->getResult(\Doctrine\ORM\Query::HYDRATE_SCALAR);
+ foreach($emailadmins as $emailadmin) {
+ array_push($to,$emailadmin["email"]);
+ }
+ $this->mail->sendEmail($subject, $body, $to, $from, $fromName);
+
+ // Email à l'ensemble des modérateurs du service pour les prévenir qu'il y a une personne à valider
+ $niveau01id=$data->getNiveau01()->getId();
+ $url = $this->generateUrl('app_modo_registration', [], UrlGeneratorInterface::ABSOLUTE_URL);
+ $to=array();
+ $from = $noreply;
+ $fromName = $appname;
+ $subject=$appname." : Inscription à valider";
+ $motivation = "Login = ".$data->getUsername()."
";
+ $motivation.= "Nom = ".$data->getLastname()."
";
+ $motivation.= "Prénom = ".$data->getFirstname()."
";
+ $motivation.= "Mail = ".$data->getEmail()."
";
+ $motivation.= $this->getParameter("appNiveau01label")." = ".$data->getNiveau01()->getLabel();
+ $motivation.= $data->getMotivation();
+ $body="Un utilisateur dont le mail n’est pas en liste blanche souhaite s’inscrire à ".$appname.".\nMerci d’approuver son inscription pour finaliser celle-ci.
Veuillez vérifier cette inscription à cette adresse:
$url
".$motivation;
+ $emailmodos= $em ->getManager()->createQueryBuilder()
+ ->select('user.email')
+ ->from("App:UserModo",'usermodo')
+ ->from("App:User",'user')
+ ->where("usermodo.niveau01 = :niveau01id")
+ ->andWhere("user.id = usermodo.user")
+ ->andWhere('user.roles LIKE :value')
+ ->setParameter("niveau01id", $niveau01id)
+ ->setParameter("value", "%ROLE_MODO%")
+ ->getQuery()
+ ->getResult(\Doctrine\ORM\Query::HYDRATE_SCALAR);
+ foreach($emailmodos as $emailmodo) {
+ array_push($to,$emailmodo["email"]);
+ }
+ $this->mail->sendEmail($subject, $body, $to, $from, $fromName);
+
+ }
+
+ // si oui : Domaine de confiance : email de validation d'inscription directement à l'utilisateur
+ else {
+ // Génération de la date de fin de validité de la clé
+ $keyexpire=new \DateTime();
+ $keyexpire->add(new \DateInterval('PT'.$appModeregistrationterme.'H'));
+
+ // Enregistrement des valeurs
+ $data->setKeyvalue(Uuid::uuid4());
+ $data->setKeyexpire($keyexpire);
+
+ // Email à l'utilisateur
+ $url = $this->generateUrl('app_registration_validation', array("key"=>$data->getKeyvalue()), UrlGeneratorInterface::ABSOLUTE_URL);
+ $subject=$appname." : confirmation de validation";
+ $body="Merci de confirmer votre inscription en cliquant sur le lien suivant
".$url."
Attention vous disposez d’un délai de 8 heures pour le faire. Passé ce délai, vous devrez vous réinscrire.
";
+ $info="Vous allez recevoir un mail de confirmation pour finaliser votre inscription";
+ $to = $data->getEmail();
+ $from = $noreply;
+ $fromName = $appname;
+ $this->mail->sendEmail($subject, $body, $to, $from, $fromName);
+ }
+
+ // Sauvegarde
+ $em->getManager()->persist($data);
+ $em->getManager()->flush();
+
+ // A voir retour sur un écran d'info indiquant si validation par admion ou s'il doit matter ses email
+ $request->getSession()->set('registrationinfo', $info);
+ $request->getSession()->set('registrationmode', "info");
+ $request->getSession()->set('registrationredirectto', null);
+
+ return $this->redirectToRoute('app_registration_info');
+ }
+ else {
+ return $this->render($this->twig.'edit.html.twig', [
+ 'useheader' => true,
+ 'usemenu' => false,
+ 'usesidebar' => false,
+ 'maxsize' => 1200,
+ $this->data => $data,
+ 'mode' => 'submit',
+ 'form' => $form->createView()
+ ]);
+ }
+
+ }
+ public function info(Request $request)
+ {
+ $info = $request->getSession()->get('registrationinfo');
+ $mode = $request->getSession()->get('registrationmode');
+ $redirectto = $request->getSession()->get('registrationredirectto');
+
+
+ return $this->render($this->twig.'info.html.twig', [
+ 'useheader' => true,
+ 'usemenu' => false,
+ 'usesidebar' => false,
+ 'maxwidth' => true,
+ 'info' => $info,
+ 'mode' => $mode,
+ 'redirectto' => $redirectto,
+ ]);
+ }
+
+ public function update($id,$access,Request $request,ManagerRegistry $em): Response
+ {
+ $appname = $request->getSession()->get('appname');
+ $noreply = $this->getParameter('appMailnoreply');
+ $appModeregistrationterme = $this->getParameter('appModeregistrationterme');
+ $appMasteridentity = $this->getParameter('appMasteridentity');
+
+ if($appModeregistrationterme=="none"||$appMasteridentity!="SQL")
+ throw $this->createAccessDeniedException('Permission denied');
+
+ // Initialisation de l'enregistrement
+ $data=$em->getRepository($this->entity)->find($id);
+ if (!$data) throw $this->createNotFoundException('Unable to find entity.');
+
+ // Controler les permissions
+ $this->canupdate($access,$data,$em);
+
+ // Création du formulaire
+ $form = $this->createForm(Form::class,$data,array(
+ "mode"=>"update",
+ "access"=>$access,
+ "userid"=>$this->getUser()->getId(),
+ "appMasteridentity"=>$this->GetParameter("appMasteridentity"),
+ "appNiveau01label"=>$this->GetParameter("appNiveau01label"),
+ "appNiveau02label"=>$this->GetParameter("appNiveau02label"),
+ ));
+
+ // Récupération des data du formulaire
+ $form->handleRequest($request);
+
+ // Sur validation
+ if ($form->get('save')->isClicked() && $form->isValid()) {
+ $data = $form->getData();
+
+ // Sauvegarde
+ $em->getManager()->flush();
+
+ // Retour à la liste
+ return $this->redirectToRoute(str_replace("_admin_","_".$access."_",$this->route));
+ }
+
+ // Sur validation
+ if ($form->get('submit')->isClicked() && $form->isValid()) {
+ $data = $form->getData();
+
+ $appname = $request->getSession()->get('appname');
+ $noreply = $this->getParameter('appMailnoreply');
+ $appModeregistrationterme = $this->getParameter('appModeregistrationterme');
+
+ // Génération de la date de fin de validité de la clé
+ $keyexpire=new \DateTime();
+ $keyexpire->add(new \DateInterval('PT'.$appModeregistrationterme.'H'));
+
+ // Enregistrement des valeurs
+ $data->setKeyvalue(Uuid::uuid4());
+ $data->setKeyexpire($keyexpire);
+
+ // Statut en attente validation utilisateur
+ $data->setStatut(2);
+
+ // Email à l'utilisateur
+ $url = $this->generateUrl('app_registration_validation', array("key"=>$data->getKeyvalue()), UrlGeneratorInterface::ABSOLUTE_URL);
+ $subject=$appname." : confirmation de validation";
+ $body="Merci de confirmer votre inscription en cliquant sur le lien suivant
".$url."
Attention vous disposez d’un délai de 8 heures pour le faire. Passé ce délai, vous devrez vous réinscrire.
";
+ $to = $data->getEmail();
+ $from = $noreply;
+ $fromName = $appname;
+ $this->mail->sendEmail($subject, $body, $to, $from, $fromName);
+
+ // Sauvegarde
+ $em->getManager()->flush();
+
+ // Retour à la liste
+ return $this->redirectToRoute(str_replace("_admin_","_".$access."_",$this->route));
+ }
+
+ // Affichage du formulaire
+ return $this->render($this->twig.'edit.html.twig', [
+ 'useheader' => true,
+ 'usemenu' => false,
+ 'usesidebar' => true,
+ $this->data => $data,
+ 'mode' => 'update',
+ 'access' => $access,
+ 'form' => $form->createView()
+ ]);
+ }
+
+ public function validation($key,Request $request,ManagerRegistry $em)
+ {
+ $appmoderegistration = $this->getParameter('appModeregistration');
+ $appMasteridentity = $this->getParameter('appMasteridentity');
+
+ if($appmoderegistration=="none"||$appMasteridentity!="SQL")
+ throw $this->createAccessDeniedException('Permission denied');
+
+ $now=new \DateTime();
+
+ $data = $em ->getManager()->createQueryBuilder()
+ ->select('entity')
+ ->from($this->entity,'entity')
+ ->where('entity.keyvalue= :key')
+ ->andWhere('entity.keyexpire >= :date')
+ ->setParameter("key", $key)
+ ->setParameter("date", $now)
+ ->getQuery()
+ ->getSingleResult();
+
+ if(!$data) {
+ $info="Clé de validation invalide";
+ $mode="danger";
+
+ $request->getSession()->set('registrationinfo', $info);
+ $request->getSession()->set('registrationmode', $mode);
+ $request->getSession()->set('registrationredirectto', null);
+ }
+ else {
+ $url=$this->generateUrl('app_login');
+ $info="Votre compte est à présent activé
Vous allez être redirigé vers la mire de connexion
Connexion";
+ $mode="success";
+
+ $request->getSession()->set('registrationinfo', $info);
+ $request->getSession()->set('registrationmode', $mode);
+
+ // Initialisation de l'enregistrement
+ $user = new User();
+ $user->setAvatar("noavatar.png");
+ $user->setUsername($data->getUsername());
+ $user->setEmail($data->getEmail());
+ $user->setLastname($data->getLastname());
+ $user->setFirstname($data->getFirstname());
+ $user->setSalt($data->getSalt());
+ $user->setPasswordDirect($data->getPassword());
+ $user->setIsvisible($data->isIsvisible());
+ $user->setMotivation($data->getMotivation());
+ $user->setNote($data->getNote());
+ $user->setApikey(Uuid::uuid4());
+
+ $user->setNiveau01($data->getNiveau01());
+ $user->setNiveau02($data->getNiveau02());
+
+ $user->setTelephonenumber($data->getTelephonenumber());
+ $user->setPostaladress($data->getPostaladress());
+ $user->setJob($data->getJob());
+ $user->setPosition($data->getPosition());
+ $user->setRoles(["ROLE_USER"]);
+
+ // Sauvegarde
+ $em->getManager()->persist($user);
+ $em->getManager()->flush();
+
+ // Suppression inscription
+ $em->getManager()->remove($data);
+ $em->getManager()->flush();
+ }
+
+ return $this->redirectToRoute('app_registration_info');
+ }
+
+
+ public function delete($id,$access,Request $request,ManagerRegistry $em)
+ {
+ // Récupération de l'enregistrement courant
+ $data=$em->getRepository($this->entity)->find($id);
+ if (!$data) throw $this->createNotFoundException('Unable to find entity.');
+
+ // Controler les permissions
+ $this->candelete($access,$data,$em);
+
+
+ // Tentative de suppression
+ try{
+ $em->getManager()->remove($data);
+ $em->getManager()->flush();
+ }
+ catch (\Exception $e) {
+ $request->getSession()->getFlashBag()->add("error", $e->getMessage());
+ return $this->redirectToRoute(str_replace("_admin_","_".$access."_",$this->route)."_update",["id"=>$id]);
+ }
+
+ return $this->redirectToRoute(str_replace("_admin_","_".$access."_",$this->route));
+ }
+
+ public function resetpwd01(Request $request,ManagerRegistry $em)
+ {
+ $appmoderegistration = $this->getParameter('appModeregistration');
+ $appMasteridentity = $this->getParameter('appMasteridentity');
+ if($appMasteridentity!="SQL")
+ throw $this->createAccessDeniedException('Permission denied');
+
+ // Création du formulaire
+ $form = $this->createForm(ResetpwdType::class,null,array("mode"=>"resetpwd01"));
+
+ // Récupération des data du formulaire
+ $form->handleRequest($request);
+ $data = $form->getData();
+
+ if ($form->get('submit')->isClicked()) {
+ $user=$em->getRepository("App\Entity\User")->findOneby(["email"=>$data->getEmail()]);
+
+ // On s'assure que le mail existe dans la base des utilisateurs
+ if(!$user) {
+ $request->getSession()->getFlashBag()->add("error", 'Mail inconnu');
+
+ // Affichage du formulaire
+ dump("here");
+ return $this->render($this->twig.'resetpwd01.html.twig', [
+ 'useheader' => true,
+ 'usemenu' => false,
+ 'usesidebar' => false,
+ 'maxsize' => 1200,
+ 'form' => $form->createView()
+ ]);
+ }
+ }
+
+ // Sur validation
+ if ($form->get('submit')->isClicked()) {
+ $user=$em->getRepository("App\Entity\User")->findOneby(["email"=>$data->getEmail()]);
+
+ $appname = $request->getSession()->get('appname');
+ $noreply = $this->getParameter('appMailnoreply');
+ $appModeregistrationterme = $this->getParameter('appModeregistrationterme');
+
+ // Génération de la date de fin de validité de la clé
+ $keyexpire=new \DateTime();
+ $keyexpire->add(new \DateInterval('PT'.$appModeregistrationterme.'H'));
+
+ // Enregistrement des valeurs
+ $user->setKeyvalue(Uuid::uuid4());
+ $user->setKeyexpire($keyexpire);
+
+ // Sauvegarde
+ $em->getManager()->flush();
+
+ // Email au user
+ $url = $this->generateUrl('app_resetpwd02', array("key"=>$user->getKeyvalue()), UrlGeneratorInterface::ABSOLUTE_URL);
+ $subject=$appname." : réinitialisation mot de passe";
+ $body="
Merci de réinitialiser votre mot de passe en cliquant sur le lien suivant
".$url."
Attention vous disposez d’un délai de ".$appModeregistrationterme." heures pour le faire.
Vous pourrez par la suite vous connecter avec votre login : ".$user->getUsername()."
";
+ $to = $user->getEmail();
+ $from = $noreply;
+ $fromName = $appname;
+ $this->mail->sendEmail($subject, $body, $to, $from, $fromName);
+
+ // Info
+ $info="Vous allez recevoir un mail avec lien qui vous permettra de réinitialiser votre mot de passe";
+ $mode="info";
+ $request->getSession()->set('registrationinfo', $info);
+ $request->getSession()->set('registrationmode', $mode);
+ $request->getSession()->set('registrationredirectto', null);
+
+ return $this->redirectToRoute('app_registration_info');
+ }
+
+ // Affichage du formulaire
+ return $this->render($this->twig.'resetpwd01.html.twig', [
+ 'useheader' => true,
+ 'usemenu' => false,
+ 'usesidebar' => false,
+ 'maxsize' => 1200,
+ 'form' => $form->createView()
+ ]);
+ }
+
+ public function resetpwd02($key,Request $request,ManagerRegistry $em)
+ {
+ $appMasteridentity = $this->getParameter('appMasteridentity');
+ if($appMasteridentity!="SQL")
+ throw $this->createAccessDeniedException('Permission denied');
+
+ $now=new \DateTime();
+
+ $user = $em ->getManager()->createQueryBuilder()
+ ->select('table')
+ ->from("App:User",'table')
+ ->where('table.keyvalue= :key')
+ ->andWhere('table.keyexpire >= :date')
+ ->setParameter("key", $key)
+ ->setParameter("date", $now)
+ ->getQuery()
+ ->getSingleResult();
+
+ if(!$user) {
+ $info="Clé de validation invalide";
+ $mode="danger";
+ $request->getSession()->set('registrationinfo', $info);
+ $request->getSession()->set('registrationmode', $mode);
+ $request->getSession()->set('registrationredirectto', null);
+ return $this->redirectToRoute('app_registration_info');
+ }
+ else {
+ // Création du formulaire
+ $form = $this->createForm(ResetpwdType::class,$user,array("mode"=>"resetpwd02"));
+
+ // Récupération des data du formulaire
+ $form->handleRequest($request);
+
+ if ($form->get('submit')->isClicked() && $form->isValid()) {
+ $data = $form->getData();
+ $user->setKeyvalue(null);
+ $user->setKeyexpire(null);
+ $user->setPassword($data->getPassword());
+
+ // Sauvegarde
+ $em->getManager()->flush();
+
+ $url=$this->generateUrl('app_login');
+ $info="Nouveau mot de passe prise en compte
Vous allez être redirigé vers la mire de connexion
Connexion";
+ $mode="success";
+ $request->getSession()->set('registrationinfo', $info);
+ $request->getSession()->set('registrationmode', $mode);
+ $request->getSession()->set('registrationredirectto', null);
+ return $this->redirectToRoute('app_registration_info');
+ }
+
+ // Affichage du formulaire
+ return $this->render($this->twig.'resetpwd02.html.twig', [
+ 'useheader' => true,
+ 'usemenu' => false,
+ 'usesidebar' => false,
+ 'maxsize' => 1200,
+ 'form' => $form->createView()
+ ]);
+ }
+ }
+
+ private function canupdate($access,$entity,$em) {
+ switch($access) {
+ case "admin" : return true; break;
+ case "modo" :
+ $usermodo=$em->getRepository("App\Entity\UserModo")->findOneBy(["user"=>$this->getUser(),"niveau01"=>$entity->getNiveau01()]);
+ if(!$usermodo) throw $this->createAccessDeniedException('Permission denied');
+ return true;
+ break;
+ }
+ throw $this->createAccessDeniedException('Permission denied');
+ }
+
+ private function candelete($access,$entity,$em) {
+ switch($access) {
+ case "admin" : return true; break;
+ case "modo" :
+ $usermodo=$em->getRepository("App\Entity\UserModo")->findOneBy(["user"=>$this->getUser(),"niveau01"=>$entity->getNiveau01()]);
+ if(!$usermodo) throw $this->createAccessDeniedException('Permission denied');
+ return true;
+ break;
+ }
+ throw $this->createAccessDeniedException('Permission denied');
+ }
+
+ protected function getErrorForm($id,$form,$request,$data,$mode,$idstatut) {
+ if ($form->get('submit')->isClicked() && $mode=="submit") {
+ // Si validation par administrateur demander une motivation
+ $appmoderegistration = $this->getParameter('appModeregistration');
+ if(is_null($data->getMotivation())&&$appmoderegistration=="byadmin") {
+ // On recherche le domaine du mail dans la liste blanche
+ $email=explode("@",$data->getEmail());
+ $domaine=end($email);
+ $whitelist = $this->getDoctrine()->getManager()->getRepository("App\Entity\Whitelist")->findBy(["label"=>$domaine]);
+ if(!$whitelist)
+ $form->addError(new FormError("Attention, le suffixe de votre adresse mail n’est pas dans la liste des administrations autorisées, merci de bien vouloir privilégier votre adresse professionnelle si vous en avez une.
Si ce n’est pas le cas, il faut que vous renseigniez la case motivation de votre demande"));
+ }
+ }
+
+ if ($form->get('submit')->isClicked() && !$form->isValid()) {
+ $errors = $form->getErrors();
+ foreach( $errors as $error ) {
+ $request->getSession()->getFlashBag()->add("error", $error->getMessage());
+ $request->getSession()->getFlashBag()->add("error", $error->getMessage());
+ }
+ }
+ }
+
+
+}
diff --git a/src/Controller/RestController.php b/src/Controller/RestController.php
new file mode 100644
index 0000000..3fc7c93
--- /dev/null
+++ b/src/Controller/RestController.php
@@ -0,0 +1,468 @@
+iskey($request->headers->get("key"))) {
+ $view = $this->view("API Key inconnue", 403);
+ return $this->handleView($view);
+ }
+
+ $output = [];
+ $users=$em->getRepository("App\Entity\User")->findAll();
+ foreach($users as $user) {
+ array_push($output,$this->userFormat($user));
+ }
+
+ $view = $this->view($output, 200);
+ return $this->handleView($view);
+ }
+
+
+ /**
+ * getOneUser
+ *
+ *
+ * @FOSRest\Get("/rest/getOneUser")
+ * @OA\Response(
+ * response=200,
+ * description="get one user by login"
+ * )
+ * )
+ * @OA\Parameter(
+ * name="key",
+ * in="header",
+ * required=true,
+ * description="APIKey",
+ * @OA\Schema(type="string")
+ * )
+ * @OA\Parameter(
+ * name="login",
+ * in="header",
+ * required=true,
+ * description="Login",
+ * @OA\Schema(type="string")
+ * )
+ */
+
+
+ public function getOneUser(Request $request,ManagerRegistry $em) {
+ set_time_limit(0);
+ ini_set('memory_limit', '1024M');
+
+ // Récupération des parametres
+ if(!$this->iskey($request->headers->get("key"))) {
+ $view = $this->view("API Key inconnue", 403);
+ return $this->handleView($view);
+ }
+
+ $output = [];
+ $user=$em->getRepository("App\Entity\User")->findOneBy(["username"=>$request->headers->get("login")]);
+ if(!$user) {
+ $view = $this->view("Utilisateur inconnue", 403);
+ return $this->handleView($view);
+ }
+
+ $output=$this->userFormat($user);
+ $view = $this->view($output, 200);
+ return $this->handleView($view);
+ }
+
+
+ /**
+ * getAllNiveau01s
+ *
+ *
+ * @FOSRest\Get("/rest/getAllNiveau01s")
+ * @OA\Response(
+ * response=200,
+ * description="get all niveau01"
+ * )
+ * )
+ * @OA\Parameter(
+ * name="key",
+ * in="header",
+ * required=true,
+ * description="APIKey",
+ * @OA\Schema(type="string")
+ * )
+ */
+
+
+
+ public function getAllNiveau01s(Request $request,ManagerRegistry $em) {
+ set_time_limit(0);
+ ini_set('memory_limit', '1024M');
+
+ // Récupération des parametres
+ if(!$this->iskey($request->headers->get("key"))) {
+ $view = $this->view("API Key inconnue", 403);
+ return $this->handleView($view);
+ }
+
+ $output = [];
+ $niveau01s=$em->getRepository("App\Entity\Niveau01")->findAll();
+ foreach($niveau01s as $niveau01) {
+ array_push($output,$this->niveau01Format($niveau01,true));
+ }
+
+ $view = $this->view($output, 200);
+ return $this->handleView($view);
+ }
+
+ /**
+ * getOneNiveau01
+ *
+ *
+ * @FOSRest\Get("/rest/getOneNiveau01")
+ * @OA\Response(
+ * response=200,
+ * description="get one niveau01 by label"
+ * )
+ * )
+ * @OA\Parameter(
+ * name="key",
+ * in="header",
+ * required=true,
+ * description="APIKey",
+ * @OA\Schema(type="string")
+ * )
+ * @OA\Parameter(
+ * name="label",
+ * in="header",
+ * required=true,
+ * description="Label",
+ * @OA\Schema(type="string")
+ * )
+ */
+
+
+ public function getOneNiveau01(Request $request,ManagerRegistry $em) {
+ set_time_limit(0);
+ ini_set('memory_limit', '1024M');
+
+ // Récupération des parametres
+ if(!$this->iskey($request->headers->get("key"))) {
+ $view = $this->view("API Key inconnue", 403);
+ return $this->handleView($view);
+ }
+
+ $output = [];
+ $niveau01=$em->getRepository("App\Entity\Niveau01")->findOneBy(["label"=>$request->headers->get("label")]);
+ if(!$niveau01) {
+ $view = $this->view("Niveau01 inconnu", 403);
+ return $this->handleView($view);
+ }
+
+ $output=$this->niveau01Format($niveau01,true);
+ $view = $this->view($output, 200);
+ return $this->handleView($view);
+ }
+
+
+ /**
+ * getAllNiveau02s
+ *
+ *
+ * @FOSRest\Get("/rest/getAllNiveau02s")
+ * @OA\Response(
+ * response=200,
+ * description="get all niveau02"
+ * )
+ * )
+ * @OA\Parameter(
+ * name="key",
+ * in="header",
+ * required=true,
+ * description="APIKey",
+ * @OA\Schema(type="string")
+ * )
+ */
+
+
+
+ public function getAllNiveau02s(Request $request,ManagerRegistry $em) {
+ set_time_limit(0);
+ ini_set('memory_limit', '1024M');
+
+ // Récupération des parametres
+ if(!$this->iskey($request->headers->get("key"))) {
+ $view = $this->view("API Key inconnue", 403);
+ return $this->handleView($view);
+ }
+
+ $output = [];
+ $niveau02s=$em->getRepository("App\Entity\Niveau02")->findAll();
+ foreach($niveau02s as $niveau02) {
+ array_push($output,$this->niveau02Format($niveau02,true));
+ }
+
+ $view = $this->view($output, 200);
+ return $this->handleView($view);
+ }
+
+ /**
+ * getOneNiveau02
+ *
+ *
+ * @FOSRest\Get("/rest/getOneNiveau02")
+ * @OA\Response(
+ * response=200,
+ * description="get one niveau02 by label"
+ * )
+ * )
+ * @OA\Parameter(
+ * name="key",
+ * in="header",
+ * required=true,
+ * description="APIKey",
+ * @OA\Schema(type="string")
+ * )
+ * @OA\Parameter(
+ * name="label",
+ * in="header",
+ * required=true,
+ * description="Label",
+ * @OA\Schema(type="string")
+ * )
+ */
+
+
+ public function getOneNiveau02(Request $request,ManagerRegistry $em) {
+ set_time_limit(0);
+ ini_set('memory_limit', '1024M');
+
+ // Récupération des parametres
+ if(!$this->iskey($request->headers->get("key"))) {
+ $view = $this->view("API Key inconnue", 403);
+ return $this->handleView($view);
+ }
+
+ $output = [];
+ $niveau02=$em->getRepository("App\Entity\Niveau02")->findOneBy(["label"=>$request->headers->get("label")]);
+ if(!$niveau02) {
+ $view = $this->view("Niveau02 inconnu", 403);
+ return $this->handleView($view);
+ }
+
+ $output=$this->niveau02Format($niveau02,true);
+ $view = $this->view($output, 200);
+ return $this->handleView($view);
+ }
+
+
+ /**
+ * getAllGroups
+ *
+ *
+ * @FOSRest\Get("/rest/getAllGroups")
+ * @OA\Response(
+ * response=200,
+ * description="get all group"
+ * )
+ * )
+ * @OA\Parameter(
+ * name="key",
+ * in="header",
+ * required=true,
+ * description="APIKey",
+ * @OA\Schema(type="string")
+ * )
+ */
+
+
+
+ public function getAllGroups(Request $request,ManagerRegistry $em) {
+ set_time_limit(0);
+ ini_set('memory_limit', '1024M');
+
+ // Récupération des parametres
+ if(!$this->iskey($request->headers->get("key"))) {
+ $view = $this->view("API Key inconnue", 403);
+ return $this->handleView($view);
+ }
+
+ $output = [];
+ $groups=$em->getRepository("App\Entity\Group")->findAll();
+ foreach($groups as $group) {
+ if($group->getId()<0) continue;
+ array_push($output,$this->groupFormat($group,true));
+ }
+
+ $view = $this->view($output, 200);
+ return $this->handleView($view);
+ }
+
+ /**
+ * getOneGroup
+ *
+ *
+ * @FOSRest\Get("/rest/getOneGroup")
+ * @OA\Response(
+ * response=200,
+ * description="get one group by label"
+ * )
+ * )
+ * @OA\Parameter(
+ * name="key",
+ * in="header",
+ * required=true,
+ * description="APIKey",
+ * @OA\Schema(type="string")
+ * )
+ * @OA\Parameter(
+ * name="label",
+ * in="header",
+ * required=true,
+ * description="Label",
+ * @OA\Schema(type="string")
+ * )
+ */
+
+
+ public function getOneGroup(Request $request,ManagerRegistry $em) {
+ set_time_limit(0);
+ ini_set('memory_limit', '1024M');
+
+ // Récupération des parametres
+ if(!$this->iskey($request->headers->get("key"))) {
+ $view = $this->view("API Key inconnue", 403);
+ return $this->handleView($view);
+ }
+
+ $output = [];
+ $group=$em->getRepository("App\Entity\Group")->findOneBy(["label"=>$request->headers->get("label")]);
+ if(!$group) {
+ $view = $this->view("Group inconnu", 403);
+ return $this->handleView($view);
+ }
+
+ $output=$this->groupFormat($group,true);
+ $view = $this->view($output, 200);
+ return $this->handleView($view);
+ }
+
+
+
+
+
+
+ private function iskey($key) {
+ return ($key==$this->getParameter("appSecret"));
+ }
+
+ private function userFormat($user) {
+ $output=[];
+
+ $output["userid"]=$user->getId();
+ $output["userlogin"]=$user->getUsername();
+ $output["userlastname"]=$user->getLastname();
+ $output["userfirstname"]=$user->getFirstname();
+ $output["useremail"]=$user->getEmail();
+ $output["userjob"]=$user->getJob();
+ $output["userposition"]=$user->getPosition();
+ $output["userpostaladress"]=$user->getPostaladress();
+ $output["usertelephonenumber"]=$user->getTelephonenumber();
+ if(stripos($user->getAvatar(),"http")===0) $output["useravatar"]=$user->getAvatar();
+ else $output["useravatar"]="https://".$this->getParameter("appWeburl").$this->getParameter("appAlias")."uploads/avatar/".$user->getAvatar();
+ $output["userniveau01"]=$this->niveau01Format($user->getNiveau01());
+ $output["userniveau02"]=$this->niveau02Format($user->getNiveau02());
+ $output["usergroups"]=[];
+ foreach($user->getGroups() as $usergroup) {
+ $groupFormat=$this->groupFormat($usergroup->getGroup());
+ if($groupFormat) array_push($output["usergroups"],$groupFormat);
+ }
+ if(empty($output["usergroups"])) $output["usergroups"]=null;
+ return $output;
+ }
+
+ private function niveau01Format($niveau01,$withmembers=false){
+ if(!$niveau01) return null;
+ $output=[];
+ $output["niveau01id"]=$niveau01->getId();
+ $output["niveau01label"]=$niveau01->getLabel();
+
+ if($withmembers) {
+ $output["niveau01users"]=[];
+ foreach($niveau01->getUsers() as $user) {
+ array_push($output["niveau01users"],["userid"=>$user->getId(),"userlogin"=>$user->getUsername()]);
+ }
+ if(empty($output["niveau01users"])) $output["niveau01users"]=null;
+ }
+
+ return $output;
+ }
+
+ private function niveau02Format($niveau02,$withmembers=false){
+ if(!$niveau02) return null;
+ $output=[];
+ $output["niveau02id"]=$niveau02->getId();
+ $output["niveau02label"]=$niveau02->getLabel();
+
+ if($withmembers) {
+ $output["niveau02niveau01"]=$this->niveau01Format($niveau02->getNiveau01());
+ $output["niveau02users"]=[];
+ foreach($niveau02->getUsers() as $user) {
+ array_push($output["niveau02users"],["userid"=>$user->getId(),"userlogin"=>$user->getUsername()]);
+ }
+ if(empty($output["niveau02users"])) $output["niveau02users"]=null;
+ }
+
+ return $output;
+ }
+
+ private function groupFormat($group,$withmembers=false){
+ if(!$group||$group->getId()<0) return null;
+ $output=[];
+ $output["groupid"]=$group->getId();
+ $output["grouplabel"]=$group->getLabel();
+
+ if($withmembers) {
+ $output["groupusers"]=[];
+ foreach($group->getUsers() as $usergroup) {
+ array_push($output["groupusers"],["userid"=>$usergroup->getUser()->getId(),"userlogin"=>$usergroup->getUser()->getUsername()]);
+ }
+ if(empty($output["groupusers"])) $output["groupusers"]=null;
+ }
+
+ return $output;
+ }
+}
+
+
diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php
new file mode 100755
index 0000000..5ea5ced
--- /dev/null
+++ b/src/Controller/SecurityController.php
@@ -0,0 +1,496 @@
+appKernel = $appKernel;
+ $this->tokenstorage = $tokenstorage;
+ $this->ldapservice = $ldapservice;
+ $this->apiservice = $apiservice;
+ }
+
+ public function login(Request $request, AuthenticationUtils $authenticationUtils,ManagerRegistry $em)
+ {
+ switch($this->getParameter("appAuth")) {
+ case "SQL":
+ return $this->loginSQL($request,$authenticationUtils,$em);
+ break;
+
+ case "CAS":
+ return $this->loginCAS($request,$authenticationUtils,$em);
+ break;
+
+ case "LDAP":
+ return $this->loginLDAP($request,$authenticationUtils,$em);
+ break;
+
+ case "OPENID":
+ return $this->loginOPENID($request,$authenticationUtils,$em);
+ break;
+ }
+ }
+
+ public function loginSQL(Request $request, AuthenticationUtils $authenticationUtils)
+ {
+ return $this->render('Home/loginSQL.html.twig', array(
+ 'last_username' => $authenticationUtils->getLastUsername(),
+ 'error' => $authenticationUtils->getLastAuthenticationError(),
+ ));
+ }
+
+ public function loginCAS(Request $request, AuthenticationUtils $authenticationUtils,ManagerRegistry $em)
+ {
+ // Récupération de la cible de navigation
+ $redirect = $request->getSession()->get("_security.main.target_path");
+
+ // Masteridentity
+ $appMasteridentity=$this->getParameter("appMasteridentity");
+
+ // Init Client CAS
+ $alias=$this->getParameter('appAlias');
+ \phpCAS::setDebug($this->appKernel->getProjectDir()."/var/log/cas.log");
+ \phpCAS::client(CAS_VERSION_2_0, $this->getParameter('casHost'), intval($this->getParameter('casPort')), is_null($this->getParameter('casPath')) ? '' : $this->getParameter('casPath'), false);
+ \phpCAS::setNoCasServerValidation();
+
+
+ // Authentification
+ \phpCAS::forceAuthentication();
+
+ // Récupération UID
+ $username = \phpCAS::getUser();
+
+ // Récupération Attribut
+ $attributes = \phpCAS::getAttributes();
+
+ // Init
+ $email = "$username@nomail.fr";
+ $lastname = $username;
+ $firstname = " ";
+ $avatar="noavatar.png";
+
+ // Rechercher l'utilisateur
+ if(isset($attributes[$this->getParameter('casUsername')]))
+ $username = $attributes[$this->getParameter('casUsername')];
+
+ if(isset($attributes[$this->getParameter('casEmail')]))
+ $email = $attributes[$this->getParameter('casEmail')];
+
+ if(isset($attributes[$this->getParameter('casLastname')]))
+ $lastname = $attributes[$this->getParameter('casLastname')];
+
+ if(isset($attributes[$this->getParameter('casFirstname')]))
+ $firstname = $attributes[$this->getParameter('casFirstname')];
+
+ if(isset($attributes[$this->getParameter('casAvatar')]))
+ $avatar = $attributes[$this->getParameter('casAvatar')];
+
+ // Génération auto des niveau01s et des groupes en fonction des attributs sso
+ $this->submitSSONiveau01($attributes,$em);
+ $this->submitSSOGroup($attributes,$em);
+
+ // Rechercher l'utilisateur
+ $user = $em->getRepository('App\Entity\User')->findOneBy(array("username"=>$username));
+ if (!$user) {
+ $niveau01=$em->getRepository('App\Entity\Niveau01')->calculateSSONiveau01($attributes);
+ $user=$this->submituser($username,$firstname,$lastname,$email,$password,$niveau01,$em);
+ }
+ else
+ $this->updateuser($user,$firstname,$lastname,$email,$avatar,$em);
+
+ // On calcule les groupes de l'utilisateur
+ $user=$em->getRepository('App\Entity\Group')->calculateSSOGroup($user,$attributes);
+
+
+ // Autoconnexion
+ return $this->autoconnexion($user,$redirect,$request);
+ }
+
+ public function loginLDAP(Request $request)
+ {
+ // Création du formulaire
+ $form = $this->createForm(LoginType::class);
+
+ // Récupération des data du formulaire
+ $form->handleRequest($request);
+
+ // Affichage du formulaire
+ return $this->render("Home/loginLDAP.html.twig", [
+ "useheader"=>false,
+ "usemenu"=>false,
+ "usesidebar"=>false,
+ "form"=>$form->createView(),
+ ]);
+ }
+
+ public function loginldapcheck(Request $request, AuthenticationUtils $authenticationUtils,ManagerRegistry $em)
+ {
+
+ $username=$request->get('login')["username"];
+ $password=$request->get('login')["password"];
+ $appMasteridentity=$this->getParameter("appMasteridentity");
+
+ // Récupération de la cible de navigation
+ $redirect = $request->getSession()->get("_security.main.target_path");
+
+ // L'utilisateur se co à l'annuaire
+ $userldap=$this->ldapservice->userconnect($username,$password);
+ if($userldap) {
+ $userldap=$userldap[0];
+
+ // Init
+ $email = "$username@nomail.fr";
+ $lastname = $username;
+ $firstname = " ";
+ $avatar="noavatar.png";
+
+ // Rechercher l'utilisateur
+ if(isset($userldap[$this->getParameter('ldapFirstname')]))
+ $firstname = $userldap[$this->getParameter('ldapFirstname')];
+
+ if(isset($userldap[$this->getParameter('ldapLastname')]))
+ $lastname = $userldap[$this->getParameter('ldapLastname')];
+
+ if(isset($userldap[$this->getParameter('ldapEmail')]))
+ $email = $userldap[$this->getParameter('ldapEmail')];
+
+ if(isset($userldap[$this->getParameter('ldapAvatar')]))
+ $avatar = $userldap[$this->getParameter('ldapAvatar')];
+
+ $user = $em->getRepository('App\Entity\User')->findOneBy(array("username"=>$username));
+
+ if (!$user) {
+ $niveau01=$em->getRepository('App\Entity\Niveau01')->calculateLDAPNiveau01($username);
+ $user=$this->submituser($username,$firstname,$lastname,$email,$avatar,$niveau01,$em);
+ }
+ else {
+ $this->updateuser($user,$firstname,$lastname,$email,$avatar,$em);
+ }
+
+ // Autoconnexion
+ return $this->autoconnexion($user,$redirect,$request);
+ }
+
+ return $this->redirect($this->generateUrl('app_login'));
+ }
+
+
+ public function loginOPENID(Request $request, AuthenticationUtils $authenticationUtils)
+ {
+ $callback=$this->generateUrl('app_loginopenidcallback', array(), UrlGeneratorInterface::ABSOLUTE_URL);
+ $url=$this->getParameter("oauthLoginurl")."?client_id=".$this->getParameter("oauthClientid")."&redirect_uri=".$callback."&response_type=code&state=STATE&scope=openid";
+ return $this->redirect($url);
+ }
+
+ public function loginopenidcallback(Request $request, AuthenticationUtils $authenticationUtils,ManagerRegistry $em)
+ {
+ // Récupération de la cible de navigation
+ $redirect = $request->getSession()->get("_security.main.target_path");
+
+ // Masteridentity
+ $appMasteridentity=$this->getParameter("appMasteridentity");
+
+ $callback=$this->generateUrl('app_loginopenidcallback', array(), UrlGeneratorInterface::ABSOLUTE_URL);
+ $apiurl = $this->getParameter("oauthTokenurl");
+ $query= [
+ "grant_type" => "authorization_code",
+ "code" => $request->get("code"),
+ "redirect_uri" => $callback,
+ "client_id" => $this->getParameter("oauthClientid"),
+ "client_secret" => $this->getParameter("oauthClientsecret"),
+ ];
+ $response=$this->apiservice->run("POST",$apiurl,$query);
+
+ if(!$response||$response->code!="200") return $this->logout($request);
+ $token=$response->body->access_token;
+ $request->getSession()->set("oauthToken",$token);
+
+ $apiurl = $this->getParameter("oauthUserinfo");
+ $response=$this->apiservice->run("GET",$apiurl,null,["Authorization"=>"token ".$token]);
+ if(!$response||$response->code!="200") return $this->logout($request);
+
+ $attributes=json_decode(json_encode($response->body), true);
+
+ // Username
+ if(isset($attributes[$this->getParameter('oauthUsername')]))
+ $username = $attributes[$this->getParameter('oauthUsername')];
+
+ // Valeur par défaut
+ $email = "$username@nomail.fr";
+ $lastname = $username;
+ $firstname = " ";
+ $avatar="noavatar.png";
+
+ // Récupérer les attributs associés
+ if(isset($attributes[$this->getParameter('oauthEmail')]))
+ $email = $attributes[$this->getParameter('oauthEmail')];
+
+ if(isset($attributes[$this->getParameter('oauthLastname')]))
+ $lastname = $attributes[$this->getParameter('oauthLastname')];
+
+ if(isset($attributes[$this->getParameter('oauthFirstname')]))
+ $firstname = $attributes[$this->getParameter('oauthFirstname')];
+
+ if(isset($attributes[$this->getParameter('oauthAvatar')]))
+ $avatar = $attributes[$this->getParameter('oauthAvatar')];
+
+ // Génération auto des niveau01s et des groupes en fonction des attributs sso
+ $this->submitSSONiveau01($attributes,$em);
+ $this->submitSSOGroup($attributes,$em);
+
+ // Rechercher l'utilisateur
+ $user = $em->getRepository('App\Entity\User')->findOneBy(array("username"=>$username));
+ if (!$user) {
+ $niveau01=$em->getRepository('App\Entity\Niveau01')->calculateSSONiveau01($attributes);
+ $user=$this->submituser($username,$firstname,$lastname,$email,$avatar,$niveau01,$em);
+ }
+ else
+ $this->updateuser($user,$firstname,$lastname,$email,$avatar,$em);
+
+ // On calcule les groupes de l'utilisateur
+ $user=$em->getRepository('App\Entity\Group')->calculateSSOGroup($user,$attributes);
+
+ // Autoconnexion
+ return $this->autoconnexion($user,$redirect,$request);
+ }
+
+ public function logout(Request $request) {
+ $auth_mode=$this->getParameter("appAuth");
+ switch($auth_mode) {
+ case "SQL":
+ return $this->logoutSQL($request);
+ break;
+
+ case "CAS":
+ return $this->logoutCAS($request);
+ break;
+
+ case "LDAP":
+ return $this->logoutLDAP($request);
+ break;
+
+ case "OPENID":
+ return $this->logoutOPENID($request);
+ break;
+ }
+
+ }
+
+ public function logoutSQL(Request $request) {
+ $this->tokenstorage->setToken(null);
+ $request->getSession()->invalidate();
+ return $this->redirect($this->generateUrl("app_home"));
+ }
+
+ public function logoutCAS(Request $request) {
+ $this->tokenstorage->setToken(null);
+ $request->getSession()->invalidate();
+
+ // Init Client CAS
+ $alias=$this->getParameter('appAlias');
+ \phpCAS::setDebug($this->appKernel->getProjectDir()."/var/log/cas.log");
+ \phpCAS::client(CAS_VERSION_2_0, $this->getParameter('casHost'), intval($this->getParameter('casPort')), is_null($this->getParameter('casPath')) ? '' : $this->getParameter('casPath'), false);
+ \phpCAS::setNoCasServerValidation();
+
+
+ // Logout
+ $url=$this->generateUrl('app_home', array(), UrlGeneratorInterface::ABSOLUTE_URL);
+ \phpCAS::logout(array("service"=>$url));
+
+ return true;
+ }
+
+ public function logoutLDAP(Request $request) {
+ $this->tokenstorage->setToken(null);
+ $request->getSession()->invalidate();
+ return $this->redirect($this->generateUrl("app_home"));
+ }
+
+
+ public function logoutOPENID(Request $request) {
+ $token=$request->getSession()->get("oauthToken");
+ $this->tokenstorage->setToken(null);
+ $request->getSession()->invalidate();
+
+ $url=$this->getParameter("oauthLogouturl");
+ if($url) {
+ $url.="?id_token_hint=$token&state=openid&post_logout_redirect_uri=http://127.0.0.1:8000";
+ return $this->redirect($url);
+ } else return $this->redirect($this->generateUrl("app_home"));
+ }
+
+ // Génération automatique des niveau01 provenant de l'attribut casniveau01
+ private function submitSSONiveau01($attributes,ManagerRegistry $em) {
+ $attrNiveau01=($this->getParameter("appAuth")=="CAS"?$this->getParameter('casNiveau01'):$this->getParameter('oauthNiveau01'));
+ if(!$attrNiveau01)
+ return null;
+
+ // Si l'utilisateur possège l'attribut niveau01 dans ses attributs
+ if(array_key_exists($attrNiveau01,$attributes)) {
+ if(!is_array($attributes[$attrNiveau01])) {
+ $attributes[$attrNiveau01]=[$attributes[$attrNiveau01]];
+ }
+
+ foreach($attributes[$attrNiveau01] as $ssoniveau01) {
+ $basedn=$this->getParameter('ldapBasedn');
+ $name=$ssoniveau01;
+ if($basedn!="") {
+ // Si présence du basedn dans le nom du groupe = nous sommes en présence d'un DN = on récupere donc comme nom que son cn
+ if(stripos($name,$basedn)!==false) {
+ $tbname=explode(",",$name);
+ $tbname=explode("=",$tbname[0]);
+ $name=$tbname[1];
+ }
+ }
+
+ // Recherche du groupe
+ $niveau01=$em->getRepository("App\Entity\Niveau01")->findOneBy(["label"=>$name]);
+ if(!$niveau01) {
+ $niveau01=new Niveau01();
+ $niveau01->setLabel($name);
+ $niveau01->setApikey(Uuid::uuid4());
+ }
+
+ $niveau01->setAttributes('{"'.$attrNiveau01.'":"'.$ssoniveau01.'"}');
+ $em->getManager()->persist($niveau01);
+ $em->getManager()->flush();
+ }
+ }
+ }
+
+
+ // Génération automatique des groupes provenant de l'attribut casgroup ou oauthgroup
+ private function submitSSOGroup($attributes,ManagerRegistry $em) {
+ $attrGroup=($this->getParameter("appAuth")=="CAS"?$this->getParameter('casGroup'):$this->getParameter('oauthGroup'));
+ if(!$attrGroup)
+ return null;
+
+ // Si l'utilisateur possège l'attribut groupe dans ses attributs
+ if(array_key_exists($attrGroup,$attributes)) {
+ if(!is_array($attributes[$attrGroup])) {
+ $attributes[$attrGroup]=[$attributes[$attrGroup]];
+ }
+
+ foreach($attributes[$attrGroup] as $ssogroup) {
+ $basedn=$this->getParameter('ldapBasedn');
+ $name=$ssogroup;
+ if($basedn!="") {
+ // Si présence du basedn dans le nom du groupe = nous sommes en présence d'un DN = on récupere donc comme nom que son cn
+ if(stripos($name,$basedn)!==false) {
+ $tbname=explode(",",$name);
+ $tbname=explode("=",$tbname[0]);
+ $name=$tbname[1];
+ }
+ }
+
+ // Recherche du groupe
+ $group=$em->getRepository("App\Entity\Group")->findOneBy(["label"=>$name]);
+ if(!$group) {
+ $group=new Group();
+ $group->setLabel($name);
+ $group->setIsopen(false);
+ $group->setIsworkgroup(false);
+ $group->setApikey(Uuid::uuid4());
+ }
+
+ $group->setAttributes('{"'.$attrGroup.'":"'.$ssogroup.'"}');
+ $em->getManager()->persist($group);
+ $em->getManager()->flush();
+ }
+ }
+ }
+
+ private function submituser($username,$firstname,$lastname,$email,$avatar,$niveau01,$em) {
+ if(empty($email)) $email = $username."@nomail.com";
+ if(empty($avatar)) $avatar = "noavatar.png";
+ if(empty($firstname)) $firstname = " ";
+ if(empty($lastname)) $lastname = $username;
+ $password=$this->getParameter("appAuth")."PWD-".$username;
+
+ // Si aucun niveau01 on prend par défaut le niveau system
+ if(!$niveau01) $niveau01=$em->getRepository('App\Entity\Niveau01')->find(-1);
+
+ // Autogénération du user vu qu'il a pu se connecter
+ $user = new User();
+
+ $user->setUsername($username);
+ $user->setEmail($email);
+ $user->setLastname($lastname);
+ $user->setFirstname($firstname);
+ $user->setApikey(Uuid::uuid4());
+
+ $user->setPassword($password);
+ $user->setNiveau01($niveau01);
+
+ $user->setAvatar($avatar);
+ $user->setIsvisible(true);
+ $user->setRole("ROLE_USER");
+
+ if(in_array($username,$this->getParameter("appAdmins")))
+ $user->setRole("ROLE_ADMIN");
+
+ $em->getManager()->persist($user);
+ $em->getManager()->flush();
+
+ return $user;
+ }
+
+ private function updateuser($user,$firstname,$lastname,$email,$avatar,$em) {
+ if($avatar=="noavatar.png") $avatar=$user->getAvatar();
+
+ if(!empty($lastname)) $user->setLastname($lastname);
+ if(!empty($firstname)) $user->setFirstname($firstname);
+ if(!empty($email)) $user->setEmail($email);
+ if(!empty($avatar)) $user->setAvatar($avatar);
+
+ if(in_array($user->getUsername(),$this->getParameter("appAdmins")))
+ $user->setRole("ROLE_ADMIN");
+
+ $em->getManager()->flush();
+ }
+
+ private function autoconnexion($user,$redirect,Request $request)
+ {
+ // Récupérer le token de l'utilisateur
+ $token = new UsernamePasswordToken($user, "main", $user->getRoles());
+ $this->tokenstorage->setToken($token);
+ $request->getSession()->set('_security_main', serialize($token));
+
+ // Simuler l'evenement de connexion
+ $event = new InteractiveLoginEvent($request, $token);
+ $dispatcher = new EventDispatcher();
+ $dispatcher->dispatch($event);
+
+ // Redirection
+ if($redirect)
+ return $this->redirect($redirect);
+ else
+ return $this->redirect($this->generateUrl('app_home'));
+ }
+}
diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php
new file mode 100644
index 0000000..f50f60a
--- /dev/null
+++ b/src/Controller/UserController.php
@@ -0,0 +1,670 @@
+getSession()->get("showannuaire"))
+ throw $this->createAccessDeniedException('Permission denied');
+
+ return $this->render($this->twig.'list.html.twig',[
+ "useheader"=>true,
+ "usemenu"=>false,
+ "usesidebar"=>($access!="user"),
+ "access"=>$access
+ ]);
+ }
+
+ public function tablelist($access, Request $request,ManagerRegistry $em): Response
+ {
+ $query=$request->query->all();
+ $start=$query['start'];
+ $length=$query['length'];
+ $search=$query['search'];
+ $draw= $query['draw'];
+ $ordercolumn=$query['order'][0]['column'];
+ $orderdir=$query['order'][0]['dir'];
+
+
+ // Nombre total d'enregistrement
+ switch($access) {
+ case "admin":
+ $total = $em->getManager()->createQueryBuilder()->select('COUNT(entity)')->from($this->entity,'entity')->getQuery()->getSingleScalarResult();
+ break;
+
+ case "modo":
+ $total = $em->getManager()->createQueryBuilder()
+ ->select('COUNT(entity)')
+ ->from($this->entity,'entity')
+ ->from("App\Entity\UserModo",'usermodo')
+ ->where("usermodo.niveau01 = entity.niveau01")
+ ->andWhere("usermodo.user = :user")
+ ->setParameter("user", $this->getUser())
+ ->getQuery()->getSingleScalarResult();
+ break;
+
+ default:
+ $niveau01=$this->getUser()->getNiveau01();
+ $niveau02=$this->getUser()->getNiveau02();
+
+ $qb=$em->getManager()->createQueryBuilder()->select('COUNT(entity)')->from($this->entity,'entity')->where('entity.isvisible=true');
+ switch($request->getSession()->get("scopeannu")) {
+ case "SAME_NIVEAU01":
+ $qb->andWhere("entity.niveau01 = :niveau01")->setParameter("niveau01",$niveau01);
+ break;
+
+ case "SAME_NIVEAU02":
+ $qb->andWhere("entity.niveau02 = :niveau02")->setParameter("niveau02",$niveau02);
+ break;
+ }
+
+ $total = $qb->getQuery()->getSingleScalarResult();
+ break;
+ }
+
+ // Nombre d'enregistrement filtré
+ if(!$search||$search["value"]=="")
+ $totalf = $total;
+ else {
+ switch($access) {
+ case "admin":
+ $totalf= $em->getManager()->createQueryBuilder()
+ ->select('COUNT(entity)')
+ ->from($this->entity,'entity')
+ ->from('App:Niveau01', 'niveau01')
+ ->where('entity.niveau01=niveau01.id')
+ ->andWhere('entity.username LIKE :value OR entity.firstname LIKE :value OR entity.lastname LIKE :value OR entity.email LIKE :value OR entity.roles LIKE :value OR niveau01.label LIKE :value')
+ ->setParameter("value", "%".$search["value"]."%")
+ ->getQuery()
+ ->getSingleScalarResult();
+ break;
+
+ case "modo":
+ $totalf= $em->getManager()->createQueryBuilder()
+ ->select('COUNT(entity)')
+ ->from($this->entity,'entity')
+ ->from('App:Niveau01', 'niveau01')
+ ->from("App:UserModo",'usermodo')
+ ->where('entity.niveau01=niveau01.id')
+ ->andWhere('entity.username LIKE :value OR entity.firstname LIKE :value OR entity.lastname LIKE :value OR entity.email LIKE :value OR entity.roles LIKE :value OR niveau01.label LIKE :value')
+ ->andWhere("usermodo.niveau01 = entity.niveau01")
+ ->andWhere("usermodo.user = :userid")
+ ->setParameter("value", "%".$search["value"]."%")
+ ->setParameter("userid", $this->getUser()->getId())
+ ->getQuery()
+ ->getSingleScalarResult();
+ break;
+
+ default:
+ $qb = $em->getManager()->createQueryBuilder()
+ ->select('COUNT(entity)')
+ ->from($this->entity,'entity')
+ ->from('App:Niveau01', 'niveau01')
+ ->where('entity.niveau01=niveau01.id')
+ ->andWhere('entity.isvisible=true')
+ ->andWhere('entity.username LIKE :value OR entity.firstname LIKE :value OR entity.lastname LIKE :value OR entity.email LIKE :value OR entity.roles LIKE :value OR niveau01.label LIKE :value')
+ ->setParameter("value", "%".$search["value"]."%");
+
+ switch($request->getSession()->get("scopeannu")) {
+ case "SAME_NIVEAU01":
+ $qb->andWhere("entity.niveau01 = :niveau01")->setParameter("niveau01",$niveau01);
+ break;
+
+ case "SAME_NIVEAU02":
+ $qb->andWhere("entity.niveau02 = :niveau02")->setParameter("niveau02",$niveau02);
+ break;
+ }
+
+ $totalf=$qb->getQuery()->getSingleScalarResult();
+ break;
+ }
+ }
+
+ // Construction du tableau de retour
+ $output = array(
+ 'draw' => $draw,
+ 'recordsFiltered' => $totalf,
+ 'recordsTotal' => $total,
+ 'data' => array(),
+ );
+
+ // Parcours des Enregistrement
+ $qb = $em->getManager()->createQueryBuilder();
+ switch($access) {
+ case "admin":
+ $qb->select('entity')->from($this->entity,'entity')->from('App:Niveau01','niveau01');
+ $qb->where('entity.niveau01=niveau01.id');
+ break;
+
+ case "modo":
+ $qb->select('entity')->from($this->entity,'entity')->from('App:Niveau01','niveau01')->from("App:UserModo",'usermodo');
+ $qb->where('entity.niveau01=niveau01.id');
+ $qb->andWhere("usermodo.niveau01 = entity.niveau01");
+ $qb->andWhere("usermodo.user = :userid");
+ $qb->setParameter("userid", $this->getUser()->getId());
+ break;
+
+ default:
+ $qb->select('entity')->from($this->entity,'entity')->from('App:Niveau01','niveau01');
+ $qb->where('entity.niveau01=niveau01.id');
+ $qb->andWhere('entity.isvisible=true');
+
+ switch($request->getSession()->get("scopeannu")) {
+ case "SAME_NIVEAU01":
+ $qb->andWhere("entity.niveau01 = :niveau01")->setParameter("niveau01",$niveau01);
+ break;
+
+ case "SAME_NIVEAU02":
+ $qb->andWhere("entity.niveau02 = :niveau02")->setParameter("niveau02",$niveau02);
+ break;
+ }
+ break;
+ }
+
+ if($search&&$search["value"]!="") {
+ $qb ->andWhere('entity.username LIKE :value OR entity.firstname LIKE :value OR entity.lastname LIKE :value OR entity.email LIKE :value OR entity.roles LIKE :value OR niveau01.label LIKE :value')
+ ->setParameter("value", "%".$search["value"]."%");
+ }
+
+ if($ordercolumn) {
+ if($access=="admin"||$access=="modo") {
+ $ordercolumn=$ordercolumn-1;
+ }
+
+ switch($ordercolumn) {
+ case 1 :
+ $qb->orderBy('entity.username',$orderdir);
+ break;
+
+ case 2 :
+ $qb->orderBy('entity.lastname',$orderdir);
+ break;
+
+ case 3 :
+ $qb->orderBy('entity.firstname',$orderdir);
+ break;
+
+ case 4 :
+ $qb->orderBy('entity.email',$orderdir);
+ break;
+
+ case 5 :
+ $qb->orderBy('entity.telephonenumber',$orderdir);
+ break;
+
+ case 6 :
+ $qb->orderBy('niveau01.label',$orderdir);
+ break;
+
+ case 8 :
+ $qb->orderBy('entity.visitedate',$orderdir);
+ break;
+
+ case 9 :
+ $qb->orderBy('entity.roles',$orderdir);
+ break;
+ }
+ }
+
+ $datas=$qb->setFirstResult($start)->setMaxResults($length)->getQuery()->getResult();
+
+ foreach($datas as $data) {
+ // Action
+ $action = "";
+ switch($access) {
+ case "admin":
+ $action.="$data->getId()))."'>";
+ break;
+ case "modo":
+ $action.="$data->getId()))."'>";
+ break;
+ }
+
+ // Groupes
+ $groups="";
+ foreach($data->getGroups() as $usergroup) {
+ $groups.=$usergroup->getGroup()->getLabel()."
";
+ }
+
+ // Roles
+ $roles="";
+ foreach($data->getRoles() as $role) {
+ $roles.=$role."
";
+ }
+
+ $tmp=array();
+ if($access=="admin"||$access=="modo") array_push($tmp,$action);
+
+ if(stripos($data->getAvatar(),"http")===0)
+ array_push($tmp,"");
+ else
+ array_push($tmp,"getAvatar()."' class='avatar'>");
+
+ array_push($tmp,$data->getUsername());
+ array_push($tmp,$data->getLastname());
+ array_push($tmp,$data->getFirstname());
+ array_push($tmp,"".$data->getEmail()."");
+ array_push($tmp,$data->getTelephonenumber());
+ array_push($tmp,$data->getNiveau01()->getLabel());
+ array_push($tmp,($data->getNiveau02()?$data->getNiveau02()->getLabel():""));
+ array_push($tmp,($data->getVisitedate()?$data->getVisitedate()->format("d/m/Y H:i")."
nb = ".$data->getVisitecpt():""));
+ array_push($tmp,$roles);
+ array_push($tmp,$groups);
+
+ array_push($output["data"],$tmp);
+ }
+
+ // Retour
+ return new JsonResponse($output);
+ }
+
+ public function selectlist($access, Request $request,ManagerRegistry $em): Response
+ {
+ $output=array();
+ $page_limit=$request->query->get('page_limit');
+ $q=$request->query->get('q');
+
+ $qb = $em->getManager()->createQueryBuilder();
+ $qb->select('entity')->from($this->entity,'entity')
+ ->where('entity.username LIKE :value')
+ ->setParameter("value", "%".$q."%")
+ ->orderBy('entity.username');
+
+ $datas=$qb->setFirstResult(0)->setMaxResults($page_limit)->getQuery()->getResult();
+ foreach($datas as $data) {
+ array_push($output,array("id"=>$data->getId(),"text"=>$data->getUsername()));
+ }
+
+ $ret_string["results"]=$output;
+ $response = new JsonResponse($ret_string);
+ return $response;
+ }
+
+ public function submit($access, Request $request,ManagerRegistry $em): Response
+ {
+ // Vérifier que l'on puisse créer
+ if($this->getParameter("appMasteridentity")!="SQL" && $this->getParameter("appSynchroPurgeUser"))
+ throw $this->createNotFoundException('Permission denied');
+
+ // Controler les permissions
+ $this->cansubmit($access,$em);
+
+ // Initialisation de l'enregistrement
+ $data = new Entity();
+ $data->setAvatar("noavatar.png");
+ $data->setIsvisible(true);
+ $data->setApikey(Uuid::uuid4());
+
+ // Création du formulaire
+ $form = $this->createForm(Form::class,$data,array(
+ "mode"=>"submit",
+ "access"=>$access,
+ "userid"=>$this->getUser()->getId(),
+ "appMasteridentity"=>$this->GetParameter("appMasteridentity"),
+ "appNiveau01label"=>$this->GetParameter("appNiveau01label"),
+ "appNiveau02label"=>$this->GetParameter("appNiveau02label"),
+ ));
+
+ // Récupération des data du formulaire
+ $form->handleRequest($request);
+
+ // Sur validation
+ if ($form->get('submit')->isClicked() && $form->isValid()) {
+ $data = $form->getData();
+
+ // S'assurer que les modos ne donne pas des ROLE_ADMIN ou ROLE_USER au user qu'il submit
+ if($access=="modo") {
+ $roles=$data->getRoles();
+ $roles=array_diff($roles,["ROLE_ADMIN","ROLE_MODO"]);
+ $data->setRoles($roles);
+ }
+
+
+
+ // On récupère les groupes et on cacule ceux à ajouter ou à supprimer
+ $lstgroups=array_filter(explode(",",$form->get("linkgroups")->getData()));
+ $lstmodos=array_filter(explode(",",$form->get("linkmodos")->getData()));
+
+ // Sauvegarde
+ $em->getManager()->persist($data);
+ $em->getManager()->flush();
+
+ // Ajout des groupes
+ foreach($lstgroups as $idgroup) {
+ $group=$em->getRepository("App\Entity\Group")->find($idgroup);
+ $usergroup=$em->getRepository('App\Entity\UserGroup')->findBy(["user"=>$data,"group"=>$group]);
+ if(!$usergroup) {
+ $usergroup= new UserGroup();
+ $usergroup->setUser($data);
+ $usergroup->setGroup($group);
+ $usergroup->setApikey(Uuid::uuid4());
+ $usergroup->setRolegroup(0);
+
+ $em->getManager()->persist($usergroup);
+ $em->getManager()->flush();
+ }
+ }
+
+ // Ajout des modos
+ foreach($lstmodos as $idmodo) {
+ $niveau01=$em->getRepository("App\Entity\Niveau01")->find($idmodo);
+ $usermodo=$em->getRepository('App\Entity\UserModo')->findBy(["user"=>$data,"niveau01"=>$niveau01]);
+ if(!$usermodo) {
+ $usermodo= new UserModo();
+ $usermodo->setUser($data);
+ $usermodo->setNiveau01($dataniveau01);
+
+ $em->getManager()->persist($usermodo);
+ $em->getManager()->flush();
+ }
+ }
+
+ // Retour à la liste
+ return $this->redirectToRoute(str_replace("_admin_","_".$access."_",$this->route));
+ }
+
+ // Affichage du formulaire
+ return $this->render($this->twig.'edit.html.twig', [
+ "useheader"=>true,
+ "usemenu"=>false,
+ "usesidebar"=>true,
+ "access"=>$access,
+ "mode"=>"submit",
+ "form"=>$form->createView(),
+ $this->data=>$data,
+ "listgroups"=>$this->getListGroups("admin",$em),
+ "listmodos"=> $this->getListModos($em)
+ ]);
+ }
+
+ public function profil($access,Request $request,ManagerRegistry $em): Response
+ {
+ $id=$this->getUser()->getId();
+ return $this->update($access,$id,$request,$em);
+ }
+
+ public function update($access,$id,Request $request,ManagerRegistry $em): Response
+ {
+ // Initialisation de l'enregistrement
+ $data=$em->getRepository($this->entity)->find($id);
+ if (!$data) throw $this->createNotFoundException('Unable to find entity.');
+
+ // Controler les permissions
+ $this->canupdate($access,$data,$em);
+
+ // Récupération de l'ancien password
+ $oldpassword=$data->getPassword();
+
+ // Récuparation des groupes associés
+ $oldlstgroups=[];
+ foreach($data->getGroups() as $group){
+ $oldlstgroups[] = $group->getGroup()->getId();
+ }
+
+ // Récuparation des modos associés
+ $oldlstmodos=[];
+ foreach($data->getModos() as $modo){
+ $oldlstmodos[] = $modo->getNiveau01()->getId();
+ }
+
+ // Création du formulaire
+ $form = $this->createForm(Form::class,$data,array(
+ "mode"=>"update",
+ "access"=>$access,
+ "userid"=>$this->getUser()->getId(),
+ "appMasteridentity"=>$this->GetParameter("appMasteridentity"),
+ "appNiveau01label"=>$this->GetParameter("appNiveau01label"),
+ "appNiveau02label"=>$this->GetParameter("appNiveau02label"),
+ ));
+
+ // Récupération des data du formulaire
+ $form->handleRequest($request);
+
+ // Sur validation
+ if ($form->get('submit')->isClicked() && $form->isValid()) {
+ $data = $form->getData();
+
+ // S'assurer que les modos ne donne pas des ROLE_ADMIN ou ROLE_USER au user qu'il update
+ if($access=="modo") {
+ $roles=$data->getRoles();
+ $roles=array_diff($roles,["ROLE_ADMIN","ROLE_MODO"]);
+ $data->setRoles($roles);
+ }
+
+ // Si pas de changement de password on replace l'ancien
+ if($data->getPassword()=="") {
+ $data->setPassword($oldpassword);
+ }
+ // Sinon on encode le nouveau
+ else {
+ $data->setPassword($data->getPassword());
+ }
+
+ // Sauvegarde
+ $em->getManager()->flush();
+
+ // On récupère les groupes et on cacule ceux à ajouter ou à supprimer
+ $lstgroups=array_filter(explode(",",$form->get("linkgroups")->getData()));
+ $removegroups=array_diff($oldlstgroups,$lstgroups);
+ $addgroups=array_diff($lstgroups,$oldlstgroups);
+
+ // Ajout des nouveaux groupes
+ foreach($addgroups as $idgroup) {
+ $group=$em->getRepository("App\Entity\Group")->find($idgroup);
+ $usergroup=$em->getRepository('App\Entity\UserGroup')->findOneBy(["user"=>$data,"group"=>$group]);
+ if(!$usergroup) {
+ $usergroup= new UserGroup();
+ $usergroup->setUser($data);
+ $usergroup->setGroup($group);
+ $usergroup->setApikey(Uuid::uuid4());
+ $usergroup->setRolegroup(0);
+ $em->getManager()->persist($usergroup);
+ $em->getManager()->flush();
+ }
+ }
+
+ // Suppression des groupes obsolètes
+ foreach($removegroups as $idgroup) {
+ $group=$em->getRepository("App\Entity\Group")->find($idgroup);
+ $usergroup=$em->getRepository('App\Entity\UserGroup')->findOneBy(["user"=>$data,"group"=>$group]);
+ if($usergroup) {
+ $em->getManager()->remove($usergroup);
+ $em->getManager()->flush();
+ }
+ }
+
+ // On récupère les modos et on cacule ceux à ajouter ou à supprimer
+ $linkmodos=array_filter(explode(",",$form->get("linkmodos")->getData()));
+ $removemodos=array_diff($oldlstmodos,$linkmodos);
+ $addmodos=array_diff($linkmodos,$oldlstmodos);
+
+
+ // Ajout des nouveaux modos
+ foreach($addmodos as $idmodo) {
+ $niveau01=$em->getRepository("App\Entity\Niveau01")->find($idmodo);
+ $usermodo=$em->getRepository('App\Entity\UserModo')->findOneBy(["user"=>$data,"niveau01"=>$niveau01]);
+ if(!$usermodo) {
+ $usermodo= new UserModo();
+ $usermodo->setUser($data);
+ $usermodo->setNiveau01($niveau01);
+ $em->getManager()->persist($usermodo);
+ $em->getManager()->flush();
+ }
+ }
+
+ // Suppression des modos obsolètes
+ foreach($removemodos as $idmodo) {
+ $niveau01=$em->getRepository("App\Entity\Niveau01")->find($idmodo);
+ $usermodo=$em->getRepository('App\Entity\UserModo')->findOneBy(["user"=>$data,"niveau01"=>$niveau01]);
+ if($usermodo) {
+ $em->getManager()->remove($usermodo);
+ $em->getManager()->flush();
+ }
+ }
+
+ // Retour à la liste
+ if($access=="user")
+ return $this->redirectToRoute("app_home");
+ else
+ return $this->redirectToRoute(str_replace("_admin_","_".$access."_",$this->route));
+ }
+
+ // Affichage du formulaire
+ return $this->render($this->twig.'edit.html.twig', [
+ "useheader"=>true,
+ "usemenu"=>false,
+ "usesidebar"=>($access=="admin"),
+ "access"=>$access,
+ "mode"=>"update",
+ "form"=>$form->createView(),
+ $this->data=>$data,
+ "listgroups"=>$this->getListGroups($access,$em),
+ "listmodos"=> $this->getListModos($em),
+ "maxsize"=>($access=="user"?1200:null),
+ ]);
+ }
+
+ public function delete($access,$id,Request $request,ManagerRegistry $em): Response
+ {
+ // Récupération de l'enregistrement courant
+ $data=$em->getRepository($this->entity)->find($id);
+ if (!$data) throw $this->createNotFoundException('Unable to find entity.');
+
+ // Controler les permissions
+ $this->candelete($access,$data,$em);
+
+ // Tentative de suppression
+ try{
+ $em->getManager()->remove($data);
+ $em->getManager()->flush();
+ }
+ catch (\Exception $e) {
+ $request->getSession()->getFlashBag()->add("error", $e->getMessage());
+ return $this->redirectToRoute(str_replace("_admin_","_".$access."_",$this->route)."_update",["id"=>$id]);
+ }
+
+ return $this->redirectToRoute(str_replace("_admin_","_".$access."_",$this->route));
+ }
+
+ protected function getListGroups($access,$em)
+ {
+ $qb=$em->getManager()->createQueryBuilder();
+ $qb->select('b')->from('App:Group','b');
+ if($access!="admin") $qb->where("b.isopen=true AND b.isworkgroup=true");
+ $qb->andWhere("b.ldapfilter IS NULL");
+ $qb->andWhere("b.attributes IS NULL");
+ $qb->andWhere("b.id>0");
+ $datas=$qb->getQuery()->getResult();
+ return $datas;
+ }
+
+ protected function getListModos($em)
+ {
+ $qb=$em->getManager()->createQueryBuilder();
+ $qb->select('b')->from('App:Niveau01','b');
+ $datas=$qb->getQuery()->getResult();
+ return $datas;
+ }
+
+
+ private function cansubmit($access,$em) {
+ switch($access) {
+ case "admin" : return true; break;
+ case "modo" : return true; break;
+ }
+ throw $this->createAccessDeniedException('Permission denied');
+ }
+
+
+ private function canupdate($access,$entity,$em) {
+ switch($access) {
+ case "admin" : return true; break;
+ case "modo" :
+ $usermodo=$em->getRepository("App\Entity\UserModo")->findOneBy(["user"=>$this->getUser(),"niveau01"=>$entity->getNiveau01()]);
+ if(!$usermodo) throw $this->createAccessDeniedException('Permission denied');
+ return true;
+ break;
+ case "user" :
+ if($this->getUser()->getId()!=$entity->getId()) throw $this->createAccessDeniedException('Permission denied');
+ return true;
+ break;
+ }
+ throw $this->createAccessDeniedException('Permission denied');
+ }
+
+ private function candelete($access,$entity,$em) {
+ switch($access) {
+ case "admin" : return true; break;
+ case "modo" :
+ $usermodo=$em->getRepository("App\Entity\UserModo")->findOneBy(["user"=>$this->getUser(),"niveau01"=>$entity->getNiveau01()]);
+ if(!$usermodo) throw $this->createAccessDeniedException('Permission denied');
+
+ if($entity->hasRole("ROLE_ADMIN")||$entity->hasRole("ROLE_MODO")) throw $this->createAccessDeniedException('Permission denied');
+ return true;
+ break;
+ case "user" :
+ if($this->getUser()->getId()!=$entity->getId()) throw $this->createAccessDeniedException('Permission denied');
+ return true;
+ break;
+ }
+ throw $this->createAccessDeniedException('Permission denied');
+ }
+
+ public function preference($access,Request $request,ManagerRegistry $em): Response
+ {
+ $key=$request->request->get('key');
+ $id=$request->request->get('id');
+ $value=$request->request->get('value');
+
+ // Récupérer les préférences de l'utilisateur
+ $preference=$this->getUser()->getPreference();
+
+ // Mise à jour de la préférence
+ $toupdate=false;
+ if(!is_array($preference)) {
+ $toupdate=true;
+ $preference=[];
+ }
+
+ if(!array_key_exists($key,$preference)) {
+ $toupdate=true;
+ $preference[$key]=[];
+ }
+ if((!array_key_exists($id,$preference[$key]))) {
+ $toupdate=true;
+ $preference[$key][$id]=$value;
+ }
+ if($preference[$key][$id]!=$value) {
+ $toupdate=true;
+ $preference[$key][$id]=$value;
+ }
+
+ // Mise à jour des préferences
+ if($toupdate) {
+ $this->getUser()->setPreference($preference);
+ $em->getManager()->flush();
+ }
+
+ return new Response();
+ }
+}
\ No newline at end of file
diff --git a/src/Controller/WhitelistController.php b/src/Controller/WhitelistController.php
new file mode 100644
index 0000000..cb73aff
--- /dev/null
+++ b/src/Controller/WhitelistController.php
@@ -0,0 +1,199 @@
+render($this->twig.'list.html.twig',[
+ "useheader"=>true,
+ "usemenu"=>false,
+ "usesidebar"=>true,
+ ]);
+ }
+
+ public function tablelist(Request $request,ManagerRegistry $em): Response
+ {
+ $query=$request->query->all();
+ $start=$query['start'];
+ $length=$query['length'];
+ $search=$query['search'];
+ $draw= $query['draw'];
+ $ordercolumn=$query['order'][0]['column'];
+ $orderdir=$query['order'][0]['dir'];
+
+ // Nombre total d'enregistrement
+ $total = $em->getManager()->createQueryBuilder()->select('COUNT(entity)')->from($this->entity,'entity')->getQuery()->getSingleScalarResult();
+
+ // Nombre d'enregistrement filtré
+ if(!$search||$search["value"]=="")
+ $totalf = $total;
+ else {
+ $totalf= $em->getManager()->createQueryBuilder()
+ ->select('COUNT(entity)')
+ ->from($this->entity,'entity')
+ ->where('entity.label LIKE :value')
+ ->setParameter("value", "%".$search["value"]."%")
+ ->getQuery()
+ ->getSingleScalarResult();
+ }
+
+ // Construction du tableau de retour
+ $output = array(
+ 'draw' => $draw,
+ 'recordsFiltered' => $totalf,
+ 'recordsTotal' => $total,
+ 'data' => array(),
+ );
+
+ // Parcours des Enregistrement
+ $qb = $em->getManager()->createQueryBuilder();
+ $qb->select('entity')->from($this->entity,'entity');
+ if($search&&$search["value"]!="") {
+ $qb ->andWhere('entity.label LIKE :value')
+ ->setParameter("value", "%".$search["value"]."%");
+ }
+
+ if($ordercolumn) {
+ switch($ordercolumn) {
+ case 1 :
+ $qb->orderBy('entity.label',$orderdir);
+ break;
+ }
+ }
+
+ $datas=$qb->setFirstResult($start)->setMaxResults($length)->getQuery()->getResult();
+
+ foreach($datas as $data) {
+ // Action
+ $action = "";
+ $action.="$data->getId()))."'>";
+
+ $tmp=array();
+ array_push($tmp,$action);
+ array_push($tmp,$data->getLabel());
+
+ if($this->getParameter("appMasteridentity")=="LDAP"||$this->getParameter("appSynchro")=="LDAP2NINE") array_push($tmp,$data->getLdapfilter());
+ if($this->getParameter("appMasteridentity")=="SSO") array_push($tmp,$data->getAttributes());
+
+ array_push($output["data"],$tmp);
+ }
+
+ // Retour
+ return new JsonResponse($output);
+ }
+
+ public function submit(Request $request,ManagerRegistry $em): Response
+ {
+ // Initialisation de l'enregistrement
+ $data = new Entity();
+
+ // Création du formulaire
+ $form = $this->createForm(Form::class,$data,array("mode"=>"submit"));
+
+ // Récupération des data du formulaire
+ $form->handleRequest($request);
+
+ // Sur validation
+ if ($form->get('submit')->isClicked() && $form->isValid()) {
+ $data = $form->getData();
+
+ // Sauvegarde
+ $em->getManager()->persist($data);
+ $em->getManager()->flush();
+
+ // Retour à la liste
+ return $this->redirectToRoute($this->route);
+ }
+
+ // Affichage du formulaire
+ return $this->render($this->twig.'edit.html.twig', [
+ "useheader"=>true,
+ "usemenu"=>false,
+ "usesidebar"=>true,
+ "mode"=>"submit",
+ "form"=>$form->createView(),
+ $this->data=>$data,
+ ]);
+ }
+
+ public function update($id,Request $request,ManagerRegistry $em): Response
+ {
+ // Initialisation de l'enregistrement
+ $data=$em->getRepository($this->entity)->find($id);
+ if (!$data) throw $this->createNotFoundException('Unable to find entity.');
+
+ // Création du formulaire
+ $form = $this->createForm(Form::class,$data,array("mode"=>"update"));
+
+ // Récupération des data du formulaire
+ $form->handleRequest($request);
+
+ // Sur validation
+ if ($form->get('submit')->isClicked() && $form->isValid()) {
+ $data = $form->getData();
+ $em->getManager()->flush();
+
+ // Retour à la liste
+ return $this->redirectToRoute($this->route);
+ }
+
+ // Affichage du formulaire
+ return $this->render($this->twig.'edit.html.twig', [
+ 'useheader' => true,
+ 'usemenu' => false,
+ 'usesidebar' => true,
+ $this->data => $data,
+ 'mode' => 'update',
+ 'form' => $form->createView()
+ ]);
+ }
+
+ public function delete($id,Request $request,ManagerRegistry $em): Response
+ {
+ // Récupération de l'enregistrement courant
+ $data=$em->getRepository($this->entity)->find($id);
+ if (!$data) throw $this->createNotFoundException('Unable to find entity.');
+
+ // Tentative de suppression
+ try{
+ $em->getManager()->remove($data);
+ $em->getManager()->flush();
+ }
+ catch (\Exception $e) {
+ $request->getSession()->getFlashBag()->add("error", $e->getMessage());
+ return $this->redirectToRoute($this->route."_update",["id"=>$id]);
+ }
+
+ return $this->redirectToRoute($this->route);
+ }
+
+ public function is(Request $request,ManagerRegistry $em)
+ {
+ $email=$request->request->get('email');
+ $email=explode("@",$email);
+ $domaine=end($email);
+
+ // Rechercher le mail dans la liste blanche
+ $whitelist=$em->getRepository($this->entity)->findOneBy(["label"=>$domaine]);
+ if($whitelist)
+ return new Response("OK", 200);
+ else
+ return new Response("KO", 200);
+ }
+}
diff --git a/src/Entity/.gitignore b/src/Entity/.gitignore
new file mode 100644
index 0000000..e69de29
diff --git a/src/Entity/Config.php b/src/Entity/Config.php
new file mode 100644
index 0000000..1701cd8
--- /dev/null
+++ b/src/Entity/Config.php
@@ -0,0 +1,227 @@
+id = $id;
+
+ return $this;
+ }
+
+ public function getValue(): ?string
+ {
+ if($this->value=="") return $this->default;
+ else return $this->value;
+ }
+
+ //== FIN DU CODE A NE PAS REGENERER
+
+ public function getId(): ?string
+ {
+ return $this->id;
+ }
+
+
+ public function getTitle(): ?string
+ {
+ return $this->title;
+ }
+
+ public function setTitle(string $title): self
+ {
+ $this->title = $title;
+
+ return $this;
+ }
+
+ public function setValue(?string $value): self
+ {
+ $this->value = $value;
+
+ return $this;
+ }
+
+ public function getDefault(): ?string
+ {
+ return $this->default;
+ }
+
+ public function setDefault(string $default): self
+ {
+ $this->default = $default;
+
+ return $this;
+ }
+
+ public function getOrder(): ?string
+ {
+ return $this->order;
+ }
+
+ public function setOrder(string $order): self
+ {
+ $this->order = $order;
+
+ return $this;
+ }
+
+ public function isVisible(): ?bool
+ {
+ return $this->visible;
+ }
+
+ public function setVisible(bool $visible): self
+ {
+ $this->visible = $visible;
+
+ return $this;
+ }
+
+ public function isChangeable(): ?bool
+ {
+ return $this->changeable;
+ }
+
+ public function setChangeable(bool $changeable): self
+ {
+ $this->changeable = $changeable;
+
+ return $this;
+ }
+
+ public function isRequired(): ?bool
+ {
+ return $this->required;
+ }
+
+ public function setRequired(bool $required): self
+ {
+ $this->required = $required;
+
+ return $this;
+ }
+
+ public function getType(): ?string
+ {
+ return $this->type;
+ }
+
+ public function setType(string $type): self
+ {
+ $this->type = $type;
+
+ return $this;
+ }
+
+ public function getGrouped(): ?string
+ {
+ return $this->grouped;
+ }
+
+ public function setGrouped(string $grouped): self
+ {
+ $this->grouped = $grouped;
+
+ return $this;
+ }
+
+ public function getCategory(): ?string
+ {
+ return $this->category;
+ }
+
+ public function setCategory(string $category): self
+ {
+ $this->category = $category;
+
+ return $this;
+ }
+
+ public function getHelp(): ?string
+ {
+ return $this->help;
+ }
+
+ public function setHelp(string $help): self
+ {
+ $this->help = $help;
+
+ return $this;
+ }
+}
diff --git a/src/Entity/Cron.php b/src/Entity/Cron.php
new file mode 100644
index 0000000..805ae8d
--- /dev/null
+++ b/src/Entity/Cron.php
@@ -0,0 +1,210 @@
+id = $id;
+ return $this;
+ }
+
+ public function __construct()
+ {
+ $this->submitdate = new \DateTime();
+ }
+
+ // A garder pour récupérer le label du statut
+ public function getStatutLabel()
+ {
+ switch($this->statut) {
+ case -1: return "Désactivé"; break;
+ case 0: return "KO"; break;
+ case 1: return "OK"; break;
+ }
+ }
+
+ public function getId(): ?int
+ {
+ return $this->id;
+ }
+
+ public function getCommand(): ?string
+ {
+ return $this->command;
+ }
+
+ public function setCommand(string $command): self
+ {
+ $this->command = $command;
+
+ return $this;
+ }
+
+ public function getDescription(): ?string
+ {
+ return $this->description;
+ }
+
+ public function setDescription(?string $description): self
+ {
+ $this->description = $description;
+
+ return $this;
+ }
+
+ public function getStatut(): ?int
+ {
+ return $this->statut;
+ }
+
+ public function setStatut($statut): self
+ {
+ $this->statut = $statut;
+
+ return $this;
+ }
+
+ public function getSubmitdate(): ?\DateTimeInterface
+ {
+ return $this->submitdate;
+ }
+
+ public function setSubmitdate(\DateTimeInterface $submitdate): self
+ {
+ $this->submitdate = $submitdate;
+
+ return $this;
+ }
+
+ public function getStartexecdate(): ?\DateTimeInterface
+ {
+ return $this->startexecdate;
+ }
+
+ public function setStartexecdate(?\DateTimeInterface $startexecdate): self
+ {
+ $this->startexecdate = $startexecdate;
+
+ return $this;
+ }
+
+ public function getEndexecdate(): ?\DateTimeInterface
+ {
+ return $this->endexecdate;
+ }
+
+ public function setEndexecdate(?\DateTimeInterface $endexecdate): self
+ {
+ $this->endexecdate = $endexecdate;
+
+ return $this;
+ }
+
+ public function getNextexecdate(): ?\DateTimeInterface
+ {
+ return $this->nextexecdate;
+ }
+
+ public function setNextexecdate(?\DateTimeInterface $nextexecdate): self
+ {
+ $this->nextexecdate = $nextexecdate;
+
+ return $this;
+ }
+
+ public function getRepeatinterval(): ?int
+ {
+ return $this->repeatinterval;
+ }
+
+ public function setRepeatinterval(?int $repeatinterval): self
+ {
+ $this->repeatinterval = $repeatinterval;
+
+ return $this;
+ }
+
+ public function getJsonargument(): ?string
+ {
+ return $this->jsonargument;
+ }
+
+ public function setJsonargument(?string $jsonargument): self
+ {
+ $this->jsonargument = $jsonargument;
+
+ return $this;
+ }
+}
diff --git a/src/Entity/Group.php b/src/Entity/Group.php
new file mode 100644
index 0000000..37e5c19
--- /dev/null
+++ b/src/Entity/Group.php
@@ -0,0 +1,268 @@
+users = new ArrayCollection();
+ }
+
+ //== CODE A NE PAS REGENERER
+ public function setId(int $id): self
+ {
+ $this->id = $id;
+ return $this;
+ }
+ //== FIN DU CODE A NE PAS REGENERER
+
+ public function getId(): ?int
+ {
+ return $this->id;
+ }
+
+ public function getLabel(): ?string
+ {
+ return $this->label;
+ }
+
+ public function setLabel(string $label): self
+ {
+ $this->label = $label;
+
+ return $this;
+ }
+
+ public function getDescription(): ?string
+ {
+ return $this->description;
+ }
+
+ public function setDescription(?string $description): self
+ {
+ $this->description = $description;
+
+ return $this;
+ }
+
+ public function getEmail(): ?string
+ {
+ return $this->email;
+ }
+
+ public function setEmail(?string $email): self
+ {
+ $this->email = $email;
+
+ return $this;
+ }
+
+ public function isIsopen(): ?bool
+ {
+ return $this->isopen;
+ }
+
+ public function setIsopen(bool $isopen): self
+ {
+ $this->isopen = $isopen;
+
+ return $this;
+ }
+
+ public function isIsworkgroup(): ?bool
+ {
+ return $this->isworkgroup;
+ }
+
+ public function setIsworkgroup(bool $isworkgroup): self
+ {
+ $this->isworkgroup = $isworkgroup;
+
+ return $this;
+ }
+
+ public function getApikey(): ?string
+ {
+ return $this->apikey;
+ }
+
+ public function setApikey(string $apikey): self
+ {
+ $this->apikey = $apikey;
+
+ return $this;
+ }
+
+ public function getLdapfilter(): ?string
+ {
+ return $this->ldapfilter;
+ }
+
+ public function setLdapfilter(?string $ldapfilter): self
+ {
+ $this->ldapfilter = $ldapfilter;
+
+ return $this;
+ }
+
+ public function getAttributes(): ?string
+ {
+ return $this->attributes;
+ }
+
+ public function setAttributes(?string $attributes): self
+ {
+ $this->attributes = $attributes;
+
+ return $this;
+ }
+
+ public function getIdexternal(): ?string
+ {
+ return $this->idexternal;
+ }
+
+ public function setIdexternal(?string $idexternal): self
+ {
+ $this->idexternal = $idexternal;
+
+ return $this;
+ }
+
+ public function getInvitations(): ?array
+ {
+ return $this->invitations;
+ }
+
+ public function setInvitations(?array $invitations): self
+ {
+ $this->invitations = $invitations;
+
+ return $this;
+ }
+
+ public function getOwner(): ?User
+ {
+ return $this->owner;
+ }
+
+ public function setOwner(?User $owner): self
+ {
+ $this->owner = $owner;
+
+ return $this;
+ }
+
+ /**
+ * @return Collection
+ */
+ public function getUsers(): Collection
+ {
+ return $this->users;
+ }
+
+ public function addUser(UserGroup $user): self
+ {
+ if (!$this->users->contains($user)) {
+ $this->users[] = $user;
+ $user->setGroup($this);
+ }
+
+ return $this;
+ }
+
+ public function removeUser(UserGroup $user): self
+ {
+ if ($this->users->removeElement($user)) {
+ // set the owning side to null (unless already changed)
+ if ($user->getGroup() === $this) {
+ $user->setGroup(null);
+ }
+ }
+
+ return $this;
+ }
+}
diff --git a/src/Entity/Niveau01.php b/src/Entity/Niveau01.php
new file mode 100644
index 0000000..2ea4982
--- /dev/null
+++ b/src/Entity/Niveau01.php
@@ -0,0 +1,292 @@
+niveau02s = new ArrayCollection();
+ $this->registrations = new ArrayCollection();
+ $this->users = new ArrayCollection();
+ $this->modos = new ArrayCollection();
+ }
+
+
+ //== CODE A NE PAS REGENERER
+ public function setId(int $id): self
+ {
+ $this->id = $id;
+ return $this;
+ }
+ //== FIN DU CODE A NE PAS REGENERER
+
+ public function getId(): ?int
+ {
+ return $this->id;
+ }
+
+ public function getLabel(): ?string
+ {
+ return $this->label;
+ }
+
+ public function setLabel(string $label): self
+ {
+ $this->label = $label;
+
+ return $this;
+ }
+
+ public function getApikey(): ?string
+ {
+ return $this->apikey;
+ }
+
+ public function setApikey(string $apikey): self
+ {
+ $this->apikey = $apikey;
+
+ return $this;
+ }
+
+ public function getLdapfilter(): ?string
+ {
+ return $this->ldapfilter;
+ }
+
+ public function setLdapfilter(?string $ldapfilter): self
+ {
+ $this->ldapfilter = $ldapfilter;
+
+ return $this;
+ }
+
+ public function getAttributes(): ?string
+ {
+ return $this->attributes;
+ }
+
+ public function setAttributes(?string $attributes): self
+ {
+ $this->attributes = $attributes;
+
+ return $this;
+ }
+
+ public function getIdexternal(): ?string
+ {
+ return $this->idexternal;
+ }
+
+ public function setIdexternal(?string $idexternal): self
+ {
+ $this->idexternal = $idexternal;
+
+ return $this;
+ }
+
+ /**
+ * @return Collection
+ */
+ public function getNiveau02s(): Collection
+ {
+ return $this->niveau02s;
+ }
+
+ public function addNiveau02(Niveau02 $niveau02): self
+ {
+ if (!$this->niveau02s->contains($niveau02)) {
+ $this->niveau02s[] = $niveau02;
+ $niveau02->setNiveau01($this);
+ }
+
+ return $this;
+ }
+
+ public function removeNiveau02(Niveau02 $niveau02): self
+ {
+ if ($this->niveau02s->removeElement($niveau02)) {
+ // set the owning side to null (unless already changed)
+ if ($niveau02->getNiveau01() === $this) {
+ $niveau02->setNiveau01(null);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * @return Collection
+ */
+ public function getRegistrations(): Collection
+ {
+ return $this->registrations;
+ }
+
+ public function addRegistration(Registration $registration): self
+ {
+ if (!$this->registrations->contains($registration)) {
+ $this->registrations[] = $registration;
+ $registration->setNiveau01($this);
+ }
+
+ return $this;
+ }
+
+ public function removeRegistration(Registration $registration): self
+ {
+ if ($this->registrations->removeElement($registration)) {
+ // set the owning side to null (unless already changed)
+ if ($registration->getNiveau01() === $this) {
+ $registration->setNiveau01(null);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * @return Collection
+ */
+ public function getUsers(): Collection
+ {
+ return $this->users;
+ }
+
+ public function addUser(User $user): self
+ {
+ if (!$this->users->contains($user)) {
+ $this->users[] = $user;
+ $user->setNiveau01($this);
+ }
+
+ return $this;
+ }
+
+ public function removeUser(User $user): self
+ {
+ if ($this->users->removeElement($user)) {
+ // set the owning side to null (unless already changed)
+ if ($user->getNiveau01() === $this) {
+ $user->setNiveau01(null);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * @return Collection
+ */
+ public function getModos(): Collection
+ {
+ return $this->modos;
+ }
+
+ public function addModo(UserModo $modo): self
+ {
+ if (!$this->modos->contains($modo)) {
+ $this->modos[] = $modo;
+ $modo->setNiveau01($this);
+ }
+
+ return $this;
+ }
+
+ public function removeModo(UserModo $modo): self
+ {
+ if ($this->modos->removeElement($modo)) {
+ // set the owning side to null (unless already changed)
+ if ($modo->getNiveau01() === $this) {
+ $modo->setNiveau01(null);
+ }
+ }
+
+ return $this;
+ }
+
+
+}
diff --git a/src/Entity/Niveau02.php b/src/Entity/Niveau02.php
new file mode 100644
index 0000000..0755bf5
--- /dev/null
+++ b/src/Entity/Niveau02.php
@@ -0,0 +1,169 @@
+registrations = new ArrayCollection();
+ $this->users = new ArrayCollection();
+ }
+
+ public function getId(): ?int
+ {
+ return $this->id;
+ }
+
+ public function getLabel(): ?string
+ {
+ return $this->label;
+ }
+
+ public function setLabel(string $label): self
+ {
+ $this->label = $label;
+
+ return $this;
+ }
+
+ public function getApikey(): ?string
+ {
+ return $this->apikey;
+ }
+
+ public function setApikey(string $apikey): self
+ {
+ $this->apikey = $apikey;
+
+ return $this;
+ }
+
+ public function getNiveau01(): ?Niveau01
+ {
+ return $this->niveau01;
+ }
+
+ public function setNiveau01(?Niveau01 $niveau01): self
+ {
+ $this->niveau01 = $niveau01;
+
+ return $this;
+ }
+
+ /**
+ * @return Collection
+ */
+ public function getRegistrations(): Collection
+ {
+ return $this->registrations;
+ }
+
+ public function addRegistration(Registration $registration): self
+ {
+ if (!$this->registrations->contains($registration)) {
+ $this->registrations[] = $registration;
+ $registration->setNiveau02($this);
+ }
+
+ return $this;
+ }
+
+ public function removeRegistration(Registration $registration): self
+ {
+ if ($this->registrations->removeElement($registration)) {
+ // set the owning side to null (unless already changed)
+ if ($registration->getNiveau02() === $this) {
+ $registration->setNiveau02(null);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * @return Collection
+ */
+ public function getUsers(): Collection
+ {
+ return $this->users;
+ }
+
+ public function addUser(User $user): self
+ {
+ if (!$this->users->contains($user)) {
+ $this->users[] = $user;
+ $user->setNiveau02($this);
+ }
+
+ return $this;
+ }
+
+ public function removeUser(User $user): self
+ {
+ if ($this->users->removeElement($user)) {
+ // set the owning side to null (unless already changed)
+ if ($user->getNiveau02() === $this) {
+ $user->setNiveau02(null);
+ }
+ }
+
+ return $this;
+ }
+
+}
diff --git a/src/Entity/Registration.php b/src/Entity/Registration.php
new file mode 100644
index 0000000..cba10dd
--- /dev/null
+++ b/src/Entity/Registration.php
@@ -0,0 +1,416 @@
+username;
+ }
+
+
+ public function setPasswordDirect($password)
+ {
+ // Permet de setter le password généré lors de l'inscription
+ $this->password = $password;
+
+ return $this;
+ }
+
+ /**
+ * @see PasswordAuthenticatedUserInterface
+ */
+ public function getPassword(): string
+ {
+ return $this->password;
+ }
+
+ public function setPassword($password): self
+ {
+ if($password!=$this->password&&$password!=""){
+ // Placer le password non encodé dans une variable tempo sur laquel on va appliquer la contraite de form
+ $this->passwordplain = $password;
+
+ // Password encrypté format openldap
+ $this->salt = uniqid(mt_rand(), true);
+ $hash = "{SSHA}" . base64_encode(pack("H*", sha1($password . $this->salt)) . $this->salt);
+
+ $this->password = $hash;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returning a salt is only needed, if you are not using a modern
+ * hashing algorithm (e.g. bcrypt or sodium) in your security.yaml.
+ *
+ * @see UserInterface
+ */
+ public function getSalt(): ?string
+ {
+ return $this->salt;
+ }
+
+ /**
+ * @see UserInterface
+ */
+ public function eraseCredentials()
+ {
+ $this->passwordplain = null;
+ }
+
+ public function getRoles(): array
+ {
+ return $this->roles;
+ }
+
+ public function hasRole(string $role): ?bool
+ {
+ return in_array($role,$this->getRoles());
+ }
+
+ public function setRole(string $role): self
+ {
+ if(!$this->hasRole($role))
+ array_push($this->roles,$role);
+
+ return $this;
+ }
+
+ public function getDisplayname() {
+ return $this->firstname." ".$this->lastname;
+ }
+
+ //== FIN DU CODE A NE PAS REGENERER
+
+ public function getId(): ?int
+ {
+ return $this->id;
+ }
+
+ public function getUsername(): ?string
+ {
+ return $this->username;
+ }
+
+ public function setUsername(string $username): self
+ {
+ $this->username = $username;
+
+ return $this;
+ }
+
+ public function getFirstname(): ?string
+ {
+ return $this->firstname;
+ }
+
+ public function setFirstname(?string $firstname): self
+ {
+ $this->firstname = $firstname;
+
+ return $this;
+ }
+
+ public function getLastname(): ?string
+ {
+ return $this->lastname;
+ }
+
+ public function setLastname(?string $lastname): self
+ {
+ $this->lastname = $lastname;
+
+ return $this;
+ }
+
+ public function setSalt(string $salt): self
+ {
+ $this->salt = $salt;
+
+ return $this;
+ }
+
+ public function getEmail(): ?string
+ {
+ return $this->email;
+ }
+
+ public function setEmail(string $email): self
+ {
+ $this->email = $email;
+
+ return $this;
+ }
+
+ public function isIsvisible(): ?bool
+ {
+ return $this->isvisible;
+ }
+
+ public function setIsvisible(bool $isvisible): self
+ {
+ $this->isvisible = $isvisible;
+
+ return $this;
+ }
+
+ public function getPostaladress(): ?string
+ {
+ return $this->postaladress;
+ }
+
+ public function setPostaladress(?string $postaladress): self
+ {
+ $this->postaladress = $postaladress;
+
+ return $this;
+ }
+
+ public function getTelephonenumber(): ?string
+ {
+ return $this->telephonenumber;
+ }
+
+ public function setTelephonenumber(?string $telephonenumber): self
+ {
+ $this->telephonenumber = $telephonenumber;
+
+ return $this;
+ }
+
+ public function getJob(): ?string
+ {
+ return $this->job;
+ }
+
+ public function setJob(?string $job): self
+ {
+ $this->job = $job;
+
+ return $this;
+ }
+
+ public function getPosition(): ?string
+ {
+ return $this->position;
+ }
+
+ public function setPosition(?string $position): self
+ {
+ $this->position = $position;
+
+ return $this;
+ }
+
+ public function getMotivation(): ?string
+ {
+ return $this->motivation;
+ }
+
+ public function setMotivation(?string $motivation): self
+ {
+ $this->motivation = $motivation;
+
+ return $this;
+ }
+
+ public function getNote(): ?string
+ {
+ return $this->note;
+ }
+
+ public function setNote(?string $note): self
+ {
+ $this->note = $note;
+
+ return $this;
+ }
+
+ public function getKeyexpire(): ?\DateTimeInterface
+ {
+ return $this->keyexpire;
+ }
+
+ public function setKeyexpire(?\DateTimeInterface $keyexpire): self
+ {
+ $this->keyexpire = $keyexpire;
+
+ return $this;
+ }
+
+ public function getKeyvalue(): ?string
+ {
+ return $this->keyvalue;
+ }
+
+ public function setKeyvalue(?string $keyvalue): self
+ {
+ $this->keyvalue = $keyvalue;
+
+ return $this;
+ }
+
+ public function getStatut(): ?int
+ {
+ return $this->statut;
+ }
+
+ public function setStatut(int $statut): self
+ {
+ $this->statut = $statut;
+
+ return $this;
+ }
+
+ public function getNiveau01(): ?Niveau01
+ {
+ return $this->niveau01;
+ }
+
+ public function setNiveau01(?Niveau01 $niveau01): self
+ {
+ $this->niveau01 = $niveau01;
+
+ return $this;
+ }
+
+ public function getNiveau02(): ?Niveau02
+ {
+ return $this->niveau02;
+ }
+
+ public function setNiveau02(?Niveau02 $niveau02): self
+ {
+ $this->niveau02 = $niveau02;
+
+ return $this;
+ }
+
+}
diff --git a/src/Entity/User.php b/src/Entity/User.php
new file mode 100644
index 0000000..e027b24
--- /dev/null
+++ b/src/Entity/User.php
@@ -0,0 +1,626 @@
+groups = new ArrayCollection();
+ $this->ownergroups = new ArrayCollection();
+ $this->modos = new ArrayCollection();
+ }
+
+ //== CODE A NE PAS REGENERER
+ public function setId(int $id): self
+ {
+ $this->id = $id;
+ return $this;
+ }
+
+
+ public function getUserIdentifier(): string
+ {
+ return $this->username;
+ }
+
+
+ public function setPasswordDirect($password)
+ {
+ // Permet de setter le password généré lors de l'inscription
+ $this->password = $password;
+
+ return $this;
+ }
+
+ /**
+ * @see PasswordAuthenticatedUserInterface
+ */
+ public function getPassword(): string
+ {
+ return $this->password;
+ }
+
+ public function setPassword($password): self
+ {
+ if($password!=$this->password&&$password!=""){
+ // Placer le password non encodé dans une variable tempo sur laquel on va appliquer la contraite de form
+ $this->passwordplain = $password;
+
+ // Password encrypté format openldap
+ $this->salt = uniqid(mt_rand(), true);
+ $hash = "{SSHA}" . base64_encode(pack("H*", sha1($password . $this->salt)) . $this->salt);
+
+ $this->password = $hash;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returning a salt is only needed, if you are not using a modern
+ * hashing algorithm (e.g. bcrypt or sodium) in your security.yaml.
+ *
+ * @see UserInterface
+ */
+ public function getSalt(): ?string
+ {
+ return $this->salt;
+ }
+
+ /**
+ * @see UserInterface
+ */
+ public function eraseCredentials()
+ {
+ $this->passwordplain = null;
+ }
+
+ public function getRoles(): array
+ {
+ return $this->roles;
+ }
+
+ public function hasRole(string $role): ?bool
+ {
+ return in_array($role,$this->getRoles());
+ }
+
+ public function setRole(string $role): self
+ {
+ if(!$this->hasRole($role))
+ array_push($this->roles,$role);
+
+ return $this;
+ }
+
+ public function getDisplayname() {
+ return $this->firstname." ".$this->lastname;
+ }
+
+ //== FIN DU CODE A NE PAS REGENERER
+
+ public function getId(): ?int
+ {
+ return $this->id;
+ }
+
+ public function getUsername(): ?string
+ {
+ return $this->username;
+ }
+
+ public function setUsername(string $username): self
+ {
+ $this->username = $username;
+
+ return $this;
+ }
+
+ public function getApikey(): ?string
+ {
+ return $this->apikey;
+ }
+
+ public function setApikey(string $apikey): self
+ {
+ $this->apikey = $apikey;
+
+ return $this;
+ }
+
+ public function getFirstname(): ?string
+ {
+ return $this->firstname;
+ }
+
+ public function setFirstname(?string $firstname): self
+ {
+ $this->firstname = $firstname;
+
+ return $this;
+ }
+
+ public function getLastname(): ?string
+ {
+ return $this->lastname;
+ }
+
+ public function setLastname(?string $lastname): self
+ {
+ $this->lastname = $lastname;
+
+ return $this;
+ }
+
+ public function setRoles(array $roles): self
+ {
+ $this->roles = $roles;
+
+ return $this;
+ }
+
+ public function setSalt(string $salt): self
+ {
+ $this->salt = $salt;
+
+ return $this;
+ }
+
+ public function getEmail(): ?string
+ {
+ return $this->email;
+ }
+
+ public function setEmail(string $email): self
+ {
+ $this->email = $email;
+
+ return $this;
+ }
+
+ public function getAvatar(): ?string
+ {
+ return $this->avatar;
+ }
+
+ public function setAvatar(?string $avatar): self
+ {
+ $this->avatar = $avatar;
+
+ return $this;
+ }
+
+ public function isIsvisible(): ?bool
+ {
+ return $this->isvisible;
+ }
+
+ public function setIsvisible(bool $isvisible): self
+ {
+ $this->isvisible = $isvisible;
+
+ return $this;
+ }
+
+ public function getPostaladress(): ?string
+ {
+ return $this->postaladress;
+ }
+
+ public function setPostaladress(?string $postaladress): self
+ {
+ $this->postaladress = $postaladress;
+
+ return $this;
+ }
+
+ public function getTelephonenumber(): ?string
+ {
+ return $this->telephonenumber;
+ }
+
+ public function setTelephonenumber(?string $telephonenumber): self
+ {
+ $this->telephonenumber = $telephonenumber;
+
+ return $this;
+ }
+
+ public function getJob(): ?string
+ {
+ return $this->job;
+ }
+
+ public function setJob(?string $job): self
+ {
+ $this->job = $job;
+
+ return $this;
+ }
+
+ public function getPosition(): ?string
+ {
+ return $this->position;
+ }
+
+ public function setPosition(?string $position): self
+ {
+ $this->position = $position;
+
+ return $this;
+ }
+
+ public function getMotivation(): ?string
+ {
+ return $this->motivation;
+ }
+
+ public function setMotivation(?string $motivation): self
+ {
+ $this->motivation = $motivation;
+
+ return $this;
+ }
+
+ public function getNote(): ?string
+ {
+ return $this->note;
+ }
+
+ public function setNote(?string $note): self
+ {
+ $this->note = $note;
+
+ return $this;
+ }
+
+ public function getPreference(): ?array
+ {
+ return $this->preference;
+ }
+
+ public function setPreference(?array $preference): self
+ {
+ $this->preference = $preference;
+
+ return $this;
+ }
+
+ public function getKeyexpire(): ?\DateTimeInterface
+ {
+ return $this->keyexpire;
+ }
+
+ public function setKeyexpire(?\DateTimeInterface $keyexpire): self
+ {
+ $this->keyexpire = $keyexpire;
+
+ return $this;
+ }
+
+ public function getKeyvalue(): ?string
+ {
+ return $this->keyvalue;
+ }
+
+ public function setKeyvalue(?string $keyvalue): self
+ {
+ $this->keyvalue = $keyvalue;
+
+ return $this;
+ }
+
+ public function getVisitedate(): ?\DateTimeInterface
+ {
+ return $this->visitedate;
+ }
+
+ public function setVisitedate(?\DateTimeInterface $visitedate): self
+ {
+ $this->visitedate = $visitedate;
+
+ return $this;
+ }
+
+ public function getVisitecpt(): ?int
+ {
+ return $this->visitecpt;
+ }
+
+ public function setVisitecpt(?int $visitecpt): self
+ {
+ $this->visitecpt = $visitecpt;
+
+ return $this;
+ }
+
+ public function getNiveau01(): ?Niveau01
+ {
+ return $this->niveau01;
+ }
+
+ public function setNiveau01(?Niveau01 $niveau01): self
+ {
+ $this->niveau01 = $niveau01;
+
+ return $this;
+ }
+
+ public function getNiveau02(): ?Niveau02
+ {
+ return $this->niveau02;
+ }
+
+ public function setNiveau02(?Niveau02 $niveau02): self
+ {
+ $this->niveau02 = $niveau02;
+
+ return $this;
+ }
+
+ /**
+ * @return Collection
+ */
+ public function getGroups(): Collection
+ {
+ return $this->groups;
+ }
+
+ public function addGroup(UserGroup $group): self
+ {
+ if (!$this->groups->contains($group)) {
+ $this->groups[] = $group;
+ $group->setUser($this);
+ }
+
+ return $this;
+ }
+
+ public function removeGroup(UserGroup $group): self
+ {
+ if ($this->groups->removeElement($group)) {
+ // set the owning side to null (unless already changed)
+ if ($group->getUser() === $this) {
+ $group->setUser(null);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * @return Collection
+ */
+ public function getOwnergroups(): Collection
+ {
+ return $this->ownergroups;
+ }
+
+ public function addOwnergroup(Group $ownergroup): self
+ {
+ if (!$this->ownergroups->contains($ownergroup)) {
+ $this->ownergroups[] = $ownergroup;
+ $ownergroup->setOwner($this);
+ }
+
+ return $this;
+ }
+
+ public function removeOwnergroup(Group $ownergroup): self
+ {
+ if ($this->ownergroups->removeElement($ownergroup)) {
+ // set the owning side to null (unless already changed)
+ if ($ownergroup->getOwner() === $this) {
+ $ownergroup->setOwner(null);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * @return Collection
+ */
+ public function getModos(): Collection
+ {
+ return $this->modos;
+ }
+
+ public function addModo(UserModo $modo): self
+ {
+ if (!$this->modos->contains($modo)) {
+ $this->modos[] = $modo;
+ $modo->setUser($this);
+ }
+
+ return $this;
+ }
+
+ public function removeModo(UserModo $modo): self
+ {
+ if ($this->modos->removeElement($modo)) {
+ // set the owning side to null (unless already changed)
+ if ($modo->getUser() === $this) {
+ $modo->setUser(null);
+ }
+ }
+
+ return $this;
+ }
+
+}
diff --git a/src/Entity/UserGroup.php b/src/Entity/UserGroup.php
new file mode 100644
index 0000000..fd77b1a
--- /dev/null
+++ b/src/Entity/UserGroup.php
@@ -0,0 +1,132 @@
+id;
+ }
+
+ public function getRolegroup(): ?int
+ {
+ return $this->rolegroup;
+ }
+
+ public function setRolegroup(int $rolegroup): self
+ {
+ $this->rolegroup = $rolegroup;
+
+ return $this;
+ }
+
+ public function getApikey(): ?string
+ {
+ return $this->apikey;
+ }
+
+ public function setApikey(string $apikey): self
+ {
+ $this->apikey = $apikey;
+
+ return $this;
+ }
+
+ public function getVisitedate(): ?\DateTimeInterface
+ {
+ return $this->visitedate;
+ }
+
+ public function setVisitedate(?\DateTimeInterface $visitedate): self
+ {
+ $this->visitedate = $visitedate;
+
+ return $this;
+ }
+
+ public function getVisitecpt(): ?int
+ {
+ return $this->visitecpt;
+ }
+
+ public function setVisitecpt(?int $visitecpt): self
+ {
+ $this->visitecpt = $visitecpt;
+
+ return $this;
+ }
+
+ public function getUser(): ?User
+ {
+ return $this->user;
+ }
+
+ public function setUser(?User $user): self
+ {
+ $this->user = $user;
+
+ return $this;
+ }
+
+ public function getGroup(): ?Group
+ {
+ return $this->group;
+ }
+
+ public function setGroup(?Group $group): self
+ {
+ $this->group = $group;
+
+ return $this;
+ }
+
+}
diff --git a/src/Entity/UserModo.php b/src/Entity/UserModo.php
new file mode 100644
index 0000000..59c629f
--- /dev/null
+++ b/src/Entity/UserModo.php
@@ -0,0 +1,62 @@
+id;
+ }
+
+ public function getUser(): ?User
+ {
+ return $this->user;
+ }
+
+ public function setUser(?User $user): self
+ {
+ $this->user = $user;
+
+ return $this;
+ }
+
+ public function getNiveau01(): ?Niveau01
+ {
+ return $this->niveau01;
+ }
+
+ public function setNiveau01(?Niveau01 $niveau01): self
+ {
+ $this->niveau01 = $niveau01;
+
+ return $this;
+ }
+ }
diff --git a/src/Entity/Whitelist.php b/src/Entity/Whitelist.php
new file mode 100644
index 0000000..da0b189
--- /dev/null
+++ b/src/Entity/Whitelist.php
@@ -0,0 +1,45 @@
+id;
+ }
+
+ public function getLabel(): ?string
+ {
+ return $this->label;
+ }
+
+ public function setLabel(string $label): self
+ {
+ $this->label = $label;
+
+ return $this;
+ }
+}
diff --git a/src/EventListener/AllSubscriber.php b/src/EventListener/AllSubscriber.php
new file mode 100644
index 0000000..c07479f
--- /dev/null
+++ b/src/EventListener/AllSubscriber.php
@@ -0,0 +1,28 @@
+entity = $args->getObject();
+
+ // Les enregistrements négatifs sont des enregistrements systeme indispensable
+ if($this->entity->getId()<0)
+ throw new \Exception("Impossible de supprimer cet enregistrement. C'est un enregistrement système");
+ }
+}
diff --git a/src/EventListener/GroupSubscriber.php b/src/EventListener/GroupSubscriber.php
new file mode 100644
index 0000000..c36aaeb
--- /dev/null
+++ b/src/EventListener/GroupSubscriber.php
@@ -0,0 +1,138 @@
+em = $em;
+ $this->ldap = $ldap;
+ }
+
+
+ public function getSubscribedEvents(): array
+ {
+ return [
+ Events::postPersist,
+ Events::preUpdate,
+ Events::postUpdate,
+ Events::preRemove,
+ Events::postRemove,
+ ];
+ }
+
+ public function postPersist(LifecycleEventArgs $args): void
+ {
+ $this->entity = $args->getObject();
+ if (!$this->entity instanceof Entity) return;
+
+ // Synchronisation nine2ldap
+ $this->nine2ldap();
+
+ // On s'assure que le propriétaire est bien membre du groupe avec le role manager
+ $this->ctrlOwner();
+ }
+
+ public function preUpdate(LifecycleEventArgs $args): void
+ {
+ $this->entity = $args->getObject();
+ if (!$this->entity instanceof Entity) return;
+ }
+
+ public function postUpdate(LifecycleEventArgs $args): void
+ {
+ $this->entity = $args->getObject();
+ if (!$this->entity instanceof Entity) return;
+
+ // Synchronisation nine2ldap
+ $this->nine2ldap();
+
+ // On s'assure que le propriétaire est bien membre du groupe avec le role manager
+ $this->ctrlOwner();
+ }
+
+ public function preRemove(LifecycleEventArgs $args): void
+ {
+ $this->entity = $args->getObject();
+ if (!$this->entity instanceof Entity) return;
+
+ // Synchronisation nine2ldap
+ $this->nine2ldapremove();
+ }
+
+ public function postRemove(LifecycleEventArgs $args): void
+ {
+ $this->entity = $args->getObject();
+ if (!$this->entity instanceof Entity) return;
+ }
+
+ private function nine2ldap() {
+ if($this->ldap->isNine2Ldap()) {
+ // On s'assure que la structure organisationnelle est présente
+ $this->ldap->addOrganisations();
+
+ // Ajout / Modification group dans annuaire
+ $filter="gidnumber=".$this->entity->getId();
+ $attributes=$this->ldap->listAttributesGroup();
+ $ldapentrys=$this->ldap->search($filter,$attributes,$this->ldap->getParameter("basegroup"));
+ if(empty($ldapentrys)) {
+ $this->ldap->addGroup($this->entity);
+ }
+ elseif($this->ldap->ismodifyGroup($this->entity,$ldapentrys[0])) {
+ $this->ldap->modifyGroup($this->entity,$ldapentrys[0]["cn"]);
+ }
+ }
+ }
+
+
+ private function nine2ldapremove() {
+ if($this->ldap->isNine2Ldap()) {
+ $filter="gidnumber=".$this->entity->getId();
+ $attributes=$this->ldap->listAttributesGroup();
+ $ldapentrys=$this->ldap->search($filter,$attributes,$this->ldap->getParameter("basegroup"));
+ if(!empty($ldapentrys)) {
+ $this->ldap->deleteGroup($this->entity);
+ }
+ }
+ }
+
+ private function ctrlOwner() {
+ $group=$this->entity;
+
+ // Le propriétaire passe manager
+ $usergroups=$this->em->getRepository("App\Entity\UserGroup")->findBy(["group"=>$group,"rolegroup"=>"100"]);
+ foreach($usergroups as $usergroup) {
+ $usergroup->setRolegroup(90);
+ $this->em->flush();
+ }
+
+ // Le propriétaire prend son role dans le groupe
+ if($group->getOwner()) {
+ $usergroup=$this->em->getRepository("App\Entity\UserGroup")->findOneBy(["group"=>$group,"user"=>$group->getOwner()]);
+ if(!$usergroup) {
+ $usergroup=new UserGroup();
+ $usergroup->setUser($group->getOwner());
+ $usergroup->setGroup($group);
+ $usergroup->setApikey(Uuid::uuid4());
+ }
+ $usergroup->setRolegroup(100);
+ $this->em->persist($usergroup);
+ $this->em->flush();
+ }
+ }
+}
diff --git a/src/EventListener/Niveau01Subscriber.php b/src/EventListener/Niveau01Subscriber.php
new file mode 100644
index 0000000..844fa90
--- /dev/null
+++ b/src/EventListener/Niveau01Subscriber.php
@@ -0,0 +1,118 @@
+em = $em;
+ $this->ldap = $ldap;
+ }
+
+
+ public function getSubscribedEvents(): array
+ {
+ return [
+ Events::postPersist,
+ Events::preUpdate,
+ Events::postUpdate,
+ Events::preRemove,
+ Events::postRemove,
+ ];
+ }
+
+ public function postPersist(LifecycleEventArgs $args): void
+ {
+ $this->entity = $args->getObject();
+ if (!$this->entity instanceof Entity) return;
+
+ // Synchronisation nine2ldap
+ $this->nine2ldap();
+ }
+
+ public function preUpdate(LifecycleEventArgs $args): void
+ {
+ $this->entity = $args->getObject();
+ if (!$this->entity instanceof Entity) return;
+ }
+
+ public function postUpdate(LifecycleEventArgs $args): void
+ {
+ $this->entity = $args->getObject();
+ if (!$this->entity instanceof Entity) return;
+
+ // Synchronisation nine2ldap
+ $this->nine2ldap();
+ }
+
+ public function preRemove(LifecycleEventArgs $args): void
+ {
+ $this->entity = $args->getObject();
+ if (!$this->entity instanceof Entity) return;
+
+ // Impossible de supprimer si présence de niveau02 rattaché
+ if(!$this->entity->getNiveau02s()->isEmpty())
+ throw new \Exception("Impossible de supprimer cet enregistrement. Il est lié à des niveaux de rang 02");
+
+ // Impossible de supprimer si présence de registration rattaché
+ if(!$this->entity->getRegistrations()->isEmpty())
+ throw new \Exception("Impossible de supprimer cet enregistrement. Il est lié à des inscriptions");
+
+ // Impossible de supprimer si présence de user rattaché
+ if(!$this->entity->getUsers()->isEmpty())
+ throw new \Exception("Impossible de supprimer cet enregistrement. Il est lié à des utilisateurs");
+
+ // Synchronisation nine2ldap
+ $this->nine2ldapremove();
+ }
+
+ public function postRemove(LifecycleEventArgs $args): void
+ {
+ $this->entity = $args->getObject();
+ if (!$this->entity instanceof Entity) return;
+ }
+
+
+ private function nine2ldap() {
+ if($this->ldap->isNine2Ldap()) {
+ // On s'assure que la structure organisationnelle est présente
+ $this->ldap->addOrganisations();
+
+ // Ajout / Modification dans annuaire
+ $filter="gidnumber=".$this->entity->getId();
+ $attributes=$this->ldap->listAttributesNiveau01();
+ $ldapentrys=$this->ldap->search($filter,$attributes,$this->ldap->getParameter("baseniveau01"));
+ if(empty($ldapentrys)) {
+ $this->ldap->addNiveau01($this->entity);
+ }
+ elseif($this->ldap->ismodifyNiveau01($this->entity,$ldapentrys[0])) {
+ $this->ldap->modifyNiveau01($this->entity,$ldapentrys[0]["cn"]);
+ }
+ }
+ }
+
+ private function nine2ldapremove() {
+ if($this->ldap->isNine2Ldap()) {
+ $filter="gidnumber=".$this->entity->getId();
+ $attributes=$this->ldap->listAttributesNiveau01();
+ $ldapentrys=$this->ldap->search($filter,$attributes,$this->ldap->getParameter("baseniveau01"));
+ if(!empty($ldapentrys)) {
+ $this->ldap->deleteNiveau01($this->entity);
+ }
+ }
+ }
+}
diff --git a/src/EventListener/Niveau02Subscriber.php b/src/EventListener/Niveau02Subscriber.php
new file mode 100644
index 0000000..5223918
--- /dev/null
+++ b/src/EventListener/Niveau02Subscriber.php
@@ -0,0 +1,112 @@
+em = $em;
+ $this->ldap = $ldap;
+ }
+
+ public function getSubscribedEvents(): array
+ {
+ return [
+ Events::postPersist,
+ Events::preUpdate,
+ Events::postUpdate,
+ Events::preRemove,
+ Events::postRemove,
+ ];
+ }
+
+ public function postPersist(LifecycleEventArgs $args): void
+ {
+ $this->entity = $args->getObject();
+ if (!$this->entity instanceof Entity) return;
+
+
+ // Synchronisation nine2ldap
+ $this->nine2ldap();
+ }
+
+ public function preUpdate(LifecycleEventArgs $args): void
+ {
+ $this->entity = $args->getObject();
+ if (!$this->entity instanceof Entity) return;
+ }
+
+ public function postUpdate(LifecycleEventArgs $args): void
+ {
+ $this->entity = $args->getObject();
+ if (!$this->entity instanceof Entity) return;
+
+ // Synchronisation nine2ldap
+ $this->nine2ldap();
+ }
+
+ public function preRemove(LifecycleEventArgs $args): void
+ {
+ $this->entity = $args->getObject();
+ if (!$this->entity instanceof Entity) return;
+
+ // Impossible de supprimer si présence de registration rattaché
+ if(!$this->entity->getRegistrations()->isEmpty())
+ throw new \Exception("Impossible de supprimer cet enregistrement. Il est lié à des inscriptions");
+
+ // Impossible de supprimer si présence de user rattaché
+ if(!$this->entity->getUsers()->isEmpty())
+ throw new \Exception("Impossible de supprimer cet enregistrement. Il est lié à des utilisateurs");
+
+ // Synchronisation nine2ldap
+ $this->nine2ldapremove();
+ }
+
+ public function postRemove(LifecycleEventArgs $args): void
+ {
+ $this->entity = $args->getObject();
+ if (!$this->entity instanceof Entity) return;;
+ }
+
+ private function nine2ldap() {
+ if($this->ldap->isNine2Ldap()) {
+ // On s'assure que la structure organisationnelle est présente
+ $this->ldap->addOrganisations();
+
+ // Ajout / Modification dans annuaire
+ $filter="gidnumber=".$this->entity->getId();
+ $attributes=$this->ldap->listAttributesNiveau02();
+ $ldapentrys=$this->ldap->search($filter,$attributes,$this->ldap->getParameter("baseniveau02"));
+ if(empty($ldapentrys)) {
+ $this->ldap->addNiveau02($this->entity);
+ }
+ elseif($this->ldap->ismodifyNiveau02($this->entity,$ldapentrys[0])) {
+ $this->ldap->modifyNiveau02($this->entity,$ldapentrys[0]["cn"]);
+ }
+ }
+ }
+
+ private function nine2ldapremove() {
+ if($this->ldap->isNine2Ldap()) {
+ $filter="gidnumber=".$this->entity->getId();
+ $attributes=$this->ldap->listAttributesNiveau02();
+ $ldapentrys=$this->ldap->search($filter,$attributes,$this->ldap->getParameter("baseniveau02"));
+ if(!empty($ldapentrys)) {
+ $this->ldap->deleteNiveau02($this->entity);
+ }
+ }
+ }
+}
diff --git a/src/EventListener/UserGroupSubscriber.php b/src/EventListener/UserGroupSubscriber.php
new file mode 100644
index 0000000..8ecaac1
--- /dev/null
+++ b/src/EventListener/UserGroupSubscriber.php
@@ -0,0 +1,67 @@
+em = $em;
+ $this->ldap = $ldap;
+ }
+
+ public function getSubscribedEvents(): array
+ {
+ return [
+ Events::postPersist,
+ Events::preRemove,
+ ];
+ }
+
+ public function postPersist(LifecycleEventArgs $args): void
+ {
+ $this->entity = $args->getObject();
+ if (!$this->entity instanceof Entity) return;
+
+
+ // Synchronisation nine2ldap
+ $this->nine2ldap();
+ }
+
+ public function preRemove(LifecycleEventArgs $args): void
+ {
+ $this->entity = $args->getObject();
+ if (!$this->entity instanceof Entity) return;
+
+ // Synchronisation nine2ldap
+ $this->nine2ldapremove();
+ }
+
+ private function nine2ldap() {
+ if($this->ldap->isNine2Ldap()) {
+ // On s'assure que la structure organisationnelle est présente
+ $this->ldap->addOrganisations();
+
+ // Ajout / Modification dans annuaire
+ $this->ldap->addUserGroup($this->entity);
+ }
+ }
+
+ private function nine2ldapremove() {
+ if($this->ldap->isNine2Ldap()) {
+ $this->ldap->delUserGroup($this->entity);
+ }
+ }
+}
diff --git a/src/EventListener/UserSubscriber.php b/src/EventListener/UserSubscriber.php
new file mode 100644
index 0000000..7e399e7
--- /dev/null
+++ b/src/EventListener/UserSubscriber.php
@@ -0,0 +1,126 @@
+em = $em;
+ $this->ldap = $ldap;
+ }
+
+
+ public function getSubscribedEvents(): array
+ {
+ return [
+ Events::postPersist,
+ Events::preUpdate,
+ Events::postUpdate,
+ Events::preRemove,
+ Events::postRemove,
+ ];
+ }
+
+ public function postPersist(LifecycleEventArgs $args): void
+ {
+ $this->entity = $args->getObject();
+ if (!$this->entity instanceof Entity) return;
+
+
+ // Synchronisation nine2ldap
+ $this->nine2ldap();
+
+ // Recherche du group tout le monde
+ $group=$this->em->getRepository("App\Entity\Group")->find(-1);
+ $usergroup=new UserGroup();
+ $usergroup->setUser($this->entity);
+ $usergroup->setGroup($group);
+ $usergroup->setApikey(Uuid::uuid4());
+ $usergroup->setRolegroup(0);
+
+ $this->em->persist($usergroup);
+ $this->em->flush();
+ }
+
+ public function preUpdate(LifecycleEventArgs $args): void
+ {
+ $this->entity = $args->getObject();
+ if (!$this->entity instanceof Entity) return;
+ }
+
+ public function postUpdate(LifecycleEventArgs $args): void
+ {
+ $this->entity = $args->getObject();
+ if (!$this->entity instanceof Entity) return;
+
+ // Synchronisation nine2ldap
+ $this->nine2ldap();
+
+ if (!$this->entity instanceof Entity) return;
+ }
+
+ public function preRemove(LifecycleEventArgs $args): void
+ {
+ $this->entity = $args->getObject();
+ if (!$this->entity instanceof Entity) return;
+
+ // Synchronisation nine2ldap
+ $this->nine2ldapremove();
+ }
+
+ public function postRemove(LifecycleEventArgs $args): void
+ {
+ $this->entity = $args->getObject();
+ if (!$this->entity instanceof Entity) return;;
+ }
+
+ private function nine2ldap() {
+ if($this->ldap->isNine2Ldap()) {
+ // On s'assure que la structure organisationnelle est présente
+ $this->ldap->addOrganisations();
+
+ // Ajout / Modification dans annuaire
+ $filter=str_replace("*",$this->entity->getUsername(),$this->ldap->getParameter("filteruser"));
+ $attributes=$this->ldap->listAttributesNiveau02();
+ $ldapentrys=$this->ldap->search($filter,$attributes,$this->ldap->getParameter("baseuser"));
+ if(empty($ldapentrys)) {
+ $this->ldap->addUser($this->entity);
+ }
+ elseif($this->ldap->ismodifyUser($this->entity,$ldapentrys[0])) {
+ $this->ldap->modifyUser($this->entity,$ldapentrys[0]["cn"]);
+ }
+
+ // Mise à jour des niveaux du user
+ $this->ldap->updateNiveauUser($this->entity);
+ }
+ }
+
+ private function nine2ldapremove() {
+ if($this->ldap->isNine2Ldap()) {
+ $filter=str_replace("*",$this->entity->getUsername(),$this->ldap->getParameter("filteruser"));
+ $attributes=$this->ldap->listAttributesNiveau02();
+ $ldapentrys=$this->ldap->search($filter,$attributes,$this->ldap->getParameter("baseuser"));
+ if(!empty($ldapentrys)) {
+ $this->ldap->deleteUser($this->entity);
+ }
+
+ // Mise à jour des niveaux du user en forçant le détachement
+ $this->ldap->updateNiveauUser($this->entity,true);
+ }
+ }
+}
diff --git a/src/Form/ConfigType.php b/src/Form/ConfigType.php
new file mode 100644
index 0000000..e8f1bd2
--- /dev/null
+++ b/src/Form/ConfigType.php
@@ -0,0 +1,189 @@
+add('submit',
+ SubmitType::class,[
+ "label" => "Valider",
+ "attr" => ["class" => "btn btn-success"],
+ ]
+ );
+
+ $builder->add('id',
+ TextType::class,
+ array("label" =>"Clé",
+ 'disabled' => true));
+
+ switch($options["type"]) {
+ case "string":
+ $builder->add('value',
+ TextType::class,
+ array("label" => "Valeur",
+ 'required' => ($options["required"]==0?false:true)));
+ break;
+
+ case "boolean":
+ $choices=["oui" => "1","non" => "0"];
+ $builder->add("value", ChoiceType::class,
+ array("label" =>"Valeur",
+ 'required' => ($options["required"]==0?false:true),
+ "choices" => $choices));
+ break;
+
+ case "integer":
+ $builder->add("value",
+ IntegerType::class, [
+ "label" =>"Valeur",
+ "attr" => ["min" => "0"],
+ "required" => ($options["required"]==0?false:true),
+ ]
+ );
+ break;
+
+ case "pourcentage":
+ $builder->add("value",
+ IntegerType::class, [
+ "label" =>"Valeur",
+ "attr" => ["min" => "0", "max"=>"100"],
+ "required" => ($options["required"]==0?false:true),
+ ]
+ );
+ break;
+
+ case "font":
+ $choices=[
+ "ABeeZee-Regular" => "ABeeZee-Regular",
+ "Acme-Regular" => "Acme-Regular",
+ "AlfaSlabOne-Regular" => "AlfaSlabOne-Regular",
+ "Anton-Regular" => "Anton-Regular",
+ "Baloo-Regular" => "Baloo-Regular",
+ "CarterOne-Regular" => "CarterOne-Regular",
+ "Chewy-Regular" => "Chewy-Regular",
+ "Courgette-Regular" => "Courgette-Regular",
+ "FredokaOne-Regular" => "FredokaOne-Regular",
+ "Grandstander" => "Grandstander",
+ "Helvetica" => "Helvetica",
+ "Justanotherhand-Regular" => "Justanotherhand-Regular",
+ "Lato-Regular" => "Lato-Regular",
+ "LexendDeca-Regular" => "LexendDeca-Regular",
+ "LuckiestGuy-Regular" => "LuckiestGuy-Regular",
+ "Overpass-Black" => "Overpass-Black",
+ "PassionOne" => "PassionOne",
+ "Peacesans" => "Peacesans",
+ "Redressed" => "Redressed",
+ "Righteous-Regular" => "Righteous-Regular",
+ "Roboto-Regular" => "Roboto-Regular",
+ "RubikMonoOne-Regular" => "RubikMonoOne-Regular",
+ "SigmarOne-Regular" => "SigmarOne-Regular",
+ "Signika-Regular" => "Signika-Regular",
+ "Teko-Bold" => "Teko-Bold",
+ "Theboldfont" => "Theboldfont",
+ "Viga-Regular" => "Viga-Regular",
+ ];
+
+ $builder->add("value", ChoiceType::class,
+ array("label" =>"Valeur",
+ 'required' => ($options["required"]==0?false:true),
+ "choices" => $choices));
+ break;
+
+ case "editor":
+ $builder->add('value',
+ CKEditorType::class,[
+ "required" => ($options["required"]==0?false:true),
+ "config_name" => "full_config",
+ "config" => [
+ 'height' => 600,
+ 'filebrowserUploadRoute' => 'app_ckeditor_upload',
+ ]
+ ]
+ );
+ break;
+
+
+ case "role":
+ $choices=array(
+ "NO_BODY" => "NO_BODY",
+ "ROLE_USER" => "ROLE_USER",
+ "ROLE_MASTER" => "ROLE_MASTER",
+ "ROLE_MODO" => "ROLE_MODO",
+ );
+
+ $builder->add("value", ChoiceType::class,
+ array("label" =>"Valeur",
+ "label_attr" => array("style" => 'margin-top:15px;'),
+ "attr" => array("class" => "form-control"),
+ 'required' => ($options["required"]==0?false:true),
+ "choices" => $choices));
+ break;
+
+
+ case "scopeannu":
+ $choices=array(
+ "ALL" => "ALL",
+ "SAME_NIVEAU01" => "SAME_NIVEAU01",
+ "SAME_NIVEAU02" => "SAME_NIVEAU02",
+ );
+
+ $builder->add("value", ChoiceType::class,
+ array("label" =>"Valeur",
+ "label_attr" => array("style" => 'margin-top:15px;'),
+ "attr" => array("class" => "form-control"),
+ 'required' => ($options["required"]==0?false:true),
+ "choices" => $choices));
+ break;
+
+ case "logo":
+ $builder->add('value',HiddenType::class);
+ break;
+
+ case "header":
+ $builder->add('value',HiddenType::class);
+ break;
+
+ case "image":
+ $builder->add('value',HiddenType::class);
+ break;
+
+ case "color":
+ $builder->add('value',
+ TextType::class,
+ array("label" => "Valeur",
+ "attr" => ["class" => "pick-a-color"],
+ 'required' => ($options["required"]==0?false:true)));
+ break;
+ }
+
+ $builder->add('help',
+ TextareaType::class,
+ array("label" =>"Aide",
+ "attr" => ["style" => "height: 200px;"],
+ 'required' => false,
+ 'disabled' => true));
+ }
+
+ public function configureOptions(OptionsResolver $resolver)
+ {
+ $resolver->setDefaults(array(
+ 'data_class' => 'App\Entity\Config',
+ 'mode' => "string",
+ 'id' => "string",
+ 'type' => "string",
+ 'required' => "string",
+ ));
+ }
+}
diff --git a/src/Form/CronType.php b/src/Form/CronType.php
new file mode 100644
index 0000000..6429767
--- /dev/null
+++ b/src/Form/CronType.php
@@ -0,0 +1,66 @@
+add('submit', SubmitType::class, [
+ "label" => "Valider",
+ "attr" => array("class" => "btn btn-success")
+ ])
+
+ ->add('command', TextType::class, [
+ 'label' => 'Commande',
+ "disabled" => true,
+ ])
+
+ ->add('jsonargument', TextType::class, [
+ 'label' => 'Argument Commande au format json',
+ "disabled" => true,
+ ])
+
+ ->add('statut', ChoiceType::class, [
+ 'label' => "Statut",
+ 'choices' => array("Désactivé" => -1,"KO" => "0","OK" => "1")
+ ])
+
+ ->add('repeatinterval', IntegerType::class, [
+ 'label' => "Interval en seconde entre deux éxécution"
+ ])
+
+ ->add('nextexecdate', DatetimeType::class, [
+ 'label' => "Prochaine exécution",
+ 'widget' => 'single_text',
+ "html5"=>true,
+ 'input_format' => "d/m/Y H:i"
+ ])
+ ;
+ }
+
+ /**
+ * @param OptionsResolver $resolver
+ */
+ public function configureOptions(OptionsResolver $resolver)
+ {
+ $resolver->setDefaults([
+ 'data_class' => 'App\Entity\Cron',
+ 'mode' => 'string'
+ ]);
+ }
+}
diff --git a/src/Form/GroupType.php b/src/Form/GroupType.php
new file mode 100644
index 0000000..a86caf5
--- /dev/null
+++ b/src/Form/GroupType.php
@@ -0,0 +1,142 @@
+add('submit',
+ SubmitType::class,[
+ "label" => "Valider",
+ "attr" => ["class" => "btn btn-success"],
+ ]
+ );
+
+ if($options["access"]=="admin") {
+ $builder->add('isworkgroup',
+ ChoiceType::class,[
+ "label" =>"Groupe de Travail",
+ "choices" => ["non" => "0","oui" => "1"],
+ ]
+ );
+ }
+
+ if($options["access"]=="admin" || $options["mode"] == "update") {
+ $builder->add('owner',
+ Select2EntityType::class, [
+ "label" => "Propriétaire",
+ "required" => false,
+ "multiple" => false,
+ "remote_route" => 'app_'.$options["access"].'_user_selectlist',
+ "class" => 'App\Entity\User',
+ "primary_key" => 'id',
+ "text_property" => 'username',
+ "minimum_input_length" => 2,
+ "page_limit" => 10,
+ "allow_clear" => true,
+ "delay" => 250,
+ "cache" => false,
+ "cache_timeout" => 60000, // if 'cache' is true
+ "language" => 'fr',
+ "placeholder" => 'Selectionner un propriétaire',
+ ]
+ );
+ }
+
+ if($options["access"]=="admin") {
+ $builder->add('email',
+ EmailType::class, [
+ "label" => "Mail",
+ "required" => false,
+ ]
+ );
+ }
+
+ $builder->add("description",
+ TextareaType::class, [
+ "label" => 'Description',
+ "required" => false,
+ "attr" => ["rows" => '4'],
+ ]
+ );
+
+ $builder->add('label',
+ TextType::class, [
+ "label" =>"Label",
+ ]
+ );
+
+ $builder->add("isopen",
+ ChoiceType::class,array(
+ "label" =>"Groupe Ouvert (inscription possible par les utilisateurs)",
+ "choices" => ["non" => "0","oui" => "1"],
+ )
+ );
+
+ // Si masteridentity = LDAP alors on demande le filtre des utilisateurs qui appartiennent à ce groupe
+ if($options["appMasteridentity"]=="LDAP"&&$options["access"]=="admin")
+ {
+ $builder->add("fgassoc",
+ ChoiceType::class,[
+ "mapped" => false,
+ "label" => "Groupe associé à l'annuaire ?",
+ "choices" => ["non" => "0","oui" => "1"],
+ ]
+ );
+
+ $builder->add('ldapfilter',
+ TextType::class, [
+ "label" => "Filtre LDAP des utilisateurs",
+ "label_attr" => ["id" => "label_group_ldapfilter"],
+ "required" => false,
+ ]
+ );
+ }
+
+ if($options["appMasteridentity"]=="SSO"&&$options["access"]=="admin")
+ {
+ $builder->add("fgassoc",
+ ChoiceType::class,[
+ "mapped" => false,
+ "label" => "Groupe associé à des attributs SSO ?",
+ "choices" => ["non" => "0","oui" => "1"],
+ ]
+ );
+
+ $builder->add('attributes',
+ TextareaType::class, [
+ "label" => "Attributs SSO des utilisateurs",
+ "label_attr" => ["id" => "label_group_attributes"],
+ "required" => false,
+ "attr" => ["rows" => 10]
+ ]
+ );
+ }
+ }
+
+ public function configureOptions(OptionsResolver $resolver)
+ {
+ $resolver->setDefaults(array(
+ 'data_class' => 'App\Entity\Group',
+ 'mode' => "string",
+ 'access' => "string",
+ 'appMasteridentity' => "string",
+ ));
+ }
+}
diff --git a/src/Form/LoginType.php b/src/Form/LoginType.php
new file mode 100644
index 0000000..9467ad8
--- /dev/null
+++ b/src/Form/LoginType.php
@@ -0,0 +1,37 @@
+add('submit',
+ SubmitType::class,[
+ "label" => "Valider",
+ "attr" => ["class" => "btn btn-success mt-4 float-end"],
+ ]
+ );
+
+ $builder->add('username',
+ TextType::class,[
+ "label" =>"Login",
+ "attr" => ["autocomplete" => "new-password"]
+ ]
+ );
+
+ $builder->add('password',
+ PasswordType::class, [
+ "always_empty" => true,
+ "label" => "Mot de Passe",
+ "attr" => ["autocomplete" => "new-password"]
+ ]
+ );
+
+ }
+}
diff --git a/src/Form/Niveau01Type.php b/src/Form/Niveau01Type.php
new file mode 100644
index 0000000..0b27ddb
--- /dev/null
+++ b/src/Form/Niveau01Type.php
@@ -0,0 +1,81 @@
+add('submit',
+ SubmitType::class,[
+ "label" => "Valider",
+ "attr" => ["class" => "btn btn-success"],
+ ]
+ );
+
+ $builder->add('label',
+ TextType::class, [
+ "label" =>"Label",
+ ]
+ );
+
+ // Si masteridentity = LDAP alors on demande le filtre des utilisateurs qui appartiennent à ce groupe
+ if($options["appMasteridentity"]=="LDAP"||$options["appSynchro"]=="LDAP2NINE")
+ {
+ $builder->add("fgassocldap",
+ ChoiceType::class,[
+ "mapped" => false,
+ "label" => $options["appNiveau01label"]." associé à l'annuaire ?",
+ "choices" => ["non" => "0","oui" => "1"],
+ ]
+ );
+
+ $builder->add('ldapfilter',
+ TextType::class, [
+ "label" => "Filtre LDAP du ".$options["appNiveau01label"],
+ "label_attr" => ["id" => "label_group_ldapfilter"],
+ "required" => false,
+ ]
+ );
+ }
+
+ if($options["appMasteridentity"]=="SSO")
+ {
+ $builder->add("fgassocsso",
+ ChoiceType::class,[
+ "mapped" => false,
+ "label" => $options["appNiveau01label"]." associé à des attributs SSO ?",
+ "choices" => ["non" => "0","oui" => "1"],
+ ]
+ );
+
+ $builder->add('attributes',
+ TextareaType::class, [
+ "label" => "Attributs SSO du ".$options["appNiveau01label"],
+ "label_attr" => ["id" => "label_group_attributes"],
+ "required" => false,
+ "attr" => ["rows" => 10]
+ ]
+ );
+ }
+
+ }
+
+ public function configureOptions(OptionsResolver $resolver)
+ {
+ $resolver->setDefaults(array(
+ 'data_class' => 'App\Entity\Niveau01',
+ 'mode' => "string",
+ 'appMasteridentity' => "string",
+ "appSynchro" => "string",
+ 'appNiveau01label' => "string"
+ ));
+ }
+}
diff --git a/src/Form/Niveau02Type.php b/src/Form/Niveau02Type.php
new file mode 100644
index 0000000..fcd904c
--- /dev/null
+++ b/src/Form/Niveau02Type.php
@@ -0,0 +1,70 @@
+add('submit',
+ SubmitType::class,[
+ "label" => "Valider",
+ "attr" => ["class" => "btn btn-success"],
+ ]
+ );
+
+ $access=$options["access"];
+ $userid=$options["userid"];
+ $builder->add('niveau01',
+ EntityType::class, [
+ "class" => "App\Entity\Niveau01",
+ "label" => $options["appNiveau01label"],
+ "placeholder" => "== Choisir ".$options["appNiveau01label"]." ==",
+ "choice_label" => "label",
+ "disabled" => ($options["mode"]!="submit"),
+ "query_builder"=> function (EntityRepository $er) use($access,$userid) {
+ switch($access) {
+ case "admin":
+ return $er->createQueryBuilder('niveau01')->orderBy('niveau01.label','ASC');
+ break;
+
+ case "modo":
+ $result=$er->createQueryBuilder("table")->innerJoin("App:UserModo", "usermodo", Join::WITH, "table.id = usermodo.niveau01")->orderBy('table.label','ASC');
+ $result->andWhere("usermodo.user = :user");
+ $result->setParameter('user', $userid);
+ return $result;
+ break;
+ }
+ },
+ ]
+ );
+
+ $builder->add('label',
+ TextType::class, [
+ "label" =>"Label",
+ ]
+ );
+ }
+
+ public function configureOptions(OptionsResolver $resolver)
+ {
+ $resolver->setDefaults(array(
+ 'data_class' => 'App\Entity\Niveau02',
+ 'mode' => "string",
+ 'access' => "string",
+ 'userid' => "string",
+ 'appMasteridentity' => "string",
+ 'appNiveau01label' => "string",
+ 'appNiveau02label' => "string"
+ ));
+ }
+}
diff --git a/src/Form/RegistrationType.php b/src/Form/RegistrationType.php
new file mode 100644
index 0000000..079b0ce
--- /dev/null
+++ b/src/Form/RegistrationType.php
@@ -0,0 +1,212 @@
+add('submit',
+ SubmitType::class,[
+ "label" => ($options["mode"]=="submit"?"Confirmer":"Enregistrer et envoyer le mail de confirmation"),
+ "attr" => ["class" => "btn btn-success"],
+ ]
+ );
+
+ if($options["mode"]=="update") {
+ $builder->add('save',
+ SubmitType::class, array(
+ "label" => "Enregistrer sans envoyer le mail de confirmation",
+ "attr" => array("class" => "btn btn-success")
+ )
+ );
+
+ $builder->add('note',
+ TextareaType::class, array(
+ "label" => "Notes Administrateur",
+ "required" => false,
+ "disabled" => ($options["mode"]=="delete"?true:false),
+ "attr" => array("class" => "form-control", "style" => "margin-bottom:15px; height: 130px")
+ )
+ );
+ }
+
+ $builder->add('username',
+ TextType::class,[
+ "label" =>"Login",
+ "disabled" => ($options["mode"]!="submit"),
+ "attr" => ["autocomplete" => "new-password"]
+ ]
+ );
+
+ $builder->add('lastname',
+ TextType::class, [
+ "label" =>"Nom",
+ "disabled" => ($options["appMasteridentity"]!="SQL"&&$options["mode"]!="submit"),
+ ]
+ );
+
+ $builder->add('firstname',
+ TextType::class, [
+ "label" =>"Prénom",
+ "disabled" => ($options["appMasteridentity"]!="SQL"&&$options["mode"]!="submit"),
+ ]
+ );
+
+ $builder->add('email',
+ EmailType::class, array(
+ "label" =>"Mail",
+ "disabled" => ($options["appMasteridentity"]!="SQL")&&$options["mode"]!="submit",
+ )
+ );
+
+ $access=$options["access"];
+ $userid=$options["userid"];
+ $builder->add('niveau01',
+ EntityType::class, [
+ "class" => "App\Entity\Niveau01",
+ "label" => $options["appNiveau01label"],
+ "placeholder" => "== Choisir ".$options["appNiveau01label"]." ==",
+ "choice_label" => "label",
+ "disabled" => ($options["appMasteridentity"]!="SQL"&&$options["mode"]!="submit"),
+ "query_builder"=> function (EntityRepository $er) use($access,$userid) {
+ switch($access) {
+ case "admin":
+ return $er->createQueryBuilder('niveau01')->orderBy('niveau01.label','ASC');
+ break;
+
+ case "modo":
+ $result=$er->createQueryBuilder("table")->innerJoin("App:UserModo", "usermodo", Join::WITH, "table.id = usermodo.niveau01")->orderBy('table.label','ASC');
+ $result->andWhere("usermodo.user = :user");
+ $result->setParameter('user', $userid);
+ return $result;
+ break;
+
+ default:
+ return $er->createQueryBuilder('niveau01')->orderBy('niveau01.label','ASC');
+ break;
+ }
+ },
+ ]
+ );
+
+ $builder->add('niveau02',
+ Select2EntityType::class, [
+ "label" => $options["appNiveau02label"],
+ "required" => false,
+ "remote_route" => "app_niveau02_selectlist",
+ "class" => "App\Entity\Niveau02",
+ //"req_params" => ["niveau01" => "parent.children[niveau01]"],
+ "primary_key" => "id",
+ "text_property" => "label",
+ "minimum_input_length" => 0,
+ "page_limit" => 10,
+ "allow_clear" => true,
+ "delay" => 250,
+ "cache" => false,
+ "cache_timeout" => 60000,
+ "language" => "fr",
+ "placeholder" => "== Choisir ".$options["appNiveau02label"]." ==",
+ ]
+ );
+
+ # Password
+ if($options["mode"]=="submit") {
+ $builder->add('password',
+ RepeatedType::class, array(
+ "type" => PasswordType::class,
+ "required" => ($options["mode"]=="submit"?true:false),
+ "first_options" => array("label" => "Mot de Passe","attr" => array("class" => "form-control", "style" => "margin-bottom:15px", "autocomplete" => "new-password")),
+ "second_options" => array('label' => 'Confirmer Mot de Passe',"attr" => array("class" => "form-control", "style" => "margin-bottom:15px")),
+ "invalid_message" => "Mot de passe non valide"
+ )
+ );
+ $builder->add('passwordplain',PasswordType::class,["mapped"=>false,"required"=>false]);
+
+ $builder->add('captcha',
+ CaptchaType::class,array(
+ "width" => 200,
+ "height" => 50,
+ "length" => 6,
+ )
+ );
+ }
+
+
+ $choices=array("oui" => "1","non" => "0");
+ $builder->add("isvisible",
+ ChoiceType::class,array(
+ "label" =>"Visible",
+ "choices" => $choices
+ )
+ );
+
+ $builder->add('postaladress',
+ TextareaType::class, [
+ "label" => "Adresse",
+ "required" => false,
+ "attr" => ["style" => "height:90px"]
+ ]
+ );
+
+ $builder->add('telephonenumber',
+ TextType::class, [
+ "label" => "Téléphone",
+ "required" => false,
+ ]
+ );
+
+ $builder->add('job',
+ TextType::class, [
+ "label" => "Métier",
+ "required" => false,
+ ]
+ );
+
+ $builder->add('position',
+ TextType::class, [
+ "label" => "Fonction",
+ "required" => false,
+ ]
+ );
+
+ $builder->add('motivation',
+ TextareaType::class, [
+ "label" => "Motivation",
+ "required" => false,
+ "attr" => ["style" => "height: 90px"],
+ ]
+ );
+ }
+
+ public function configureOptions(OptionsResolver $resolver)
+ {
+ $resolver->setDefaults(array(
+ 'data_class' => 'App\Entity\Registration',
+ 'mode' => "string",
+ 'access' => "string",
+ 'userid' => "string",
+ 'appMasteridentity' => "string",
+ 'appNiveau01label' => "string",
+ 'appNiveau02label' => "string",
+ ));
+ }
+}
diff --git a/src/Form/ResetpwdType.php b/src/Form/ResetpwdType.php
new file mode 100644
index 0000000..1f46953
--- /dev/null
+++ b/src/Form/ResetpwdType.php
@@ -0,0 +1,55 @@
+add('submit',
+ SubmitType::class,[
+ "label" => "Valider",
+ "attr" => ["class" => "btn btn-success"],
+ ]
+ );
+
+ if($options["mode"]=="resetpwd01") {
+ $builder->add('email',
+ TextType::class, array(
+ "label" =>"Votre Mail",
+ "disabled" => ($options["mode"]=="delete"?true:false),
+ "attr" => array("class" => "form-control", "style" => "margin-bottom:15px")
+ )
+ );
+ }
+ else {
+ $builder->add('password',
+ RepeatedType::class, array(
+ "type" => PasswordType::class,
+ "required" => ($options["mode"]=="submit"?true:false),
+ "options" => array("always_empty" => true),
+ "first_options" => array("label" => "Votre nouveau Mot de Passe","attr" => array("class" => "form-control", "style" => "margin-bottom:15px")),
+ "second_options" => array('label' => 'Confirmer votre nouveau Mot de Passe',"attr" => array("class" => "form-control", "style" => "margin-bottom:15px")),
+ "invalid_message" => "Mot de passe non valide"
+ )
+ );
+ $builder->add('passwordplain',PasswordType::class,["mapped"=>false,"required"=>false]);
+ }
+ }
+
+ public function configureOptions(OptionsResolver $resolver)
+ {
+ $resolver->setDefaults(array(
+ 'data_class' => 'App\Entity\User',
+ 'mode' => "string"
+ ));
+ }
+}
diff --git a/src/Form/UserType.php b/src/Form/UserType.php
new file mode 100644
index 0000000..fb789e2
--- /dev/null
+++ b/src/Form/UserType.php
@@ -0,0 +1,232 @@
+add('submit',
+ SubmitType::class,[
+ "label" => "Valider",
+ "attr" => ["class" => "btn btn-success"],
+ ]
+ );
+
+ $builder->add('username',
+ TextType::class,[
+ "label" =>"Login",
+ "disabled" => ($options["mode"]!="submit"),
+ "attr" => ["autocomplete" => "new-password"]
+ ]
+ );
+
+ if($options["appMasteridentity"]=="SQL"||$options["mode"]=="submit") {
+ $builder->add('password',
+ RepeatedType::class, [
+ "type" => PasswordType::class,
+ "required" => ($options["mode"]=="submit"),
+ "options" => ["always_empty" => true],
+ "first_options" => ["label" => "Mot de Passe","attr" => ["autocomplete" => "new-password"]],
+ "second_options" => ["label" => 'Confirmer Mot de Passe'],
+ "invalid_message" => "Mot de passe non valide"
+ ]
+ );
+ $builder->add('passwordplain',PasswordType::class,["mapped"=>false,"required"=>false]);
+ }
+
+ $builder->add('lastname',
+ TextType::class, [
+ "label" =>"Nom",
+ "disabled" => ($options["appMasteridentity"]!="SQL"&&$options["mode"]!="submit"),
+ ]
+ );
+
+ $builder->add('firstname',
+ TextType::class, [
+ "label" =>"Prénom",
+ "disabled" => ($options["appMasteridentity"]!="SQL"&&$options["mode"]!="submit"),
+ ]
+ );
+
+ $builder->add('email',
+ EmailType::class, array(
+ "label" =>"Mail",
+ "disabled" => ($options["appMasteridentity"]!="SQL")&&$options["mode"]!="submit",
+ )
+ );
+
+ $access=$options["access"];
+ $userid=$options["userid"];
+ $builder->add('niveau01',
+ EntityType::class, [
+ "class" => "App\Entity\Niveau01",
+ "label" => $options["appNiveau01label"],
+ "placeholder" => "== Choisir ".$options["appNiveau01label"]." ==",
+ "choice_label" => "label",
+ "disabled" => ($options["appMasteridentity"]!="SQL"&&$options["mode"]!="submit"),
+ "query_builder"=> function (EntityRepository $er) use($access,$userid) {
+ switch($access) {
+ case "admin":
+ return $er->createQueryBuilder('niveau01')->orderBy('niveau01.label','ASC');
+ break;
+
+ case "modo":
+ $result=$er->createQueryBuilder("table")->innerJoin("App:UserModo", "usermodo", Join::WITH, "table.id = usermodo.niveau01")->orderBy('table.label','ASC');
+ $result->andWhere("usermodo.user = :user");
+ $result->setParameter('user', $userid);
+ return $result;
+ break;
+
+ default:
+ return $er->createQueryBuilder('niveau01')->orderBy('niveau01.label','ASC');
+ break;
+ }
+ },
+ ]
+ );
+
+ $builder->add('niveau02',
+ Select2EntityType::class, [
+ "label" => $options["appNiveau02label"],
+ "required" => false,
+ "remote_route" => "app_niveau02_selectlist",
+ "class" => "App\Entity\Niveau02",
+ //"req_params" => ["niveau01" => "parent.children[niveau01]"],
+ "primary_key" => "id",
+ "text_property" => "label",
+ "minimum_input_length" => 0,
+ "page_limit" => 10,
+ "allow_clear" => true,
+ "delay" => 250,
+ "cache" => false,
+ "cache_timeout" => 60000,
+ "language" => "fr",
+ "placeholder" => "== Choisir ".$options["appNiveau02label"]." ==",
+ ]
+ );
+
+ $choices=array("oui" => "1","non" => "0");
+ $builder->add("isvisible",
+ ChoiceType::class,array(
+ "label" =>"Visible",
+ "choices" => $choices
+ )
+ );
+
+ $builder->add('postaladress',
+ TextareaType::class, [
+ "label" => "Adresse",
+ "required" => false,
+ "attr" => ["style" => "height:90px"]
+ ]
+ );
+
+ $builder->add('telephonenumber',
+ TextType::class, [
+ "label" => "Téléphone",
+ "required" => false,
+ ]
+ );
+
+ $builder->add('job',
+ TextType::class, [
+ "label" => "Métier",
+ "required" => false,
+ ]
+ );
+
+ $builder->add('position',
+ TextType::class, [
+ "label" => "Fonction",
+ "required" => false,
+ ]
+ );
+
+
+ $builder->add('visitedate',
+ DateTimeType::class, [
+ "label" => "Date de dernière visite",
+ "disabled" => true,
+ "required" => false,
+ "widget" => 'single_text',
+ ]
+ );
+
+ $builder->add('visitecpt',
+ IntegerType::class, [
+ "label" => "Nombre de visites",
+ "disabled" => true,
+ "required" => false,
+ ]
+ );
+
+ $builder->add('motivation',
+ TextareaType::class, [
+ "label" => "Motivation",
+ "required" => false,
+ "attr" => ["style" => "height: 90px"],
+ ]
+ );
+
+ $builder->add('avatar',HiddenType::class);
+ $builder->add('linkgroups',HiddenType::class, array("mapped" => false));
+ $builder->add('linkmodos',HiddenType::class, array("mapped" => false));
+
+ if($options["access"]=="admin" || $options["access"]=="modo") {
+ $choices=array("ROLE_ADMIN" => "ROLE_ADMIN","ROLE_MODO" => "ROLE_MODO","ROLE_MASTER" => "ROLE_MASTER","ROLE_USER" => "ROLE_USER");
+ $builder->add("roles",
+ ChoiceType::class,[
+ "label" =>"Rôle",
+ "required" => true,
+ "multiple" => true,
+ "expanded" => true,
+ "choices" => $choices
+ ]
+ );
+
+ $builder->add('note',
+ TextareaType::class, [
+ "label" => "Notes Administrateur",
+ "required" => false,
+ "attr" => ["style" => "height: 130px"]
+ ]
+ );
+ }
+
+ }
+
+ public function configureOptions(OptionsResolver $resolver)
+ {
+ $resolver->setDefaults(array(
+ 'data_class' => 'App\Entity\User',
+ 'mode' => "string",
+ 'access' => "string",
+ 'userid' => "string",
+ 'appMasteridentity' => "string",
+ 'appNiveau01label' => "string",
+ 'appNiveau02label' => "string",
+ ));
+ }
+}
+
diff --git a/src/Form/WhitelistType.php b/src/Form/WhitelistType.php
new file mode 100644
index 0000000..7ecc1af
--- /dev/null
+++ b/src/Form/WhitelistType.php
@@ -0,0 +1,35 @@
+add('submit',
+ SubmitType::class,[
+ "label" => "Valider",
+ "attr" => ["class" => "btn btn-success"],
+ ]
+ );
+
+ $builder->add('label',
+ TextType::class, [
+ "label" =>"Label",
+ ]
+ );
+ }
+
+ public function configureOptions(OptionsResolver $resolver)
+ {
+ $resolver->setDefaults(array(
+ 'data_class' => 'App\Entity\Whitelist',
+ 'mode' => "string"
+ ));
+ }
+}
diff --git a/src/Kernel.php b/src/Kernel.php
new file mode 100644
index 0000000..779cd1f
--- /dev/null
+++ b/src/Kernel.php
@@ -0,0 +1,11 @@
+getEntityManager()->persist($entity);
+
+ if ($flush) {
+ $this->getEntityManager()->flush();
+ }
+ }
+
+ public function remove(Config $entity, bool $flush = false): void
+ {
+ $this->getEntityManager()->remove($entity);
+
+ if ($flush) {
+ $this->getEntityManager()->flush();
+ }
+ }
+}
diff --git a/src/Repository/CronRepository.php b/src/Repository/CronRepository.php
new file mode 100644
index 0000000..1a60900
--- /dev/null
+++ b/src/Repository/CronRepository.php
@@ -0,0 +1,51 @@
+getEntityManager()->persist($entity);
+
+ if ($flush) {
+ $this->getEntityManager()->flush();
+ }
+ }
+
+ public function remove(Cron $entity, bool $flush = false): void
+ {
+ $this->getEntityManager()->remove($entity);
+
+ if ($flush) {
+ $this->getEntityManager()->flush();
+ }
+ }
+
+ public function toExec()
+ {
+ // Les commandes à executer
+ // = statut = 0 (à executer)
+ // = statut = 2 (OK) et derniere execution + interval > now et nombre d'appel = 0
+ // = statut = 3 (KO) et derniere execution + interval > now et nombre d'appel = 0
+ // = statut = 3 (KO) et nombre d'execution < nombre d'appel
+
+
+ $now=new \DateTime();
+
+ $qb = $this->createQueryBuilder('cron')
+ ->Where('cron.statut=0')
+ ->orWhere('(cron.statut=0 OR cron.statut=1) AND cron.nextexecdate<:now');
+
+ return $qb->getQuery()->setParameter('now',$now->format("Y-m-d H:i:s"))->getResult();
+ }
+}
diff --git a/src/Repository/GroupRepository.php b/src/Repository/GroupRepository.php
new file mode 100644
index 0000000..82da0ba
--- /dev/null
+++ b/src/Repository/GroupRepository.php
@@ -0,0 +1,98 @@
+getEntityManager()->persist($entity);
+
+ if ($flush) {
+ $this->getEntityManager()->flush();
+ }
+ }
+
+ public function remove(Group $entity, bool $flush = false): void
+ {
+ $this->getEntityManager()->remove($entity);
+
+ if ($flush) {
+ $this->getEntityManager()->flush();
+ }
+ }
+
+ /* Déterminer les groupes d'un user SSO en fonction de ses attributs */
+ public function calculateSSOGroup($user,$attruser)
+ {
+ $groups = $this->_em->getRepository('App\Entity\Group')->findAll();
+ $retgroups= new ArrayCollection();
+ foreach($groups as $group) {
+ if($group->getAttributes()) {
+ $attgroup=json_decode($group->getAttributes(),true);
+
+ foreach($attgroup as $key => $value) {
+ if(array_key_exists($key,$attruser)) {
+ if(is_array($attruser[$key])) {
+ foreach($attruser[$key] as $val) {
+ if($value=="*")
+ $retgroups->add($group);
+ elseif($val==$value)
+ $retgroups->add($group);
+ }
+ }
+ else {
+ if($value=="*")
+ $retgroups->add($group);
+ elseif($value==$attruser[$key])
+ $retgroups->add($group);
+ }
+ }
+ }
+ }
+ }
+
+ // Pour chaque groupe de l'utilisateur
+ $usergroups=$user->getGroups();
+
+ // On le détache des groupes auxquelles il n'appartient plus
+ if($usergroups) {
+ foreach($usergroups as $usergroup) {
+ if($usergroup->getGroup()->getAttributes()!="") {
+ if(!$retgroups->contains($usergroup->getGroup())) {
+ $user->removeGroup($usergroup);
+ }
+ }
+ }
+ }
+
+ // On attache le user aux groupes
+ foreach($retgroups as $retgroup) {
+ $usergroup=$this->_em->getRepository('App\Entity\UserGroup')->findBy(["user"=>$user,"group"=>$retgroup]);
+ if(!$usergroup) {
+ $usergroup=new UserGroup();
+ $usergroup->setUser($user);
+ $usergroup->setGroup($retgroup);
+ $usergroup->setApikey(Uuid::uuid4());
+ $usergroup->setRolegroup(0);
+
+ $this->_em->persist($usergroup);
+ $this->_em->flush();
+ }
+ }
+
+ return $user;
+ }
+}
diff --git a/src/Repository/Niveau01Repository.php b/src/Repository/Niveau01Repository.php
new file mode 100644
index 0000000..210e921
--- /dev/null
+++ b/src/Repository/Niveau01Repository.php
@@ -0,0 +1,83 @@
+ldapservice=$ldapservice;
+ }
+
+ public function add(Niveau01 $entity, bool $flush = false): void
+ {
+ $this->getEntityManager()->persist($entity);
+
+ if ($flush) {
+ $this->getEntityManager()->flush();
+ }
+ }
+
+ public function remove(Niveau01 $entity, bool $flush = false): void
+ {
+ $this->getEntityManager()->remove($entity);
+
+ if ($flush) {
+ $this->getEntityManager()->flush();
+ }
+ }
+
+ /* Déterminer le niveau01 d'un user SSO en fonction de ses attributs */
+ public function calculateSSONiveau01($attruser)
+ {
+ $niveau01s = $this->_em->getRepository('App\Entity\Niveau01')->findAll();
+ foreach($niveau01s as $niveau01) {
+ if($niveau01->getAttributes()) {
+ $attniveau=json_decode($niveau01->getAttributes(),true);
+
+ foreach($attniveau as $key => $value) {
+ if(array_key_exists($key,$attruser)) {
+ if(is_array($attruser[$key])) {
+ foreach($attruser[$key] as $val) {
+ if($value=="*")
+ return $niveau01;
+ elseif($val==$value)
+ return $niveau01;
+ }
+ }
+ else {
+ if($value=="*")
+ return $niveau01;
+ elseif($value==$attruser[$key])
+ return $niveau01;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /* Déterminer le niveau01 d'un user LDAP */
+ public function calculateLDAPNiveau01($username)
+ {
+ $niveau01s = $this->_em->getRepository('App\Entity\Niveau01')->findAll();
+ foreach($niveau01s as $niveau01) {
+ if($niveau01->getLdapfilter()) {
+ $ismember=$this->ldapservice->findNiveau01ismember($niveau01->getLdapfilter(),$username);
+ if($ismember) return $niveau01;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/src/Repository/Niveau02Repository.php b/src/Repository/Niveau02Repository.php
new file mode 100644
index 0000000..d53ee55
--- /dev/null
+++ b/src/Repository/Niveau02Repository.php
@@ -0,0 +1,33 @@
+getEntityManager()->persist($entity);
+
+ if ($flush) {
+ $this->getEntityManager()->flush();
+ }
+ }
+
+ public function remove(Niveau02 $entity, bool $flush = false): void
+ {
+ $this->getEntityManager()->remove($entity);
+
+ if ($flush) {
+ $this->getEntityManager()->flush();
+ }
+ }
+}
diff --git a/src/Repository/RegistrationRepository.php b/src/Repository/RegistrationRepository.php
new file mode 100644
index 0000000..8eeabae
--- /dev/null
+++ b/src/Repository/RegistrationRepository.php
@@ -0,0 +1,33 @@
+getEntityManager()->persist($entity);
+
+ if ($flush) {
+ $this->getEntityManager()->flush();
+ }
+ }
+
+ public function remove(Registration $entity, bool $flush = false): void
+ {
+ $this->getEntityManager()->remove($entity);
+
+ if ($flush) {
+ $this->getEntityManager()->flush();
+ }
+ }
+}
diff --git a/src/Repository/StatisticRepository.php b/src/Repository/StatisticRepository.php
new file mode 100644
index 0000000..8ef48a7
--- /dev/null
+++ b/src/Repository/StatisticRepository.php
@@ -0,0 +1,33 @@
+getEntityManager()->persist($entity);
+
+ if ($flush) {
+ $this->getEntityManager()->flush();
+ }
+ }
+
+ public function remove(Statistic $entity, bool $flush = false): void
+ {
+ $this->getEntityManager()->remove($entity);
+
+ if ($flush) {
+ $this->getEntityManager()->flush();
+ }
+ }
+}
diff --git a/src/Repository/UserGroupRepository.php b/src/Repository/UserGroupRepository.php
new file mode 100644
index 0000000..87dc18f
--- /dev/null
+++ b/src/Repository/UserGroupRepository.php
@@ -0,0 +1,33 @@
+getEntityManager()->persist($entity);
+
+ if ($flush) {
+ $this->getEntityManager()->flush();
+ }
+ }
+
+ public function remove(UserGroup $entity, bool $flush = false): void
+ {
+ $this->getEntityManager()->remove($entity);
+
+ if ($flush) {
+ $this->getEntityManager()->flush();
+ }
+ }
+}
diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php
new file mode 100644
index 0000000..a035825
--- /dev/null
+++ b/src/Repository/UserRepository.php
@@ -0,0 +1,33 @@
+getEntityManager()->persist($entity);
+
+ if ($flush) {
+ $this->getEntityManager()->flush();
+ }
+ }
+
+ public function remove(User $entity, bool $flush = false): void
+ {
+ $this->getEntityManager()->remove($entity);
+
+ if ($flush) {
+ $this->getEntityManager()->flush();
+ }
+ }
+}
diff --git a/src/Service/ApiService.php b/src/Service/ApiService.php
new file mode 100644
index 0000000..0309488
--- /dev/null
+++ b/src/Service/ApiService.php
@@ -0,0 +1,89 @@
+params = $params;
+ }
+
+ public function setbody(Array $array)
+ {
+ return \Unirest\Request\Body::json($array);
+ }
+
+ public function run($method,$url,$query,$header=null) {
+ // Entete
+ $headerini = [
+ 'Accept' => 'application/json',
+ 'Content-Type' => 'application/json',
+ ];
+
+ if($header) $header=array_merge($headerini,$header);
+ else $header=$headerini;
+
+ // Paramétrage unirest
+ \Unirest\Request::verifyPeer(false);
+ \Unirest\Request::verifyHost(false);
+ \Unirest\Request::timeout(5);
+
+ // Déclaration du proxy
+ $proxyUse = $this->params->get("proxyUse");
+ if($proxyUse) {
+ $proxyHost = $this->params->get("proxyHost");
+ $proxyPort = $this->params->get("proxyPort");
+ \Unirest\Request::proxy($proxyHost, $proxyPort, CURLPROXY_HTTP, true);
+ }
+
+ if($query) $query = \Unirest\Request\Body::json($query);
+
+ $response = false;
+ switch($method) {
+ case "POST":
+ try{
+ $response = \Unirest\Request::post($url,$header,$query);
+ }
+ catch (\Exception $e) {
+ die();
+ return false;
+ }
+ break;
+
+ case "GET":
+ try{
+ $response = @\Unirest\Request::get($url,$header,$query);
+ }
+ catch (\Exception $e) {
+ dump($e);
+ return false;
+ }
+ break;
+
+ case "DELETE":
+ try{
+ $response = \Unirest\Request::delete($url,$header,$query);
+ }
+ catch (\Exception $e) {
+ return false;
+ }
+ break;
+
+ case "PATCH":
+ try{
+ $response = \Unirest\Request::patch($url,$header,$query);
+ }
+ catch (\Exception $e) {
+ return false;
+ }
+ break;
+ }
+ return $response;
+ }
+}
diff --git a/src/Service/AppSession.php b/src/Service/AppSession.php
new file mode 100644
index 0000000..0bcf9f6
--- /dev/null
+++ b/src/Service/AppSession.php
@@ -0,0 +1,155 @@
+container = $container;
+ $this->requeststack = $requeststack;
+ $this->em = $em;
+ $this->token = $token;
+ }
+
+ public function onDomainParse(RequestEvent $event) {
+ $session = $this->requeststack->getSession();
+
+ $configs = $this->em->getRepository("App\Entity\Config")->findAll();
+ foreach($configs as $config) {
+ $session->set($config->getId(), strval($config->getValue()));
+ }
+ $session->set("headerimage","uploads/header/".$session->get("headerimage"));
+
+ // Calcul couleur
+ $session->set("colorbgbodylight-darker", $this->adjustBrightness($session->get("colorbgbodylight"),-10));
+ $session->set("colorfttitlelight-darker", $this->adjustBrightness($session->get("colorfttitlelight"),-50));
+
+ $session->set("colorbgbodydark-darker", $this->adjustBrightness($session->get("colorbgbodydark"),-50));
+ $session->set("colorbgbodydark-lighter", $this->adjustBrightness($session->get("colorbgbodydark"),+50));
+
+ $session->set("colorbgbodydark-rgb", $this->hexToRgb($session->get("colorbgbodydark")));
+ $session->set("colorbgbodydark-darkrgb", $this->hexToRgb($session->get("colorbgbodydark-darker")));
+ $session->set("colorbgbodydark-lightrgb", $this->hexToRgb($session->get("colorbgbodydark-lighter")));
+
+ // Current user
+ $token = $this->token->getToken();
+ if(!$token) return;
+ $curentuser=$token->getUser();
+
+ // Préférence par défaut
+ $session->set("fgheader", true);
+
+ // Préférence
+ if($curentuser!="anon.") {
+ $preference=$curentuser->getPreference();
+ if(is_array($preference)) {
+ // Préférence header
+ if(array_key_exists("fgheader",$preference)) {
+ $fgheader=($preference["fgheader"][0]=="true");
+ $session->set("fgheader", $fgheader);
+ }
+ }
+ }
+
+ // Permissions
+ $showannuaire=false;
+ $submitgroup=false;
+ if($curentuser!="anon.") {
+ switch($session->get("permannu")) {
+ case "ROLE_USER" :
+ $showannuaire=($curentuser->hasRole("ROLE_ADMIN")||$curentuser->hasRole("ROLE_MODO")||$curentuser->hasRole("ROLE_MASTER")||$curentuser->hasRole("ROLE_USER"));
+ break;
+
+ case "ROLE_MASTER" :
+ $showannuaire=($curentuser->hasRole("ROLE_ADMIN")||$curentuser->hasRole("ROLE_MODO")||$curentuser->hasRole("ROLE_MASTER"));
+ break;
+
+ case "ROLE_MODO" :
+ $showannuaire=($curentuser->hasRole("ROLE_ADMIN")||$curentuser->hasRole("ROLE_MODO"));
+ break;
+ }
+
+ switch($session->get("permgroup")) {
+ case "ROLE_USER" :
+ $submitgroup=($curentuser->hasRole("ROLE_ADMIN")||$curentuser->hasRole("ROLE_MODO")||$curentuser->hasRole("ROLE_MASTER")||$curentuser->hasRole("ROLE_USER"));
+ break;
+
+ case "ROLE_MASTER" :
+ $submitgroup=($curentuser->hasRole("ROLE_ADMIN")||$curentuser->hasRole("ROLE_MODO")||$curentuser->hasRole("ROLE_MASTER"));
+ break;
+
+ case "ROLE_MODO" :
+ $submitgroup=($curentuser->hasRole("ROLE_ADMIN")||$curentuser->hasRole("ROLE_MODO"));
+ break;
+ }
+ }
+ $session->set("showannuaire", $showannuaire);
+ $session->set("submitgroup", $submitgroup);
+
+ // Visite
+ if($curentuser!="anon.") {
+ $now=new \DateTime();
+ if(!$curentuser->getVisitedate()) {
+ $curentuser->setVisitedate($now);
+ $curentuser->setVisitecpt($curentuser->getVisitecpt()+1);
+ $this->em->persist($curentuser);
+ $this->em->flush();
+ }
+ else {
+ $visitedate=clone $curentuser->getVisitedate();
+ $visitedate->add(new \DateInterval("PT1H"));
+ if($visitedate<$now) {
+ $curentuser->setVisitedate($now);
+ $curentuser->setVisitecpt($curentuser->getVisitecpt()+1);
+ $this->em->persist($curentuser);
+ $this->em->flush();
+ }
+ }
+ }
+ }
+
+
+ private function adjustBrightness($hex, $steps) {
+ // Steps should be between -255 and 255. Negative = darker, positive = lighter
+ $steps = max(-255, min(255, $steps));
+
+ // Normalize into a six character long hex string
+ $hex = str_replace('#', '', $hex);
+ if (strlen($hex) == 3) {
+ $hex = str_repeat(substr($hex,0,1), 2).str_repeat(substr($hex,1,1), 2).str_repeat(substr($hex,2,1), 2);
+ }
+
+ // Split into three parts: R, G and B
+ $color_parts = str_split($hex, 2);
+ $return = '';
+
+ foreach ($color_parts as $color) {
+ $color = hexdec($color); // Convert to decimal
+ $color = max(0,min(255,$color + $steps)); // Adjust color
+ $return .= str_pad(dechex($color), 2, '0', STR_PAD_LEFT); // Make two char hex code
+ }
+
+ return '#'.$return;
+ }
+
+ public function hexToRgb($hex) {
+ $hex = str_replace('#', '', $hex);
+ $length = strlen($hex);
+ $rgb['r'] = hexdec($length == 6 ? substr($hex, 0, 2) : ($length == 3 ? str_repeat(substr($hex, 0, 1), 2) : 0));
+ $rgb['g'] = hexdec($length == 6 ? substr($hex, 2, 2) : ($length == 3 ? str_repeat(substr($hex, 1, 1), 2) : 0));
+ $rgb['b'] = hexdec($length == 6 ? substr($hex, 4, 2) : ($length == 3 ? str_repeat(substr($hex, 2, 1), 2) : 0));
+
+ return $rgb['r'].",".$rgb['g'].",".$rgb['b'];
+ }
+ }
diff --git a/src/Service/LdapService.php b/src/Service/LdapService.php
new file mode 100644
index 0000000..bb2f3ca
--- /dev/null
+++ b/src/Service/LdapService.php
@@ -0,0 +1,841 @@
+appMasteridentity = $container->getParameter("appMasteridentity");
+ $this->synchro = $container->getParameter("appSynchro");
+ $this->host = $container->getParameter("ldapHost");
+ $this->port = $container->getParameter("ldapPort");
+ $this->usetls = $container->getParameter("ldapUsetls");
+ $this->userwriter = $container->getParameter("ldapUserwriter");
+ $this->user = $container->getParameter("ldapUser");
+ $this->password = $container->getParameter("ldapPassword");
+ $this->basedn = $container->getParameter("ldapBasedn");
+ $this->baseorganisation = $container->getParameter("ldapBaseorganisation");
+ $this->baseniveau01 = $container->getParameter("ldapBaseniveau01");
+ $this->baseniveau02 = $container->getParameter("ldapBaseniveau02");
+ $this->basegroup = $container->getParameter("ldapBasegroup");
+ $this->baseuser = $container->getParameter("ldapBaseuser");
+ $this->username = $container->getParameter("ldapUsername");
+ $this->firstname = $container->getParameter("ldapFirstname");
+ $this->lastname = $container->getParameter("ldapLastname");
+ $this->email = $container->getParameter("ldapEmail");
+ $this->avatar = $container->getParameter("ldapAvatar");
+ $this->memberof = $container->getParameter("ldapMemberof");
+ $this->groupgid = $container->getParameter("ldapGroupgid");
+ $this->groupname = $container->getParameter("ldapGroupname");
+ $this->groupmember = $container->getParameter("ldapGroupmember");
+ $this->groupmemberisdn = $container->getParameter("ldapGroupmemberisdn");
+ $this->filtergroup = $container->getParameter("ldapFiltergroup");
+ $this->filteruser = $container->getParameter("ldapFilteruser");
+
+ $this->userattributes = [$this->username,$this->firstname,$this->lastname,$this->email,$this->avatar,$this->memberof];
+
+ }
+
+ public function isNine2Ldap() {
+ return ($this->connect()&&$this->appMasteridentity=="SQL"&&$this->synchro=="NINE2LDAP"&&$this->userwriter&&$this->baseorganisation&&$this->baseniveau01&&$this->baseniveau02&&$this->basegroup&&$this->baseuser);
+ }
+
+ public function connect() {
+ // Si on est déjà co = on rebind pour gérer le cas d'un timeout de connection
+ if($this->connection){
+ if(!@ldap_bind($this->connection, $this->user, $this->password)){
+ $this->disconnect();
+ }
+ }
+
+ if($this->connection){
+ return $this->connection;
+ } else {
+ $ldapConn = ldap_connect($this->host, $this->port);
+
+ if($ldapConn){
+ ldap_set_option($ldapConn, LDAP_OPT_PROTOCOL_VERSION, 3);
+ ldap_set_option($ldapConn, LDAP_OPT_REFERRALS, 0);
+ if($this->usetls) ldap_start_tls($ldapConn);
+
+ if(@ldap_bind( $ldapConn, $this->user, $this->password)){
+ $this->connection = $ldapConn;
+ return $this->connection;
+ }
+ }
+ }
+ return false;
+ }
+
+ public function userconnect($username,$userpassword) {
+ $ldapConn = ldap_connect($this->host, $this->port);
+ $this->connection = $ldapConn;
+
+ if($this->connection){
+ ldap_set_option($ldapConn, LDAP_OPT_PROTOCOL_VERSION, 3);
+ ldap_set_option($ldapConn, LDAP_OPT_REFERRALS, 0);
+ if($this->usetls) ldap_start_tls($ldapConn);
+
+ $dn = $this->getUserDN($username);
+ if(@ldap_bind( $ldapConn, $dn, $userpassword)){
+ $res = $this->search(str_replace("*",$username,$this->filteruser),$this->userattributes, $this->baseuser);
+ $this->disconnect();
+ return $res;
+ }
+ }
+ $this->disconnect();
+ return false;
+ }
+
+ public function getParameter($key) {
+ switch($key) {
+ case "baseuser" : return $this->baseuser; break;
+ case "basegroup" : return $this->basegroup; break;
+ case "baseniveau01" : return $this->baseniveau01; break;
+ case "baseniveau02" : return $this->baseniveau02; break;
+ case "basedn" : return $this->basedn; break;
+ case "filteruser" : return $this->filteruser; break;
+ }
+ }
+
+ public function search($filter, $attributes = array(), $subBranch = '') {
+ $connection = $this->connect();
+ $branch = ($subBranch ? $subBranch : $this->basedn);
+ $result = ldap_search($connection, $branch, $filter, $attributes,0,0,0);
+ if(!$result) {
+ $this->ldapError();
+ }
+ return $this->resultToArray($result);
+ }
+
+ public function searchdn($dn, $subBranch = '') {
+ $connection = $this->connect();
+ $tbdn=ldap_explode_dn($dn,0);
+ $branch = ($subBranch ? $subBranch : $this->basedn);
+ $result = ldap_search($connection, $branch, "(".$tbdn[0].")", [],0,0,0);
+ if(!$result) {
+ $this->ldapError();
+ }
+ return $this->resultToArray($result);
+ }
+
+ public function deleteByDN($dn){
+ $connection = $this->connect();
+ $removed = ldap_delete($connection, $dn);
+ if(!$removed){
+ $this->ldapError();
+ }
+ }
+
+ public function rename($oldDN, $newDN, $parentDN = '', $deleteOldDN = true){
+ $connection = $this->connect();
+ $result = ldap_rename($connection, $oldDN, $newDN, $parentDN, $deleteOldDN);
+ if(!$result) $this->ldapError();
+ return $result;
+ }
+
+
+ private function resultToArray($result){
+
+ $connection = $this->connect();
+ $resultArray = array();
+
+ if($result){
+ $entry = ldap_first_entry($connection, $result);
+ while ($entry){
+ $row = array();
+ $attr = ldap_first_attribute($connection, $entry);
+ while ($attr){
+ $val = ldap_get_values_len($connection, $entry, $attr);
+ if(array_key_exists('count', $val) AND $val['count'] == 1){
+ $row[strtolower($attr)] = $val[0];
+ } else {
+ $row[strtolower($attr)] = $val;
+ }
+
+ if(is_array($row[strtolower($attr)])) {
+ unset($row[strtolower($attr)]["count"]);
+ }
+
+ $attr = ldap_next_attribute($connection, $entry);
+ }
+ $resultArray[] = $row;
+ $entry = ldap_next_entry($connection, $entry);
+ }
+ }
+
+ return $resultArray;
+ }
+
+ public function in_array_r($item , $array){
+ return preg_match('/"'.$item.'"/i' , json_encode($array));
+ }
+
+ public function disconnect(){
+ if($this->connection) {
+ ldap_unbind($this->connection);
+ $this->connection=null;
+ }
+ }
+
+ public function ldapError(){
+ $connection = $this->connect();
+ throw new \Exception(
+ 'Error: ('. ldap_errno($connection) .') '. ldap_error($connection)
+ );
+ }
+
+ public function ldapModify($dn,$attrs) {
+ $connection = $this->connect();
+ $result = ldap_modify($connection, $dn, $attrs);
+ if(!$result) $this->ldapError();
+ }
+
+//==================================================================================================================================================================
+//== Function Organisation==========================================================================================================================================
+//==================================================================================================================================================================
+
+ public function addOrganisations() {
+ $ldapentrys=$this->searchdn($this->baseorganisation);
+ if(empty($ldapentrys)) {
+ $this->addOrganisation($this->basedn);
+ }
+
+ $ldapentrys=$this->searchdn($this->baseniveau01,$this->baseorganisation);
+ if(empty($ldapentrys)) {
+ $this->addOrganisation($this->baseniveau01);
+ }
+
+ $ldapentrys=$this->searchdn($this->baseniveau02,$this->baseorganisation);
+ if(empty($ldapentrys)) {
+ $this->addOrganisation($this->baseniveau02);
+ }
+
+ $ldapentrys=$this->searchdn($this->basegroup,$this->baseorganisation);
+ if(empty($ldapentrys)) {
+ $this->addOrganisation($this->basegroup);
+ }
+
+ $ldapentrys=$this->searchdn($this->baseuser,$this->baseorganisation);
+ if(empty($ldapentrys)) {
+ $this->addOrganisation($this->baseuser);
+ }
+ }
+
+ public function addOrganisation($dn) {
+
+ $connection = $this->connect();
+ $attrs = array();
+ $attrs['objectclass'] = ["top","organizationalUnit"];
+ $result = ldap_add($connection, $dn, $attrs);
+ if(!$result) $this->ldapError();
+
+ return $result;
+ }
+
+//==================================================================================================================================================================
+//== Function User==================================================================================================================================================
+//==================================================================================================================================================================
+
+ public function addUser(User $user) {
+
+ $connection = $this->connect();
+ $dn = $this->getUserDN($user->getUsername());
+
+ $attrs = array();
+ $attrs['objectclass'] = $this->getObjectClassesUser();
+ $this->fillAttributesUser($user, $attrs);
+
+ foreach($attrs as $key => $value){
+ if(empty($value)){
+ unset($attrs[$key]);
+ }
+ }
+
+
+ $result = ldap_add($connection, $dn, $attrs);
+ if(!$result) $this->ldapError();
+
+ return $result;
+ }
+
+ public function ismodifyUser(User $user,$entry){
+ $attrs = [];
+ $this->fillAttributesUser($user, $attrs);
+
+ foreach($attrs as $key => $value) {
+ if(!array_key_exists($key,$entry)&&!empty($value)) return true;
+ elseif(array_key_exists($key,$entry)&&$value!=$entry[$key]) return true;
+ }
+
+ foreach($entry as $key => $value) {
+ if(!array_key_exists($key,$attrs)&&!empty($value)) return true;
+ elseif(array_key_exists($key,$attrs)&&$value!=$attrs[$key]) return true;
+ }
+
+ return false;
+ }
+
+ public function modifyUser(User $user){
+ $dn = $this->basedn;
+ $connection = $this->connect();
+
+ $attrs = array();
+ $this->fillAttributesUser($user, $attrs);
+
+ // Rechercher le DN du user
+ $dn = $this->getUserDN($user->getUsername());
+
+ foreach($attrs as $key => $value){
+ if(empty($value)){
+ // Bien mettre un @ car si l'attribut est déjà vide cela crache une erreur car l'attribut n'existe déjà plus
+ @ldap_mod_del($connection, $dn, array($key => array()));
+ unset($attrs[$key]);
+ }
+ }
+
+ $result = ldap_modify($connection, $dn, $attrs);
+ if(!$result) $this->ldapError();
+ }
+
+
+ public function modifyUserpwd(User $user){
+ $dn = $this->basedn;
+ $connection = $this->connect();
+
+ $attrs = array();
+
+ // Attributs associés au password
+ if($this->type=="AD")
+ $attrs["unicodepwd"] = $user->getPasswordad();
+ $attrs['userpassword'] = $user->getPassword();
+
+ // Rechercher le DN du user
+ $dn = $this->getUserDN($user->getUsername());
+
+ foreach($attrs as $key => $value){
+ if(empty($value)){
+ // Bien mettre un @ car si l'attribut est déjà vide cela crache une erreur car l'attribut n'existe déjà plus
+ @ldap_mod_del($connection, $dn, array($key => array()));
+ unset($attrs[$key]);
+ }
+ }
+
+
+ $result = ldap_modify($connection, $dn, $attrs);
+ if(!$result) $this->ldapError();
+ }
+
+ public function updateNiveauUser(User $user,$todel=false) {
+ $dn = $this->basedn;
+ $connection = $this->connect();
+
+ // NIVEAU01
+ // On recherche le Niveau01 actuellement asscocié à l'utilisateur
+ $criteria = '(&(cn=*)(memberUid='.$user->getUsername().'))';
+ $subbranch=$this->baseniveau01;
+ $results = $this->search($criteria, array('cn'), $subbranch);
+ foreach($results as $result) {
+ // Si Niveau01 différent de celui en cours on le détache de ce Niveau01
+ if($result["cn"]!=$user->getNiveau01()->getLabel()||$todel) {
+ $dn = $this->getNiveau01DN($result["cn"]);
+ $entry['memberuid'] = $user->getUsername();
+ $result = ldap_mod_del($connection, $dn, $entry);
+ if(!$result) $this->ldapError();
+ }
+ }
+
+ // On recherche le Niveau01 en cours
+ if(!$todel) {
+ $criteria = '(cn='.$user->getNiveau01()->getLabel().')';
+ $subbranch=$this->baseniveau01;
+ $result = $this->search($criteria, array('memberuid'), $subbranch);
+
+ // S'il n'est pas membre du Niveau01 on le rattache
+ if(!$this->in_array_r($user->getUsername(),$result[0])) {
+ $dn = $this->getNiveau01DN($user->getNiveau01()->getLabel());
+ $entry['memberuid'] = $user->getUsername();
+ $result = ldap_mod_add($connection, $dn, $entry);
+ if(!$result) $this->ldapError();
+ }
+ }
+
+ // NIVEAU02
+ // On recherche le Niveau02 actuellement asscocié à l'utilisateur
+ $criteria = '(&(cn=*)(memberUid='.$user->getUsername().'))';
+ $subbranch=$this->baseniveau02;
+ $results = $this->search($criteria, array('cn'), $subbranch);
+ foreach($results as $result) {
+ // Si Niveau02 différent de celui en cours on le détache de ce Niveau02
+ if($user->getNiveau02()===null||$result["cn"]!=$user->getNiveau02()->getLabel()||$todel) {
+ $dn = $this->getNiveau02DN($result["cn"]);
+ $entry['memberuid'] = $user->getUsername();
+ $result = ldap_mod_del($connection, $dn, $entry);
+ if(!$result) $this->ldapError();
+ }
+ }
+
+ // On recherche le Niveau02 en cours
+ if(!$todel) {
+ if($user->getNiveau02()!==null) {
+ $criteria = '(cn='.$user->getNiveau02()->getLabel().')';
+ $subbranch=$this->baseniveau02;
+ $result = $this->search($criteria, array('memberuid'), $subbranch);
+
+ // S'il n'est pas membre du Niveau02 on le rattache
+ if(empty($result)||!$this->in_array_r($user->getUsername(),$result[0])) {
+ $dn = $this->getNiveau02DN($user->getNiveau02()->getLabel());
+ $entry['memberuid'] = $user->getUsername();
+ $result = ldap_mod_add($connection, $dn, $entry);
+ if(!$result) $this->ldapError();
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ public function deleteUser(User $user){
+ $dn = $this->getUserDN($user->getUsername());
+ return $this->deleteByDN($dn);
+ }
+
+ public function getObjectClassesUser() {
+ $oc = array(
+ 'top',
+ 'person',
+ 'organizationalPerson',
+ 'inetOrgPerson',
+ );
+ return $oc;
+ }
+
+ public function listAttributesUser() {
+ return [
+ "uid",
+ "cn",
+ "givenname",
+ "sn",
+ "mail",
+ "displayname",
+ "telephonenumber",
+ "postaladdress",
+ "userpassword",
+ ];
+ }
+
+ public function fillAttributesUser(User $user, array &$attrs) {
+ $attrs['uid'] = $user->getUsername();
+ $attrs['cn'] = $user->getFirstname() . ' ' . $user->getLastname();
+ $attrs['givenname'] = $user->getFirstname();
+ $attrs['sn'] = $user->getLastname();
+ $attrs['mail'] = $user->getEmail();
+ $attrs['displayname'] = $user->getFirstname() . ' ' . $user->getLastname();
+ $attrs['telephonenumber'] = $user->getTelephonenumber();
+ $attrs['postaladdress'] = $user->getPostaladress();
+ $attrs['userpassword'] = $user->getPassword();
+ }
+
+ public function getUserDN($username) {
+ return $this->username.'='.$username.','.$this->baseuser;
+ }
+
+//==================================================================================================================================================================
+//== Function Niveau01==============================================================================================================================================
+//==================================================================================================================================================================
+
+ public function findNiveau01($ldapfilter) {
+ $ldapentrys=$this->search($ldapfilter,[$this->groupgid,$this->groupname,$this->groupmember],$this->baseniveau01);
+ return $ldapentrys;
+ }
+
+ public function findNiveau01ismember($ldapfilter,$username) {
+ $ldapentrys=$this->findNiveau01($ldapfilter);
+ foreach($ldapentrys as $ldapentry) {
+ if(is_array($ldapentry[$this->groupmember])) {
+ if(in_array($username,$ldapentry[$this->groupmember])) return true;
+ }
+ elseif($username==$ldapentry[$this->groupmember]) return true;
+ }
+ return false;
+ }
+
+ public function addNiveau01(Niveau01 $niveau01) {
+
+ $connection = $this->connect();
+ $dn = $this->getNiveau01DN($niveau01->getLabel());
+
+ $attrs = array();
+ $attrs['objectclass'] = $this->getObjectClassesNiveau01();
+ $this->fillAttributesNiveau01($niveau01, $attrs);
+
+ foreach($attrs as $key => $value){
+ if(empty($value)){
+ unset($attrs[$key]);
+ }
+ }
+
+ $result = ldap_add($connection, $dn, $attrs);
+ if(!$result) $this->ldapError();
+
+ return $result;
+ }
+
+
+ public function ismodifyNiveau01(Niveau01 $niveau01,$entry){
+ $attrs = [];
+ $this->fillAttributesNiveau01($niveau01, $attrs);
+
+ foreach($attrs as $key => $value) {
+ if(!array_key_exists($key,$entry)&&!empty($value)) return true;
+ elseif(array_key_exists($key,$entry)&&$value!=$entry[$key]) return true;
+ }
+
+ foreach($entry as $key => $value) {
+ if(!array_key_exists($key,$attrs)&&!empty($value)) return true;
+ elseif(array_key_exists($key,$attrs)&&$value!=$attrs[$key]) return true;
+ }
+
+ return false;
+ }
+
+ public function modifyNiveau01(Niveau01 $niveau01,$oldid){
+
+ $dn = $this->basedn;
+ $connection = $this->connect();
+
+ $attrs = array();
+ $this->fillAttributesNiveau01($niveau01, $attrs);
+ unset($attrs["cn"]);
+
+ $dn = $this->getNiveau01DN($niveau01->getLabel());
+
+ foreach($attrs as $key => $value){
+ if(empty($value)){
+ // Bien mettre un @ car si l'attribut est déjà vide cela crache une erreur car l'attribut n'existe déjà plus
+ @ldap_mod_del($connection, $dn, array($key => array()));
+ unset($attrs[$key]);
+ }
+ }
+
+ if(isset($oldid)&&$oldid!=$niveau01->getLabel()) {
+ $olddn = $this->getNiveau01DN($oldid);
+ $this->rename($olddn,"cn=".$niveau01->getLabel(),$this->baseniveau01);
+ }
+
+ $result = ldap_modify($connection, $dn, $attrs);
+ if(!$result) $this->ldapError();
+ }
+
+ public function deleteNiveau01(Niveau01 $niveau01){
+ $dn = $this->getNiveau01DN($niveau01->getLabel());
+ return $this->deleteByDN($dn);
+ }
+
+ private function getObjectClassesNiveau01() {
+ $oc = array(
+ 'top',
+ 'posixGroup',
+ );
+
+ return $oc;
+ }
+
+ public function listAttributesNiveau01() {
+ return [
+ "cn",
+ "gidnumber",
+ "memberuid",
+ ];
+ }
+
+ public function fillAttributesNiveau01(Niveau01 $niveau01, array &$attrs) {
+ $attrs['cn'] = $niveau01->getLabel();
+ $attrs['gidnumber'] = $niveau01->getId();
+
+ $attrs['memberuid'] = [];
+ foreach($niveau01->getUsers() as $user) {
+ array_push($attrs['memberuid'],$user->getUsername());
+ }
+
+ sort($attrs['memberuid']);
+ if(count($attrs['memberuid'])==1) $attrs['memberuid'] = $attrs['memberuid'][0];
+ }
+
+ public function getNiveau01DN($id) {
+ return 'cn='.$id.','.$this->baseniveau01;
+ }
+
+//==================================================================================================================================================================
+//== Function Niveau02==============================================================================================================================================
+//==================================================================================================================================================================
+
+ public function addNiveau02(Niveau02 $niveau02) {
+
+ $connection = $this->connect();
+ $dn = $this->getNiveau02DN($niveau02->getLabel());
+
+ $attrs = array();
+ $attrs['objectclass'] = $this->getObjectClassesNiveau02();
+ $this->fillAttributesNiveau02($niveau02, $attrs);
+
+ foreach($attrs as $key => $value){
+ if(empty($value)){
+ unset($attrs[$key]);
+ }
+ }
+
+ $result = ldap_add($connection, $dn, $attrs);
+ if(!$result) $this->ldapError();
+
+ return $result;
+ }
+
+ public function ismodifyNiveau02(Niveau02 $niveau02,$entry){
+ $attrs = [];
+ $this->fillAttributesNiveau02($niveau02, $attrs);
+
+ foreach($attrs as $key => $value) {
+ if(!array_key_exists($key,$entry)&&!empty($value)) return true;
+ elseif(array_key_exists($key,$entry)&&$value!=$entry[$key]) return true;
+ }
+
+ foreach($entry as $key => $value) {
+ if(!array_key_exists($key,$attrs)&&!empty($value)) return true;
+ elseif(array_key_exists($key,$attrs)&&$value!=$attrs[$key]) return true;
+ }
+
+ return false;
+ }
+
+ public function modifyNiveau02(Niveau02 $niveau02,$oldid){
+ $dn = $this->basedn;
+ $connection = $this->connect();
+
+ $attrs = array();
+ $this->fillAttributesNiveau02($niveau02, $attrs);
+ unset($attrs["cn"]);
+
+ $dn = $this->getNiveau02DN($niveau02->getLabel());
+
+ foreach($attrs as $key => $value){
+ if(empty($value)){
+ // Bien mettre un @ car si l'attribut est déjà vide cela crache une erreur car l'attribut n'existe déjà plus
+ @ldap_mod_del($connection, $dn, array($key => array()));
+ unset($attrs[$key]);
+ }
+ }
+
+ if(isset($oldid)&&$oldid!=$niveau02->getLabel()) {
+ $olddn = $this->getNiveau02DN($oldid);
+ $this->rename($olddn,"cn=".$niveau02->getLabel(),$this->baseniveau02);
+ }
+
+ $result = ldap_modify($connection, $dn, $attrs);
+ if(!$result) $this->ldapError();
+ }
+
+
+
+ public function deleteNiveau02(Niveau02 $niveau02){
+ $dn = $this->getNiveau02DN($niveau02->getLabel());
+ return $this->deleteByDN($dn);
+ }
+
+ private function getObjectClassesNiveau02() {
+ $oc = array(
+ 'top',
+ 'posixGroup',
+ );
+
+ return $oc;
+ }
+
+ public function listAttributesNiveau02() {
+ return [
+ "cn",
+ "gidnumber",
+ "memberuid"
+ ];
+ }
+
+ public function fillAttributesNiveau02(Niveau02 $niveau02, array &$attrs) {
+ $attrs['cn'] = $niveau02->getLabel();
+ $attrs['gidnumber'] = $niveau02->getId();
+
+ $attrs['memberuid'] = [];
+ foreach($niveau02->getUsers() as $user) {
+ array_push($attrs['memberuid'],$user->getUsername());
+ }
+
+ sort($attrs['memberuid']);
+ if(count($attrs['memberuid'])==1) $attrs['memberuid'] = $attrs['memberuid'][0];
+
+ }
+
+ public function getNiveau02DN($id) {
+ return 'cn='.$id.','.$this->baseniveau02;
+ }
+
+//==================================================================================================================================================================
+//== Function Group=================================================================================================================================================
+//==================================================================================================================================================================
+
+ public function addGroup(Group $group) {
+
+ $connection = $this->connect();
+ $dn = $this->getGroupDN($group->getLabel());
+
+ $attrs = array();
+ $attrs['objectclass'] = $this->getObjectClassesGroup();
+ $this->fillAttributesGroup($group, $attrs);
+
+ foreach($attrs as $key => $value){
+ if(empty($value)){
+ unset($attrs[$key]);
+ }
+ }
+
+ $result = ldap_add($connection, $dn, $attrs);
+ if(!$result) $this->ldapError();
+
+ return $result;
+ }
+
+ public function ismodifyGroup(Group $group,$entry){
+ $attrs = [];
+ $this->fillAttributesGroup($group, $attrs);
+
+ foreach($attrs as $key => $value) {
+ if(!array_key_exists($key,$entry)&&!empty($value)) return true;
+ elseif(array_key_exists($key,$entry)&&$value!=$entry[$key]) return true;
+ }
+
+ foreach($entry as $key => $value) {
+ if(!array_key_exists($key,$attrs)&&!empty($value)) return true;
+ elseif(array_key_exists($key,$attrs)&&$value!=$attrs[$key]) return true;
+ }
+
+ return false;
+ }
+
+ public function modifyGroup(Group $group,$oldid){
+ $dn = $this->basedn;
+ $connection = $this->connect();
+
+ $attrs = array();
+ $this->fillAttributesGroup($group, $attrs);
+ unset($attrs["cn"]);
+
+ $dn = $this->getGroupDN($group->getLabel());
+
+ foreach($attrs as $key => $value){
+ if(empty($value)){
+ // Bien mettre un @ car si l'attribut est déjà vide cela crache une erreur car l'attribut n'existe déjà plus
+ @ldap_mod_del($connection, $dn, array($key => array()));
+ unset($attrs[$key]);
+ }
+ }
+
+ if(isset($oldid)&&$oldid!=$group->getLabel()) {
+ $olddn = $this->getGroupDN($oldid);
+ $this->rename($olddn,"cn=".$group->getLabel(),$this->basegroup);
+ }
+
+ $result = ldap_modify($connection, $dn, $attrs);
+ if(!$result) $this->ldapError();
+ }
+
+ public function deleteGroup(Group $group){
+ $dn = $this->getGroupDN($group->getLabel());
+ return $this->deleteByDN($dn);
+ }
+
+ private function getObjectClassesGroup() {
+ $oc = array(
+ 'top',
+ 'posixGroup',
+ );
+ return $oc;
+ }
+
+ public function listAttributesGroup() {
+ return [
+ "cn",
+ "gidnumber",
+ "memberuid"
+ ];
+ }
+
+ public function fillAttributesGroup(Group $group, array &$attrs) {
+ $attrs['cn'] = $group->getLabel();
+ $attrs['gidnumber'] = $group->getId();
+
+
+ $attrs['memberuid'] = [];
+ foreach($group->getUsers() as $usergroup) {
+ array_push($attrs['memberuid'],$usergroup->getUser()->getUsername());
+ }
+
+ sort($attrs['memberuid']);
+ if(count($attrs['memberuid'])==1) $attrs['memberuid'] = $attrs['memberuid'][0];
+ }
+
+ public function getGroupDN($id) {
+ return 'cn='.$id.','.$this->basegroup;
+ }
+
+//==================================================================================================================================================================
+//== Function UserGroup=============================================================================================================================================
+//==================================================================================================================================================================
+
+ function addUserGroup(UserGroup $usergroup) {
+ $dn = $this->basedn;
+ $connection = $this->connect();
+
+ // On recherche le group en cours
+ $criteria = '(cn='.$usergroup->getGroup()->getLabel().')';
+ $subbranch=$this->basegroup;
+ $result = $this->search($criteria, array('memberuid'), $subbranch);
+
+ if(!$this->in_array_r($usergroup->getUser()->getUsername(),$result[0])) {
+ $dn = $this->getGroupDN($usergroup->getGroup()->getLabel());
+ $entry['memberuid'] = $usergroup->getUser()->getUsername();
+ $result = ldap_mod_add($connection, $dn, $entry);
+ if(!$result) $this->ldapError();
+ }
+
+ return $result;
+ }
+
+ function delUserGroup(UserGroup $usergroup) {
+ $dn = $this->basedn;
+ $connection = $this->connect();
+
+ // On recherche le group en cours
+ $criteria = '(cn='.$usergroup->getGroup()->getLabel().')';
+ $subbranch=$this->basegroup;
+ $result = $this->search($criteria, array('memberuid'), $subbranch);
+
+ if($this->in_array_r($usergroup->getUser()->getUsername(),$result[0])) {
+ $dn = $this->getGroupDN($usergroup->getGroup()->getLabel());
+ $entry['memberuid'] = $usergroup->getUser()->getUsername();
+ $result = ldap_mod_del($connection, $dn, $entry);
+ if(!$result) $this->ldapError();
+ }
+
+ return $result;
+ }
+}
diff --git a/src/Service/MailService.php b/src/Service/MailService.php
new file mode 100644
index 0000000..260b8f4
--- /dev/null
+++ b/src/Service/MailService.php
@@ -0,0 +1,57 @@
+mailer = $mailer;
+ $this->twig = $twig;
+ }
+
+ /**
+ * Send email
+ *
+ * @param string $template email template
+ * @param mixed $parameters custom params for template
+ * @param string $to to email address or array of email addresses
+ * @param string $from from email address
+ * @param string $fromName from name
+ *
+ * @return boolean send status
+ */
+ public function sendEmail($subject, $body, $to, $from, $fromName = null)
+ {
+ $template = $this->twig->load('Home/mail.html.twig');
+
+ $parameters=["subject"=>$subject,"body"=>$body];
+ $subject = $template->renderBlock('subject', $parameters);
+ $bodyHtml = $template->renderBlock('body', $parameters);
+
+ try {
+ if(!is_array($to)) $to=[$to];
+
+ foreach($to as $t) {
+ $message = (new Email())
+ ->subject($subject)
+ ->from(Address::create($fromName. "<".$from.">"))
+ ->to($t)
+ ->html($bodyHtml);
+ $this->mailer->send($message);
+
+ }
+ } catch (TransportExceptionInterface $e) {
+ return $e->getMessage();
+ }
+
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/Service/PasswordEncoder.php b/src/Service/PasswordEncoder.php
new file mode 100644
index 0000000..807f5a2
--- /dev/null
+++ b/src/Service/PasswordEncoder.php
@@ -0,0 +1,36 @@
+isPasswordTooLong($plainPassword)) {
+ throw new InvalidPasswordException();
+ }
+ $hash = "{SSHA}" . base64_encode(pack("H*", sha1($plainPassword . $salt)) . $salt);
+ return $hash;
+ }
+
+ public function verify(string $hashedPassword, string $plainPassword, string $salt = null): bool
+ {
+ if ('' === $plainPassword || $this->isPasswordTooLong($plainPassword)) {
+ return false;
+ }
+
+ var_dump($salt);
+ return $this->hash($plainPassword,$salt) === $hashedPassword;
+ }
+
+ public function needsRehash(string $hashedPassword): bool
+ {
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/Service/UploadListener.php b/src/Service/UploadListener.php
new file mode 100644
index 0000000..e86fa91
--- /dev/null
+++ b/src/Service/UploadListener.php
@@ -0,0 +1,87 @@
+em = $em;
+ }
+
+ protected function getHeight($image) {
+ $size = getimagesize($image);
+ $height = $size[1];
+ return $height;
+ }
+
+ // Cacul de la largeur
+ protected function getWidth($image) {
+ $size = getimagesize($image);
+ $width = $size[0];
+ return $width;
+ }
+
+ protected function resizeImage($image,$width,$height,$scale) {
+ list($imagewidth, $imageheight, $imageType) = getimagesize($image);
+ $imageType = image_type_to_mime_type($imageType);
+ $newImageWidth = ceil($width * $scale);
+ $newImageHeight = ceil($height * $scale);
+ $newImage = imagecreatetruecolor($newImageWidth,$newImageHeight);
+ switch($imageType) {
+ case "image/gif":
+ $source=imagecreatefromgif($image);
+ break;
+ case "image/pjpeg":
+ case "image/jpeg":
+ case "image/jpg":
+ $source=imagecreatefromjpeg($image);
+ break;
+ case "image/png":
+ case "image/x-png":
+ $source=imagecreatefrompng($image);
+ break;
+ }
+
+ $newImage = imagecreatetruecolor( $newImageWidth, $newImageHeight );
+ imagealphablending( $newImage, false );
+ imagesavealpha( $newImage, true );
+ imagecopyresampled($newImage,$source,0,0,0,0,$newImageWidth,$newImageHeight,$width,$height);
+
+ switch($imageType) {
+ case "image/gif":
+ imagegif($newImage,$image);
+ break;
+ case "image/pjpeg":
+ case "image/jpeg":
+ case "image/jpg":
+ imagejpeg($newImage,$image,90);
+ break;
+ case "image/png":
+ case "image/x-png":
+ imagepng($newImage,$image);
+ break;
+ }
+
+ chmod($image, 0640);
+ return $image;
+ }
+
+ public function onUpload(PostPersistEvent $event)
+ {
+ $type=$event->getType();
+
+ switch($type) {
+ default:
+ $file=$event->getFile();
+ $filename=$file->getFilename();
+ $response = $event->getResponse();
+ $response['file'] = $filename;
+ break;
+ }
+ }
+}
diff --git a/src/Twig/AppExtension.php b/src/Twig/AppExtension.php
new file mode 100644
index 0000000..adc33f3
--- /dev/null
+++ b/src/Twig/AppExtension.php
@@ -0,0 +1,31 @@
+container->getParameter("appAlias")."uploads/avatar/".$avatar;
+ }
+
+ public function setContainer($container)
+ {
+ $this->container = $container;
+ }
+}
\ No newline at end of file
diff --git a/src/Validator/Grouplabel.php b/src/Validator/Grouplabel.php
new file mode 100644
index 0000000..0fac9a1
--- /dev/null
+++ b/src/Validator/Grouplabel.php
@@ -0,0 +1,12 @@
+getEntityBy("App:Group","label",$value);
+ if($tmp) $form->addError(new FormError('Un groupe utilise déjà ce label'));
+
+ $tmp=$this->getEntityBy("App:Niveau02","label",$data->getLabel());
+ if($tmp) $form->addError(new FormError('Un niveau de rang 02 utilise déjà ce label'));
+ */
+
+ // On s'assure que le label ne contient pas des caractères speciaux
+ $string = preg_replace('~[^ éèêôöàïî\'@a-zA-Z0-9._-]~', '', $value);
+ if($string!=$value)
+ {
+ $this->context->addViolation($constraint->message);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Validator/Niveau01unique.php b/src/Validator/Niveau01unique.php
new file mode 100644
index 0000000..2bdd764
--- /dev/null
+++ b/src/Validator/Niveau01unique.php
@@ -0,0 +1,13 @@
+em = $em;
+ }
+
+ public function validate($value, Constraint $constraint)
+ {
+ $group = $this->em->getRepository("App\Entity\Group")->findOneBy(["label"=>$value]);
+ if($group) {
+ $this->context->addViolation($constraint->messagegroup);
+ }
+
+ $niveau02 = $this->em->getRepository("App\Entity\Niveau02")->findOneBy(["label"=>$value]);
+ if($niveau02) {
+ $this->context->addViolation($constraint->messageniveau02);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Validator/Niveau02unique.php b/src/Validator/Niveau02unique.php
new file mode 100644
index 0000000..3bd3b02
--- /dev/null
+++ b/src/Validator/Niveau02unique.php
@@ -0,0 +1,13 @@
+em = $em;
+ }
+
+ public function validate($value, Constraint $constraint)
+ {
+ $group = $this->em->getRepository("App\Entity\Group")->findOneBy(["label"=>$value]);
+ if($group) {
+ $this->context->addViolation($constraint->messagegroup);
+ }
+
+ $niveau02 = $this->em->getRepository("App\Entity\Niveau01")->findOneBy(["label"=>$value]);
+ if($niveau02) {
+ $this->context->addViolation($constraint->messageniveau01);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Validator/Password.php b/src/Validator/Password.php
new file mode 100644
index 0000000..0022c28
--- /dev/null
+++ b/src/Validator/Password.php
@@ -0,0 +1,12 @@
+context->addViolation($constraint->message);
+ }
+ elseif(!preg_match("#[0-9]+#",$value)) {
+ $this->context->addViolation($constraint->message);
+ }
+ elseif(!preg_match("#[a-zA-Z]+#",$value)) {
+ $this->context->addViolation($constraint->message);
+ }
+ elseif(!preg_match("/[|!@#$%&*\/=?,;.:\-_+~^\\\]/",$value)) {
+ $this->context->addViolation($constraint->message);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Validator/Userusername.php b/src/Validator/Userusername.php
new file mode 100644
index 0000000..83b54a0
--- /dev/null
+++ b/src/Validator/Userusername.php
@@ -0,0 +1,13 @@
+em = $em;
+ }
+
+ public function validate($value, Constraint $constraint)
+ {
+ if(!empty($value)) {
+ // On s'assure que le login soit de 5 caractères minimum
+ if (strlen($value) < '5') {
+ $this->context->addViolation($constraint->messageinvalid);
+ }
+
+ // On s'assure que le username ne contient pas des caractères speciaux
+ $string = preg_replace('~[^@a-zA-Z0-9._-]~', '', $value);
+ if($string!=$value)
+ $this->context->addViolation($constraint->messageinvalid);
+
+ // On s'assure que le username n'existe pas dans la table des registration
+ $registration = $this->em->getRepository("App\Entity\Registration")->findOneBy(["username"=>$value]);
+ if($registration) {
+ $this->context->addViolation($constraint->messagenotunique);
+ }
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/symfony.lock b/symfony.lock
new file mode 100644
index 0000000..872629e
--- /dev/null
+++ b/symfony.lock
@@ -0,0 +1,378 @@
+{
+ "doctrine/annotations": {
+ "version": "1.13",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "1.10",
+ "ref": "64d8583af5ea57b7afa4aba4b159907f3a148b05"
+ }
+ },
+ "doctrine/doctrine-bundle": {
+ "version": "2.7",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "2.4",
+ "ref": "ddddd8249dd55bbda16fa7a45bb7499ef6f8e90e"
+ },
+ "files": [
+ "config/packages/doctrine.yaml",
+ "src/Entity/.gitignore",
+ "src/Repository/.gitignore"
+ ]
+ },
+ "doctrine/doctrine-migrations-bundle": {
+ "version": "3.2",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "3.1",
+ "ref": "ee609429c9ee23e22d6fa5728211768f51ed2818"
+ },
+ "files": [
+ "config/packages/doctrine_migrations.yaml",
+ "migrations/.gitignore"
+ ]
+ },
+ "friendsofsymfony/ckeditor-bundle": {
+ "version": "2.4",
+ "recipe": {
+ "repo": "github.com/symfony/recipes-contrib",
+ "branch": "main",
+ "version": "2.0",
+ "ref": "f5ad42002183a6881962683e6d84bbb25cdfce5d"
+ }
+ },
+ "friendsofsymfony/rest-bundle": {
+ "version": "3.3",
+ "recipe": {
+ "repo": "github.com/symfony/recipes-contrib",
+ "branch": "main",
+ "version": "2.2",
+ "ref": "fa845143b7e0a4c70aedd1a88c549e6d977e9ae5"
+ },
+ "files": [
+ "config/packages/fos_rest.yaml"
+ ]
+ },
+ "gregwar/captcha-bundle": {
+ "version": "v2.2.0"
+ },
+ "nelmio/api-doc-bundle": {
+ "version": "4.9",
+ "recipe": {
+ "repo": "github.com/symfony/recipes-contrib",
+ "branch": "main",
+ "version": "3.0",
+ "ref": "c8e0c38e1a280ab9e37587a8fa32b251d5bc1c94"
+ },
+ "files": [
+ "config/packages/nelmio_api_doc.yaml",
+ "config/routes/nelmio_api_doc.yaml"
+ ]
+ },
+ "oneup/uploader-bundle": {
+ "version": "3.2",
+ "recipe": {
+ "repo": "github.com/symfony/recipes-contrib",
+ "branch": "main",
+ "version": "2.0",
+ "ref": "b19678d1535b9fe7cdf4647fd3483455e4fca458"
+ },
+ "files": [
+ "config/packages/oneup_uploader.yaml",
+ "config/routes/oneup_uploader.yaml"
+ ]
+ },
+ "phpunit/phpunit": {
+ "version": "9.5",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "9.3",
+ "ref": "a6249a6c4392e9169b87abf93225f7f9f59025e6"
+ },
+ "files": [
+ ".env.test",
+ "phpunit.xml.dist",
+ "tests/bootstrap.php"
+ ]
+ },
+ "sensio/framework-extra-bundle": {
+ "version": "6.2",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "5.2",
+ "ref": "fb7e19da7f013d0d422fa9bce16f5c510e27609b"
+ },
+ "files": [
+ "config/packages/sensio_framework_extra.yaml"
+ ]
+ },
+ "sentry/sentry-symfony": {
+ "version": "4.3",
+ "recipe": {
+ "repo": "github.com/symfony/recipes-contrib",
+ "branch": "main",
+ "version": "4.0",
+ "ref": "3d8dc8c0f689e2e94f860d2e36f81fcc26348069"
+ },
+ "files": [
+ "config/packages/sentry.yaml"
+ ]
+ },
+ "symfony/console": {
+ "version": "6.1",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "5.3",
+ "ref": "da0c8be8157600ad34f10ff0c9cc91232522e047"
+ },
+ "files": [
+ "bin/console"
+ ]
+ },
+ "symfony/debug-bundle": {
+ "version": "6.1",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "5.3",
+ "ref": "5aa8aa48234c8eb6dbdd7b3cd5d791485d2cec4b"
+ },
+ "files": [
+ "config/packages/debug.yaml"
+ ]
+ },
+ "symfony/flex": {
+ "version": "2.2",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "1.0",
+ "ref": "c0eeb50665f0f77226616b6038a9b06c03752d8e"
+ },
+ "files": [
+ ".env"
+ ]
+ },
+ "symfony/framework-bundle": {
+ "version": "6.1",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "5.4",
+ "ref": "3cd216a4d007b78d8554d44a5b1c0a446dab24fb"
+ },
+ "files": [
+ "config/packages/cache.yaml",
+ "config/packages/framework.yaml",
+ "config/preload.php",
+ "config/routes/framework.yaml",
+ "config/services.yaml",
+ "public/index.php",
+ "src/Controller/.gitignore",
+ "src/Kernel.php"
+ ]
+ },
+ "symfony/lock": {
+ "version": "6.1",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "5.2",
+ "ref": "a1c8800e40ae735206bb14586fdd6c4630a51b8d"
+ },
+ "files": [
+ "config/packages/lock.yaml"
+ ]
+ },
+ "symfony/mailer": {
+ "version": "6.1",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "4.3",
+ "ref": "97a61eabb351d7f6cb7702039bcfe07fe9d7e03c"
+ },
+ "files": [
+ "config/packages/mailer.yaml"
+ ]
+ },
+ "symfony/maker-bundle": {
+ "version": "1.44",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "1.0",
+ "ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f"
+ }
+ },
+ "symfony/mercure-bundle": {
+ "version": "0.3",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "0.3",
+ "ref": "e0a854b5439186e04b28fb8887b42c54f24a0d32"
+ },
+ "files": [
+ "config/packages/mercure.yaml"
+ ]
+ },
+ "symfony/messenger": {
+ "version": "6.1",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "6.0",
+ "ref": "2523f7d31488903e247a522e760dc279be7f7aaf"
+ },
+ "files": [
+ "config/packages/messenger.yaml"
+ ]
+ },
+ "symfony/monolog-bundle": {
+ "version": "3.8",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "3.7",
+ "ref": "213676c4ec929f046dfde5ea8e97625b81bc0578"
+ },
+ "files": [
+ "config/packages/monolog.yaml"
+ ]
+ },
+ "symfony/notifier": {
+ "version": "6.1",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "5.0",
+ "ref": "c31585e252b32fe0e1f30b1f256af553f4a06eb9"
+ },
+ "files": [
+ "config/packages/notifier.yaml"
+ ]
+ },
+ "symfony/phpunit-bridge": {
+ "version": "6.1",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "5.3",
+ "ref": "97cb3dc7b0f39c7cfc4b7553504c9d7b7795de96"
+ },
+ "files": [
+ ".env.test",
+ "bin/phpunit",
+ "phpunit.xml.dist",
+ "tests/bootstrap.php"
+ ]
+ },
+ "symfony/routing": {
+ "version": "6.1",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "6.1",
+ "ref": "a44010c0d06989bd4f154aa07d2542d47caf5b83"
+ },
+ "files": [
+ "config/packages/routing.yaml",
+ "config/routes.yaml"
+ ]
+ },
+ "symfony/security-bundle": {
+ "version": "6.1",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "6.0",
+ "ref": "8a5b112826f7d3d5b07027f93786ae11a1c7de48"
+ },
+ "files": [
+ "config/packages/security.yaml"
+ ]
+ },
+ "symfony/translation": {
+ "version": "6.1",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "5.3",
+ "ref": "da64f5a2b6d96f5dc24914517c0350a5f91dee43"
+ },
+ "files": [
+ "config/packages/translation.yaml",
+ "translations/.gitignore"
+ ]
+ },
+ "symfony/twig-bundle": {
+ "version": "6.1",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "5.4",
+ "ref": "bb2178c57eee79e6be0b297aa96fc0c0def81387"
+ },
+ "files": [
+ "config/packages/twig.yaml",
+ "templates/base.html.twig"
+ ]
+ },
+ "symfony/validator": {
+ "version": "6.1",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "5.3",
+ "ref": "c32cfd98f714894c4f128bb99aa2530c1227603c"
+ },
+ "files": [
+ "config/packages/validator.yaml"
+ ]
+ },
+ "symfony/web-profiler-bundle": {
+ "version": "6.1",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "6.1",
+ "ref": "e42b3f0177df239add25373083a564e5ead4e13a"
+ },
+ "files": [
+ "config/packages/web_profiler.yaml",
+ "config/routes/web_profiler.yaml"
+ ]
+ },
+ "symfony/webpack-encore-bundle": {
+ "version": "1.14",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "1.10",
+ "ref": "91f4d29f5ebbd2fcccc71ca55a17d14a17b308e8"
+ },
+ "files": [
+ "assets/app.js",
+ "assets/bootstrap.js",
+ "assets/controllers.json",
+ "assets/controllers/hello_controller.js",
+ "assets/styles/app.css",
+ "config/packages/webpack_encore.yaml",
+ "package.json",
+ "webpack.config.js"
+ ]
+ },
+ "tetranz/select2entity-bundle": {
+ "version": "v3.1.0"
+ },
+ "twig/extra-bundle": {
+ "version": "v3.4.0"
+ }
+}
diff --git a/templates/Config/edit.html.twig b/templates/Config/edit.html.twig
new file mode 100755
index 0000000..12b00bc
--- /dev/null
+++ b/templates/Config/edit.html.twig
@@ -0,0 +1,94 @@
+{% extends 'base.html.twig' %}
+
+{% block body %}
+{{ form_start(form) }}
+
+
+ {{ form_widget(form.submit) }}
+
+ Annuler
+
+ {% if mode=="update" and not config.required %}
+
+ Supprimer
+
+ {% endif %}
+
+
+
+ {% if app.session.flashbag.has('error') %}
+
+ Erreur
+ {% for flashMessage in app.session.flashbag.get('error') %}
+ {{ flashMessage }}
+ {% endfor %}
+
+ {% endif %}
+
+ {% if app.session.flashbag.has('notice') %}
+
+ Information
+ {% for flashMessage in app.session.flashbag.get('notice') %}
+ {{ flashMessage }}
+ {% endfor %}
+
+ {% endif %}
+
+
+
+
+
+ {{ form_row(form.id) }}
+ {{ form_row(form.value) }}
+ {% if config.type=="logo" %}
+
+ {% set color = "" %}
+ {% if config.id=='logodark' %}
+ {% set color = app.session.get('colorbgbodydark') %}
+ {% elseif config.id=='logolight' %}
+ {% set color = app.session.get('colorbgbodylight') %}
+ {% endif %}
+
+
+
Modifier
+
+ {% elseif config.type=="header" %}
+
+ {% elseif config.type=="image" %}
+
+ {% endif %}
+ {{ form_row(form.help) }}
+
+
+{{ form_end(form) }}
+
+{% endblock %}
+
+{% block localjavascript %}
+ $("#config_value_img").on('error', function(){
+ var imgSrc = $(this).attr('src');
+ if(imgSrc!="/{{appAlias}}/uploads/{{config.type}}/")
+ $(this).attr('src',imgSrc);
+ });
+ $('#mymodallarge').on('hidden.bs.modal', function () {
+ var imgSrc = $("#config_value_img").attr('src');
+ $("#config_value_img").attr('src',imgSrc);
+ });
+{% endblock %}
diff --git a/templates/Config/list.html.twig b/templates/Config/list.html.twig
new file mode 100644
index 0000000..0cd5dd6
--- /dev/null
+++ b/templates/Config/list.html.twig
@@ -0,0 +1,66 @@
+{% extends "base.html.twig" %}
+
+{% block body %}
+
+
+
+
+
+
Générale
+ {{ render(path("app_admin_config_render",{category:"site"})) }}
+
+
+
+
Couleurs des fonds de page
+ {{ render(path("app_admin_config_render",{category:"colorbgbody"})) }}
+
+
+
+
Polices
+ {{ render(path("app_admin_config_render",{category:"font"})) }}
+
+
+
+
Couleurs des titres
+ {{ render(path("app_admin_config_render",{category:"colorfttitle"})) }}
+
+
+
+
Couleurs de la police
+ {{ render(path("app_admin_config_render",{category:"colorftbody"})) }}
+
+
+
+
Logo
+ {{ render(path("app_admin_config_render",{category:"logo"})) }}
+
+
+
+
Bannière
+ {{ render(path("app_admin_config_render",{category:"header"})) }}
+
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
+
+{% block localjavascript %}
+ $(document).ready(function() {
+ $('#dataTables').DataTable({
+ columnDefs: [ { "targets": "no-sort", "orderable": false }, { "targets": "no-string", "type" : "num" } ],
+ responsive: true,
+ iDisplayLength: 100,
+ order: [[ 1, "asc" ]]
+ });
+ });
+{% endblock %}
\ No newline at end of file
diff --git a/templates/Config/logo.html.twig b/templates/Config/logo.html.twig
new file mode 100644
index 0000000..f71cabf
--- /dev/null
+++ b/templates/Config/logo.html.twig
@@ -0,0 +1,35 @@
+{% extends "base.html.twig" %}
+
+{% block encoretags %}
+ {{ encore_entry_link_tags('dropzone') }}
+{% endblock %}
+
+{% block body %}
+ Annuler
+
+
+{% endblock %}
+
+{% block localscript %}
+ {{ encore_entry_script_tags('dropzone') }}
+
+
+{% endblock %}
diff --git a/templates/Config/render.html.twig b/templates/Config/render.html.twig
new file mode 100644
index 0000000..a715bd3
--- /dev/null
+++ b/templates/Config/render.html.twig
@@ -0,0 +1,85 @@
+
+
+ {% for config in configs|sort((a, b) => a.order <=> b.order) %}
+ {% set continue = true %}
+ {% if not config.grouped is empty %}
+
+ {% if app.session.get(config.grouped)==0 %}
+ {% set continue = false %}
+ {% endif %}
+ {% endif %}
+
+ {%if continue %}
+
+
+ {% if config.changeable %}
+
+ {% endif %}
+
+ {% if not config.required %}
+
+ {% endif %}
+ |
+ {{config.title}} |
+
+ {% set val = config.value %}
+ {% if val is empty %}
+ {% set val=app.session.get(config.id) %}
+ {% endif %}
+
+ {% set color = "" %}
+ {% set bgcolor = "" %}
+ {% set otherstyle = "" %}
+ {% if config.id=='colorbgbodydark' %}
+ {% set bgcolor = val %}
+ {% set color = app.session.get('colorftbodydark') %}
+ {% elseif config.id=='colorbgbodylight' %}
+ {% set bgcolor = val %}
+ {% set color = app.session.get('colorftbodylight') %}
+ {% elseif config.id=='colorfttitledark' %}
+ {% set bgcolor = app.session.get('colorbgbodydark') %}
+ {% set color = val %}
+ {% elseif config.id=='colorfttitlelight' %}
+ {% set bgcolor = app.session.get('colorbgbodylight') %}
+ {% set color = val %}
+ {% elseif config.id=='colorftbodydark' %}
+ {% set bgcolor = app.session.get('colorbgbodydark') %}
+ {% set color = val %}
+ {% elseif config.id=='colorftbodylight' %}
+ {% set bgcolor = app.session.get('colorbgbodylight') %}
+ {% set color = val %}
+ {% elseif config.id=='fonttitle' %}
+ {% set color = app.session.get('colorfttitlelight') %}
+ {% set otherstyle = "font-family: "~val~";" %}
+ {% elseif config.id=='logodark' %}
+ {% set bgcolor = app.session.get('colorbgbodydark') %}
+ {% set otherstyle = "text-align: center;" %}
+ {% elseif config.id=='logolight' %}
+ {% set bgcolor = app.session.get('colorbgbodylight') %}
+ {% set otherstyle = "text-align: center;" %}
+ {% endif %}
+
+
+
+
+ {% if config.type=="boolean" %}
+ {% if val=="0" %} Non
+ {% elseif val=="1" %} Oui
+ {%endif%}
+ {% elseif config.type=="logo" %}
+ {%if not val is empty %}
+
+ {% endif %}
+ {% elseif config.type=="header" %}
+ {%if not val is empty %}
+
+ {% endif %}
+ {% else %}
+ {{ val|raw }}
+ {% endif %}
+ |
+
+ {% endif %}
+ {% endfor %}
+
+
diff --git a/templates/Cron/edit.html.twig b/templates/Cron/edit.html.twig
new file mode 100644
index 0000000..0021824
--- /dev/null
+++ b/templates/Cron/edit.html.twig
@@ -0,0 +1,58 @@
+
+{% extends 'base.html.twig' %}
+
+{% block body %}
+{{ form_start(form) }}
+
+
+ {{ form_widget(form.submit) }}
+
+ Annuler
+
+
+
+ {% if app.session.flashbag.has('error') %}
+
+ Erreur
+ {% for flashMessage in app.session.flashbag.get('error') %}
+ {{ flashMessage }}
+ {% endfor %}
+
+ {% endif %}
+
+ {% if app.session.flashbag.has('notice') %}
+
+ Information
+ {% for flashMessage in app.session.flashbag.get('notice') %}
+ {{ flashMessage }}
+ {% endfor %}
+
+ {% endif %}
+
+
+
+
+ {{ form_row(form.command) }}
+ {{ form_row(form.jsonargument) }}
+ {{ form_row(form.statut) }}
+ {{ form_row(form.repeatinterval) }}
+ {{ form_row(form.nextexecdate) }}
+
+
+{{ form_end(form) }}
+{% endblock %}
+
+{% block localjavascript %}
+ $(document).ready(function() {
+ $("#command").focus();
+ });
+{% endblock %}
+
+
+
diff --git a/templates/Cron/list.html.twig b/templates/Cron/list.html.twig
new file mode 100644
index 0000000..697a335
--- /dev/null
+++ b/templates/Cron/list.html.twig
@@ -0,0 +1,47 @@
+{% extends "base.html.twig" %}
+
+{% block body %}
+
+
+
+
+
+
+
+
+
+
+ Action |
+ Prochaine exécution |
+ Command |
+ Description |
+ Statut |
+
+
+
+
+
+
+{% endblock %}
+
+{% block localscript %}
+
+{% endblock %}
+
+
diff --git a/templates/Cron/logs.html.twig b/templates/Cron/logs.html.twig
new file mode 100644
index 0000000..57a0c18
--- /dev/null
+++ b/templates/Cron/logs.html.twig
@@ -0,0 +1,16 @@
+{% extends 'base.html.twig' %}
+
+{% block body %}
+
+
+ {% if cron_activate %}
+ Log CRON
+ {% endif %}
+ Log PROD
+ Log DEV
+ {% if cron_activate %}
+ Dump de la Base
+ {% endif %}
+{% endblock %}
\ No newline at end of file
diff --git a/templates/Crop/crop01.html.twig b/templates/Crop/crop01.html.twig
new file mode 100644
index 0000000..10e282c
--- /dev/null
+++ b/templates/Crop/crop01.html.twig
@@ -0,0 +1,36 @@
+{% extends 'base.html.twig' %}
+
+{% block encoretags %}
+ {{ encore_entry_link_tags('dropzone') }}
+{% endblock %}
+
+
+
+{% block body %}
+ Annuler
+
+{% endblock %}
+
+{% block localscript %}
+ {{ encore_entry_script_tags('dropzone') }}
+
+
+{% endblock %}
diff --git a/templates/Crop/crop02.html.twig b/templates/Crop/crop02.html.twig
new file mode 100644
index 0000000..84da513
--- /dev/null
+++ b/templates/Crop/crop02.html.twig
@@ -0,0 +1,125 @@
+{% extends 'base.html.twig' %}
+
+{% block localstyle %}
+
+{% endblock %}
+
+{% block body %}
+ {% if not submited %}
+ {{ form_start(form) }}
+ {{ form_widget(form.submit) }}
+
+
+ {% if ratio=="1:1" %}
+ {% set class="width:90px; height:90px;" %}
+ {% elseif ratio=="16:9" %}
+ {% set class="width:160px; height:90px;" %}
+ {% elseif ratio=="16:2" %}
+ {% set class="width:160px; height:20px;" %}
+ {% endif %}
+
+
+
+
+
+
+ {{ form_end(form) }}
+ {% endif %}
+{% endblock %}
+
+
+{% block localscript %}
+
+{% endblock %}
diff --git a/templates/Form/fields.html.twig b/templates/Form/fields.html.twig
new file mode 100644
index 0000000..2a56ce5
--- /dev/null
+++ b/templates/Form/fields.html.twig
@@ -0,0 +1,124 @@
+{% extends 'form_div_layout.html.twig' %}
+
+{# Voir https://github.com/symfony/symfony/blob/master/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig #}
+
+{# On commence par simplement ajouter le form-group au row de nos formulaires #}
+{% block form_row -%}
+ {% set attr = attr|merge({'help': (attr.help|default(true)) }) %}
+
+ {{- form_label(form) }}
+ {{- form_widget(form) }}
+ {{ form_errors(form) }}
+
+{%- endblock form_row %}
+
+{# Puis on modifie très simplement nos input et textarea les plus importants pour y ajouter le class imposée par Bootstrap 3 #}
+{% block textarea_widget %}
+ {% set attr = attr|merge({'class': attr.class|default('') ~ ' form-control'}) %}
+ {{ parent() }}
+{% endblock textarea_widget %}
+
+{% block form_widget_simple %}
+ {% set attr = attr|merge({'class': attr.class|default('') ~ ' form-control'}) %}
+ {{ parent() }}
+{% endblock form_widget_simple %}
+
+{% block form_label -%}
+ {% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ ' control-label')|trim}) %}
+ {% if 'checkbox' not in block_prefixes %}
+ {% if label is not same as(false) -%}
+ {% if not compound -%}
+ {% set label_attr = label_attr|merge({'for': id}) %}
+ {%- endif %}
+ {% if required -%}
+ {% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ ' required')|trim}) %}
+ {%- endif %}
+ {% if label is empty -%}
+ {% set label = name|humanize %}
+ {%- endif -%}
+
+
+ {%- endif %}
+ {% endif %}
+{%- endblock form_label %}
+
+{# et enfin les erreurs #}
+{% block form_errors %}
+ {% if errors|length > 0 %}
+ {% if attr.help is defined and attr.help %}
+
+ {% for error in errors %}
+ {{ error.message }}
+ {% endfor %}
+
+ {% else %}
+
+
+ {% for error in errors %}
+ {{ error.message|raw }}
+ {% endfor %}
+
+ {% endif %}
+ {% endif %}
+{% endblock form_errors %}
+
+{# Personnalisation des boutons #}
+{% block button_widget -%}
+ {% if label is empty -%}
+ {% set label = name|humanize %}
+ {%- endif -%}
+ {% set attr = attr|merge({'class': (attr.class|default('') ~ '')|trim}) %}
+
+{%- endblock button_widget %}
+
+{# Personnalisation des select #}
+{% block choice_widget_collapsed %}
+ {% set attr = attr|merge({'class': (attr.class|default('') ~ ' form-control')|trim}) %}
+ {{ parent() }}
+{%- endblock choice_widget_collapsed %}
+
+{% block choice_widget %}
+ {% if expanded %}
+
+ {% for child in form %}
+ -
+ {{ form_widget(child) }}
+ {{ form_label(child) }}
+
+ {% endfor %}
+
+ {% else %}
+ {{ parent() }}
+ {% endif %}
+{% endblock choice_widget %}
+
+{% block checkbox_widget %}
+
+{% endblock checkbox_widget %}
+
+{% block radio_widget %}
+
+{% endblock radio_widget %}
+
+
+
+{% block file_widget %}
+ {% set type = type|default('file') %}
+
+{% endblock file_widget %}
diff --git a/templates/Group/edit.html.twig b/templates/Group/edit.html.twig
new file mode 100755
index 0000000..4667318
--- /dev/null
+++ b/templates/Group/edit.html.twig
@@ -0,0 +1,98 @@
+{% extends 'base.html.twig' %}
+
+{% block body %}
+{{ form_start(form) }}
+
+
+ {{ form_widget(form.submit) }}
+ Annuler
+ {% if mode=="update" %}
+ Supprimer
+ {% endif %}
+
+ {% if app.session.flashbag.has('error') %}
+
+ Erreur
+ {% for flashMessage in app.session.flashbag.get('error') %}
+ {{ flashMessage }}
+ {% endfor %}
+
+ {% endif %}
+
+ {% if app.session.flashbag.has('notice') %}
+
+ Information
+ {% for flashMessage in app.session.flashbag.get('notice') %}
+ {{ flashMessage }}
+ {% endfor %}
+
+ {% endif %}
+
+
+
+
+
+ {{ form_row(form.label) }}
+
+ Caractères interdits = caractères spéciaux sauf ' @ . - _
+
+
+ {% if form.owner is defined %}
+ {{ form_row(form.owner) }}
+ {% endif %}
+
+ {% if form.isworkgroup is defined %}
+ {{ form_row(form.isworkgroup) }}
+ {% endif %}
+
+
+ {{ form_row(form.isopen) }}
+
+
+ {{ form_row(form.description) }}
+
+ {% if form.ldapfilter is defined %}
+ {{ form_row(form.fgassoc) }}
+ {{ form_row(form.ldapfilter) }}
+ {% endif %}
+
+ {% if form.attributes is defined %}
+ {{ form_row(form.fgassoc) }}
+ {{ form_row(form.attributes) }}
+ {% endif %}
+
+ {% if form.email is defined %}
+ {{ form_row(form.email) }}
+ {% endif %}
+
+
+
+{{ form_end(form) }}
+{% endblock %}
+
+{% block localscript %}
+
+
+{% endblock %}
+
diff --git a/templates/Group/list.html.twig b/templates/Group/list.html.twig
new file mode 100644
index 0000000..e1dad74
--- /dev/null
+++ b/templates/Group/list.html.twig
@@ -0,0 +1,47 @@
+{% extends 'base.html.twig' %}
+
+{% block body %}
+
+
+ {%if access=="admin" %}
+ Ajouter
+ {%elseif access=="user" and app.session.get("submitgroup") %}
+ Ajouter
+ {% endif %}
+
+
+
+
+
+
+
+
+ Action |
+ Groupe |
+ Groupe de Travail |
+ Ouvert |
+ Propriétaire |
+
+
+
+
+
+{% endblock %}
+
+{% block localscript %}
+
+{% endblock %}
diff --git a/templates/Group/users.html.twig b/templates/Group/users.html.twig
new file mode 100644
index 0000000..7f2c2ac
--- /dev/null
+++ b/templates/Group/users.html.twig
@@ -0,0 +1,161 @@
+{% extends 'base.html.twig' %}
+
+{% block body %}
+
+ Fermer
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Action |
+ Avatar |
+ Login |
+ Email |
+ {% if group.isworkgroup %}
+ Permissions |
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Action |
+ Avatar |
+ Login |
+ Email |
+ {% if group.isworkgroup %}
+ Permissions |
+ {% endif %}
+
+
+
+
+
+
+
+
+{% endblock %}
+
+{% block localscript %}
+
+
+{% endblock %}
diff --git a/templates/Home/docrest.html.twig b/templates/Home/docrest.html.twig
new file mode 100644
index 0000000..a63ded7
--- /dev/null
+++ b/templates/Home/docrest.html.twig
@@ -0,0 +1,18 @@
+{% extends "base.html.twig" %}
+
+{% block localstyle %}
+
+{% endblock %}
+{% block body %}
+{{ render(path("app_swagger_ui")) }}
+{% endblock %}
\ No newline at end of file
diff --git a/templates/Home/home.html.twig b/templates/Home/home.html.twig
new file mode 100644
index 0000000..59f00fe
--- /dev/null
+++ b/templates/Home/home.html.twig
@@ -0,0 +1,82 @@
+{% extends "base.html.twig" %}
+
+{% block body %}
+ {% if app.user %}
+
+
+ {% endif %}
+{% endblock %}
+
+{% block localscript %}
+{% if app.user %}
+
+{% endif %}
+{% endblock %}
\ No newline at end of file
diff --git a/templates/Home/loginLDAP.html.twig b/templates/Home/loginLDAP.html.twig
new file mode 100755
index 0000000..15eda6f
--- /dev/null
+++ b/templates/Home/loginLDAP.html.twig
@@ -0,0 +1,36 @@
+{% extends "base.html.twig" %}
+
+{% block localstyle %}
+
+{% endblock %}
+
+{% block body %}
+
+
+
{{appName}}
+ {{ form_start(form, {'action': path('app_loginldapcheck'), 'method': 'POST'}) }}
+
+
+ {{ form_row(form.username) }}
+ {{ form_row(form.password) }}
+ {{ form_row(form.submit) }}
+
+
+ {% if appMasteridentity == "SQL"%}
+
Mot de passe oublié ?
+ {% endif %}
+ {{ form_end(form) }}
+
+{% endblock %}
+
+{% block localscript %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/templates/Home/loginSQL.html.twig b/templates/Home/loginSQL.html.twig
new file mode 100755
index 0000000..d623da7
--- /dev/null
+++ b/templates/Home/loginSQL.html.twig
@@ -0,0 +1,46 @@
+{% extends "base.html.twig" %}
+
+{% block localstyle %}
+
+{% endblock %}
+
+{% block body %}
+
+
+
{{appName}}
+
+
+{% endblock %}
+
+{% block localscript %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/templates/Home/mail.html.twig b/templates/Home/mail.html.twig
new file mode 100644
index 0000000..a99b325
--- /dev/null
+++ b/templates/Home/mail.html.twig
@@ -0,0 +1,9 @@
+{% block subject %}
+ {{ subject }}
+{% endblock %}
+
+{% block body %}
+{% autoescape %}
+{{ body|raw }}
+{% endautoescape %}
+{% endblock %}
\ No newline at end of file
diff --git a/templates/Include/sidebaradmin.html.twig b/templates/Include/sidebaradmin.html.twig
new file mode 100644
index 0000000..2f11f07
--- /dev/null
+++ b/templates/Include/sidebaradmin.html.twig
@@ -0,0 +1,105 @@
+{% set sidebar = [
+ {
+ 'id': 'sidebar-config',
+ 'icon': 'fa fa-gear',
+ 'name' : 'CONFIGURATION',
+ 'items' : [
+ {
+ icon: 'fa fa-table',
+ route: 'app_admin_config',
+ name: 'Général',
+ },
+ ]
+ },
+ {
+ 'id': 'sidebar-organisation',
+ 'icon': 'fa fa-sitemap',
+ 'name' : 'ORGANISATION',
+ 'items' : [
+ {
+ icon: 'fa fa-building',
+ route: 'app_admin_niveau01',
+ name: appNiveau01label~'s',
+ },
+ {
+ icon: 'fa fa-sitemap',
+ route: 'app_admin_niveau02',
+ name: appNiveau02label~'s',
+ },
+ {
+ icon: 'fa fa-users',
+ route: 'app_admin_group',
+ name: 'Groupes',
+ },
+ {
+ icon: 'fa fa-child',
+ route: 'app_admin_user',
+ name: 'Utilisateurs',
+ },
+ {
+ icon: 'fa fa-edit',
+ route: 'app_admin_registration',
+ name: 'Inscriptions',
+ },
+ {
+ icon: 'fa fa-tasks',
+ route: 'app_admin_whitelist',
+ name: 'Listes Blanche',
+ },
+ ]
+ },
+ {
+ 'id': 'sidebar-cron',
+ 'icon': 'fa fa-wrench',
+ 'name' : 'OUTILS',
+ 'items' : [
+ {
+ icon: 'fa fa-cogs',
+ route: 'app_admin_cron',
+ name: 'Cron Jobs',
+ },
+
+ {
+ icon: 'fas fa-star-of-life',
+ route: 'app_rest',
+ name: 'API',
+ },
+ ]
+ },
+]
+%}
+
+
+
\ No newline at end of file
diff --git a/templates/Include/sidebarmodo.html.twig b/templates/Include/sidebarmodo.html.twig
new file mode 100644
index 0000000..e91d412
--- /dev/null
+++ b/templates/Include/sidebarmodo.html.twig
@@ -0,0 +1,64 @@
+{% set sidebar = [
+ {
+ 'id': 'sidebar-organisation',
+ 'icon': 'fa fa-sitemap',
+ 'name' : 'ORGANISATION',
+ 'items' : [
+ {
+ icon: 'fa fa-sitemap',
+ route: 'app_modo_niveau02',
+ name: appNiveau02label~'s',
+ },
+ {
+ icon: 'fa fa-users',
+ route: 'app_modo_group',
+ name: 'Groupes',
+ },
+ {
+ icon: 'fa fa-child',
+ route: 'app_modo_user',
+ name: 'Utilisateurs',
+ },
+ {
+ icon: 'fa fa-edit',
+ route: 'app_modo_registration',
+ name: 'Inscriptions',
+ },
+ ]
+ }
+]
+%}
+
+
+
\ No newline at end of file
diff --git a/templates/Include/style.css.twig b/templates/Include/style.css.twig
new file mode 100644
index 0000000..61f434d
--- /dev/null
+++ b/templates/Include/style.css.twig
@@ -0,0 +1,24 @@
+
\ No newline at end of file
diff --git a/templates/Niveau01/edit.html.twig b/templates/Niveau01/edit.html.twig
new file mode 100755
index 0000000..61e9d92
--- /dev/null
+++ b/templates/Niveau01/edit.html.twig
@@ -0,0 +1,120 @@
+{% extends 'base.html.twig' %}
+
+{% block body %}
+{{ form_start(form) }}
+
+
+ {{ form_widget(form.submit) }}
+ Annuler
+ {% if mode=="update" %}
+ Supprimer
+ {% endif %}
+
+ {% if app.session.flashbag.has('error') %}
+
+ Erreur
+ {% for flashMessage in app.session.flashbag.get('error') %}
+ {{ flashMessage }}
+ {% endfor %}
+
+ {% endif %}
+
+ {% if app.session.flashbag.has('notice') %}
+
+ Information
+ {% for flashMessage in app.session.flashbag.get('notice') %}
+ {{ flashMessage }}
+ {% endfor %}
+
+ {% endif %}
+
+
+
+
+
+ {{ form_row(form.label) }}
+
+ Caractères interdits = caractères spéciaux sauf ' @ . - _
+
+
+ {% if form.ldapfilter is defined %}
+ {{ form_row(form.fgassocldap) }}
+
+
+ {{ form_row(form.ldapfilter) }}
+
+ Exemple = (cn=monniveau01)
+ La recherche pour retrouver l'entrée annuaire se fera via la branche présente en parametre d'environnement LDAP_BASENIVEAU01
+ On rattachera les utilisateurs membres de cette entrée via le parametre d'environnement LDAP_GROUPMEMBER
+ Si aucun niveau01 est trouvé pour un utilisateur il sera rattaché au niveau01 avec l'id -1
+ Si un utilisateur est membre de plusieurs niveau01 annuaire, il sera rattaché au dernier niveau01 trouvé dans l'annuaire
+
+
+
+ {% endif %}
+
+ {% if form.attributes is defined %}
+ {{ form_row(form.fgassocsso) }}
+
+
+ {{ form_row(form.attributes) }}
+
+ Exemple = {"user_groups" : "monniveau01"}
+ user_groups étant le parametre d'environnement CAS_GROUP
+
+
+ {% endif %}
+
+
+{{ form_end(form) }}
+{% endblock %}
+
+{% block localscript %}
+
+{% endblock %}
diff --git a/templates/Niveau01/list.html.twig b/templates/Niveau01/list.html.twig
new file mode 100644
index 0000000..614b0d8
--- /dev/null
+++ b/templates/Niveau01/list.html.twig
@@ -0,0 +1,47 @@
+{% extends 'base.html.twig' %}
+
+{% block body %}
+
+
+ Ajouter
+
+
+
+
+
+
+
+
+ Action |
+ {{appNiveau01label}} |
+ {% if appMasteridentity=="LDAP" or appSynchro=="LDAP2NINE" %}
+ Filtre LDAP |
+ {% endif %}
+
+ {% if appMasteridentity=="SSO" %}
+ Attributs SSO |
+ {% endif %}
+
+
+
+
+
+{% endblock %}
+
+{% block localscript %}
+
+{% endblock %}
diff --git a/templates/Niveau02/edit.html.twig b/templates/Niveau02/edit.html.twig
new file mode 100755
index 0000000..24be361
--- /dev/null
+++ b/templates/Niveau02/edit.html.twig
@@ -0,0 +1,60 @@
+{% extends 'base.html.twig' %}
+
+{% block body %}
+{{ form_start(form) }}
+
+
+ {{ form_widget(form.submit) }}
+ Annuler
+ {% if mode=="update" %}
+ Supprimer
+ {% endif %}
+
+ {% if app.session.flashbag.has('error') %}
+
+ Erreur
+ {% for flashMessage in app.session.flashbag.get('error') %}
+ {{ flashMessage }}
+ {% endfor %}
+
+ {% endif %}
+
+ {% if app.session.flashbag.has('notice') %}
+
+ Information
+ {% for flashMessage in app.session.flashbag.get('notice') %}
+ {{ flashMessage }}
+ {% endfor %}
+
+ {% endif %}
+
+
+
+
+
+ {{ form_row(form.niveau01) }}
+
+ {{ form_row(form.label) }}
+
+ Caractères interdits = caractères spéciaux sauf ' @ . - _
+
+
+ {% if form.ldapfilter is defined %}
+ {{ form_row(form.ldapfilter) }}
+ {% endif %}
+
+ {% if form.attributes is defined %}
+ {{ form_row(form.attributes) }}
+ {% endif %}
+
+
+{{ form_end(form) }}
+{% endblock %}
diff --git a/templates/Niveau02/list.html.twig b/templates/Niveau02/list.html.twig
new file mode 100644
index 0000000..e125ab0
--- /dev/null
+++ b/templates/Niveau02/list.html.twig
@@ -0,0 +1,41 @@
+{% extends 'base.html.twig' %}
+
+{% block body %}
+
+
+ Ajouter
+
+
+
+
+
+
+
+
+ Action |
+ {{appNiveau01label}} |
+ {{appNiveau02label}} |
+
+
+
+
+
+{% endblock %}
+
+{% block localscript %}
+
+{% endblock %}
diff --git a/templates/Registration/edit.html.twig b/templates/Registration/edit.html.twig
new file mode 100755
index 0000000..7df9eef
--- /dev/null
+++ b/templates/Registration/edit.html.twig
@@ -0,0 +1,222 @@
+{% extends 'base.html.twig' %}
+
+{% block localstyle %}
+
+{% endblock %}
+{% block body %}
+{{ form_start(form) }}
+
+ {% if appModeregistration!="none" %}
+
+
+ {{ form_widget(form.submit) }}
+
+ {% if mode=="update" %}
+ {{ form_widget(form.save) }}
+ Envoyer un mail
+ Annuler
+ Supprimer
+ {%endif%}
+
+
+
+
+ {% if app.session.flashbag.has('error') %}
+
+ Erreur
+ {% for flashMessage in app.session.flashbag.get('error') %}
+ {{ flashMessage | raw }}
+ {% endfor %}
+
+ {% endif %}
+
+ {% if app.session.flashbag.has('notice') %}
+
+ Information
+ {% for flashMessage in app.session.flashbag.get('notice') %}
+ {{ flashMessage | raw }}
+ {% endfor %}
+
+ {% endif %}
+
+ {% if mode=="send" %}
+
+ Information
+ Cette action permet de générer une nouvelle clé de validation et de renouveller la date d'expiration de cette clé.
+ Un email sera envoyé à l'utilisateur
+
+
+ {% endif %}
+
+
+ {% if mode=="update" %}
+
+
+
+
+ {{ form_widget(form.note) }}
+
+
+
+ {%endif%}
+
+
+
+
+
+
+ {{ form_row(form.username) }}
+
+
+ Formatez votre login sous la forme prenom.nom dans la mesure du possible
+ Taille minimum = 5 caractères
+ Caractères interdits = accent, espace, caractères spéciaux sauf @ . - _
+
+
+ {% if form.password is defined %}
+ {{ form_row(form.password) }}
+
{{ form_row(form.passwordplain) }}
+
+ Le mot de passe doit comporter au moins 8 caractères, avec obligatoirement des lettres, chiffres et caractères spéciaux.
+
+
+ {% if form.captcha is defined %}
+ {{ form_row(form.captcha) }}
+ {% endif %}
+ {% endif %}
+
+
+
+
+
+
+
+ {{ form_row(form.motivation) }}
+
+
+
+
+
+
+
+
+ {{ form_row(form.niveau01) }}
+ {{ form_row(form.niveau02) }}
+ {{ form_row(form.firstname) }}
+ {{ form_row(form.lastname) }}
+ {{ form_row(form.email) }}
+
+ Votre mail ne fait pas partie des mails professonniels pré-inscrits dans {{app.session.get("appname")}}.
+ S'il s'agit de votre mail professionnel vous devez compléter le formulaire "Motivations" pour que l'administrateur de {{app.session.get("appname")}} valide votre demannde d'inscription.
+
+ {{ form_row(form.isvisible) }}
+
+
+
+
+
+
+ {{ form_row(form.job) }}
+ {{ form_row(form.position) }}
+ {{ form_row(form.postaladress) }}
+ {{ form_row(form.telephonenumber) }}
+
+
+
+
+ {% endif %}
+{{ form_end(form) }}
+{% endblock %}
+
+
+
+{% block localscript %}
+
+{% endblock %}
diff --git a/templates/Registration/info.html.twig b/templates/Registration/info.html.twig
new file mode 100644
index 0000000..c8c2631
--- /dev/null
+++ b/templates/Registration/info.html.twig
@@ -0,0 +1,33 @@
+{% extends 'base.html.twig' %}
+
+{% block body %}
+
+
+
+
+ {% autoescape %}
+
{{ info|raw }}
+ {% endautoescape %}
+
+
+{% endblock %}
+
+{% block localscript %}
+
+
+
+{% endblock %}
+
diff --git a/templates/Registration/list.html.twig b/templates/Registration/list.html.twig
new file mode 100644
index 0000000..48760dd
--- /dev/null
+++ b/templates/Registration/list.html.twig
@@ -0,0 +1,44 @@
+{% extends 'base.html.twig' %}
+
+{% block body %}
+
+
+
+
+
+
+
+
+
+
+ Action |
+ Login |
+ Email |
+ {{ appNiveau01label }} |
+ Statut |
+ Expire le |
+
+
+
+
+
+
+{% endblock %}
+
+{% block localscript %}
+
+{% endblock %}
diff --git a/templates/Registration/resetpwd01.html.twig b/templates/Registration/resetpwd01.html.twig
new file mode 100755
index 0000000..dfc36c2
--- /dev/null
+++ b/templates/Registration/resetpwd01.html.twig
@@ -0,0 +1,54 @@
+{% extends 'base.html.twig' %}
+
+{% block body %}
+
+{{ form_start(form) }}
+
+
+ {{ form_widget(form.submit) }}
+
+ {% if form.captcha is defined %}
+ {{ form_label(form.captcha) }}
+ {{ form_widget(form.captcha) }}
+ {% endif %}
+
+
+
+ {% if app.session.flashbag.has('error') %}
+
+ Erreur
+ {% for flashMessage in app.session.flashbag.get('error') %}
+ {{ flashMessage }}
+ {% endfor %}
+
+ {% endif %}
+
+ {% if app.session.flashbag.has('notice') %}
+
+ Information
+ {% for flashMessage in app.session.flashbag.get('notice') %}
+ {{ flashMessage }}
+ {% endfor %}
+
+ {% endif %}
+
+
+
+
+
+ {{ form_label(form.email) }}
+ {{ form_widget(form.email) }}
+
+
+ Veuillez saisir l'adresse mail que vous avez utilisé pour votre compte.
+ Vous recevrez un lien permettant de réinitialiser votre mot de passe.
+
+
+
+{{ form_end(form) }}
+{% endblock %}
+
diff --git a/templates/Registration/resetpwd02.html.twig b/templates/Registration/resetpwd02.html.twig
new file mode 100755
index 0000000..dc16eda
--- /dev/null
+++ b/templates/Registration/resetpwd02.html.twig
@@ -0,0 +1,62 @@
+{% extends 'base.html.twig' %}
+
+{% block localstyle %}
+
+{% endblock %}
+
+
+{% block body %}
+{{ form_start(form) }}
+
+
+ {{ form_widget(form.submit) }}
+
+ {% if form.captcha is defined %}
+ {{ form_label(form.captcha) }}
+ {{ form_widget(form.captcha) }}
+ {% endif %}
+
+
+
+ {% if app.session.flashbag.has('error') %}
+
+ Erreur
+ {% for flashMessage in app.session.flashbag.get('error') %}
+ {{ flashMessage }}
+ {% endfor %}
+
+ {% endif %}
+
+ {% if app.session.flashbag.has('notice') %}
+
+ Information
+ {% for flashMessage in app.session.flashbag.get('notice') %}
+ {{ flashMessage }}
+ {% endfor %}
+
+ {% endif %}
+
+
+
+
+
+ {{ form_row(form.password) }}
+
{{ form_row(form.passwordplain) }}
+
+ Le mot de passe doit comporter au moins 8 caractères, avec obligatoirement des lettres, chiffres et caractères spéciaux.
+
+
+
+{{ form_end(form) }}
+{% endblock %}
+
diff --git a/templates/User/edit.html.twig b/templates/User/edit.html.twig
new file mode 100755
index 0000000..afb12d8
--- /dev/null
+++ b/templates/User/edit.html.twig
@@ -0,0 +1,482 @@
+{% extends 'base.html.twig' %}
+
+{% block localstyle %}
+
+{% endblock %}
+
+
+{% block body %}
+{{ form_start(form) }}
+
+
+ {{ form_widget(form.submit) }}
+ {% if access=="admin" or access=="modo" %}
+ Annuler
+ {% if mode=="update" %}
+ {% if access=="admin" %}
+ Supprimer
+ {% else %}
+ {% if not user.hasrole("ROLE_ADMIN") and not user.hasrole("ROLE_MODO") %}
+ Supprimer
+ {% endif %}
+ {% endif %}
+ {% endif %}
+ {% else %}
+ Annuler
+ {% endif %}
+
+ {% if app.session.flashbag.has('error') %}
+
+ Erreur
+ {% for flashMessage in app.session.flashbag.get('error') %}
+ {{ flashMessage | raw }}
+ {% endfor %}
+
+
+ {% endif %}
+
+ {% if app.session.flashbag.has('notice') %}
+
+ Information
+ {% for flashMessage in app.session.flashbag.get('notice') %}
+ {{ flashMessage | raw }}
+ {% endfor %}
+
+
+ {% endif %}
+
+
+
+
+ {% set avatar= "noavatar.png" %}
+ {% if user.avatar %}
+ {% set avatar= user.avatar %}
+ {% endif %}
+
+ {{ form_widget(form.avatar) }}
+
Modifier
+
+
+
+
+
+
+
+
+ {{ form_row(form.username) }}
+
+
+ Formatez votre login sous la forme prenom.nom dans la mesure du possible
+ Taille minimum = 5 caractères
+ Caractères interdits = accent, espace, caractères spéciaux sauf @ . - _
+
+
+ {% if form.password is defined %}
+ {{ form_row(form.password) }}
+
{{ form_row(form.passwordplain) }}
+
+ Le mot de passe doit comporter au moins 8 caractères, avec obligatoirement des lettres, chiffres et caractères spéciaux.
+
+ {% endif %}
+
+ {% if form.roles is defined %}
+ {{ form_row(form.roles) }}
+ {% endif %}
+
+
+
+ {% if access=="admin" %}
+
+
+
+
+ {% if is_granted('ROLE_ADMIN') %}
+
Ajouter
+
+ {% endif %}
+
+ {{ form_widget(form.linkmodos) }}
+
+
+
+
+
+ Action |
+ {{ appNiveau01label }} |
+
+
+
+
+ {% for modo in user.modos %}
+
+ |
+
+ {{ modo.niveau01.label }}
+ |
+
+ {% endfor %}
+
+
+
+
+
+ {% endif %}
+
+
+
+
+
+ {{ form_row(form.motivation) }}
+
+
+
+ {% if access=="admin" or access=="modo"%}
+
+
+
+ {{ form_widget(form.note) }}
+
+
+ {%endif%}
+
+
+
+
+
+ {{ form_row(form.visitedate) }}
+ {{ form_row(form.visitecpt) }}
+
+
+
+
+
+
+
+
+
+ {{ form_row(form.niveau01) }}
+ {{ form_row(form.niveau02) }}
+ {{ form_row(form.firstname) }}
+ {{ form_row(form.lastname) }}
+ {{ form_row(form.email) }}
+ {{ form_row(form.isvisible) }}
+
+
+
+
+
+
+
+
Inscription à un Groupe
+
+
+ {{ form_widget(form.linkgroups) }}
+
+
+
+
+ Action |
+ Groupe |
+
+
+
+
+ {% for usergroup in user.groups %}
+
+ {% set canleave = true %}
+ {% if usergroup.group.id<0 or usergroup.group.owner==user %}
+ {% set canleave = false %}
+ {% elseif access!="admin" and access!="modo"%}
+ {% if not usergroup.group.isworkgroup and not usergroup.group.isopen %}
+ {% set canleave = false %}
+ {% endif %}
+ {% endif %}
+
+ {% if canleave %}
+ |
+ {% else %}
+ |
+ {% endif %}
+
+
+ {{ usergroup.group.label }}
+ |
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+ {{ form_row(form.job) }}
+ {{ form_row(form.position) }}
+ {{ form_row(form.postaladress) }}
+ {{ form_row(form.telephonenumber) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Action |
+ Groupe |
+
+
+
+
+ {% set listidgroup = [] %}
+ {% for usergroup in user.groups %}
+ {% set listidgroup = listidgroup|merge([usergroup.group.id]) %}
+ {% endfor %}
+
+ {% for group in listgroups %}
+ {% if group.id in listidgroup %}
+ {% set style = "display:none;" %}
+ {% else %}
+ {% set style = "" %}
+ {% endif %}
+
+
+ |
+
+ {{ group.label }}
+ |
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Action |
+ {{ appNiveau01label }} |
+
+
+
+
+ {% set listidmodo = [] %}
+ {% for usermodo in user.modos %}
+ {% set listidmodo = listidmodo|merge([usermodo.niveau01.id]) %}
+ {% endfor %}
+
+ {% for niveau01 in listmodos %}
+ {% if niveau01.id in listidmodo %}
+ {% set style = "display:none;" %}
+ {% else %}
+ {% set style = "" %}
+ {% endif %}
+
+
+ |
+
+ {{ niveau01.label }}
+ |
+
+ {% endfor %}
+
+
+
+
+
+
+{{ form_end(form) }}
+{% endblock %}
+
+{% block localscript %}
+
+{% endblock %}
diff --git a/templates/User/list.html.twig b/templates/User/list.html.twig
new file mode 100644
index 0000000..a60d732
--- /dev/null
+++ b/templates/User/list.html.twig
@@ -0,0 +1,64 @@
+{% extends 'base.html.twig' %}
+
+{% block body %}
+ {% if access=="admin" or access=="modo" %}
+
+ {% if appMasteridentity=="SQL" or not appSynchroPurgeUser %}
+ Ajouter
+ {% endif %}
+ {% endif %}
+
+
+
+
+
+
+
+
+ {% if access=="admin" or access=="modo"%}
+ Action |
+ {% endif %}
+
+ Avatar |
+ Login |
+ Nom |
+ Prénom |
+ Email |
+ Téléphone |
+ {{ appNiveau01label }} |
+ {{ appNiveau02label }} |
+ Visite |
+ Rôle |
+ Groupes |
+
+
+
+
+
+{% endblock %}
+
+{% block localscript %}
+
+{% endblock %}
diff --git a/templates/Whitelist/edit.html.twig b/templates/Whitelist/edit.html.twig
new file mode 100755
index 0000000..54d7492
--- /dev/null
+++ b/templates/Whitelist/edit.html.twig
@@ -0,0 +1,54 @@
+{% extends 'base.html.twig' %}
+
+{% block body %}
+{{ form_start(form) }}
+
+
+ {{ form_widget(form.submit) }}
+ Annuler
+ {% if mode=="update" %}
+ Supprimer
+ {% endif %}
+
+
+
+ {% if app.session.flashbag.has('error') %}
+
+ Erreur
+ {% for flashMessage in app.session.flashbag.get('error') %}
+ {{ flashMessage }}
+ {% endfor %}
+
+ {% endif %}
+
+ {% if app.session.flashbag.has('notice') %}
+
+ Information
+ {% for flashMessage in app.session.flashbag.get('notice') %}
+ {{ flashMessage }}
+ {% endfor %}
+
+ {% endif %}
+
+
+
+
+
+
+ {{ form_label(form.label) }}
+ {{ form_widget(form.label) }}
+
+
+{{ form_end(form) }}
+{% endblock %}
+
+{% block localjavascript %}
+{% endblock %}
diff --git a/templates/Whitelist/list.html.twig b/templates/Whitelist/list.html.twig
new file mode 100644
index 0000000..8fea047
--- /dev/null
+++ b/templates/Whitelist/list.html.twig
@@ -0,0 +1,42 @@
+{% extends 'base.html.twig' %}
+
+{% block body %}
+
+
+ Ajouter
+
+
+
+
+
+
+
+
+
+ Action |
+ Label |
+
+
+
+
+
+
+{% endblock %}
+
+{% block localscript %}
+
+{% endblock %}
diff --git a/templates/base.html.twig b/templates/base.html.twig
new file mode 100644
index 0000000..3cd53c7
--- /dev/null
+++ b/templates/base.html.twig
@@ -0,0 +1,194 @@
+
+
+
+
+ {% block title %}{{app.session.get("appname")}}{% endblock %}
+
+
+
+ {{ encore_entry_link_tags('app') }}
+ {{ encore_entry_script_tags('app') }}
+ {% block encoretags %}{% endblock encoretags %}
+
+
+ {{ include('Include/style.css.twig') }}
+ {% block localstyle %}{% endblock %}
+
+
+
+ {% if (useheader is defined and useheader) or (usemenu is defined and usemenu) %}
+
+ {% endif %}
+
+
+ {% if usesidebar is defined and usesidebar %}
+ {% if is_granted('ROLE_ADMIN') %}
+ {{ include('Include/sidebaradmin.html.twig') }}
+ {% elseif is_granted('ROLE_MODO') %}
+ {{ include('Include/sidebarmodo.html.twig') }}
+ {% endif %}
+ {%endif%}
+
+
+ {%if maxsize is defined %}
{%endif%}
+ {% block body %}{% endblock %}
+ {%if maxsize is defined %}
{%endif%}
+
+
+
+
+
+
+
+
+
+
+ {% block localscript %}{% endblock %}
+
+
+
\ No newline at end of file
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
new file mode 100644
index 0000000..469dcce
--- /dev/null
+++ b/tests/bootstrap.php
@@ -0,0 +1,11 @@
+bootEnv(dirname(__DIR__).'/.env');
+}
diff --git a/translations/.gitignore b/translations/.gitignore
new file mode 100644
index 0000000..e69de29
diff --git a/webpack.config.js b/webpack.config.js
new file mode 100644
index 0000000..14ef7e4
--- /dev/null
+++ b/webpack.config.js
@@ -0,0 +1,89 @@
+const Encore = require('@symfony/webpack-encore');
+
+// Manually configure the runtime environment if not already configured yet by the "encore" command.
+// It's useful when you use tools that rely on webpack.config.js file.
+if (!Encore.isRuntimeEnvironmentConfigured()) {
+ Encore.configureRuntimeEnvironment(process.env.NODE_ENV || 'dev');
+}
+
+Encore
+ // directory where compiled assets will be stored
+ .setOutputPath('public/build/')
+ // public path used by the web server to access the output path
+ .setPublicPath('/build')
+ // only needed for CDN's or sub-directory deploy
+ //.setManifestKeyPrefix('build/')
+
+ /*
+ * ENTRY CONFIG
+ *
+ * Each entry will result in one JavaScript file (e.g. app.js)
+ * and one CSS file (e.g. app.css) if your JavaScript imports CSS.
+ */
+ .addEntry('app', './assets/app.js')
+ .addEntry('dropzone', './assets/dropzone.js')
+
+ // enables the Symfony UX Stimulus bridge (used in assets/bootstrap.js)
+ .enableStimulusBridge('./assets/controllers.json')
+
+ // When enabled, Webpack "splits" your files into smaller pieces for greater optimization.
+ .splitEntryChunks()
+
+ // will require an extra script tag for runtime.js
+ // but, you probably want this, unless you're building a single-page app
+ .enableSingleRuntimeChunk()
+
+ /*
+ * FEATURE CONFIG
+ *
+ * Enable & configure other features below. For a full
+ * list of features, see:
+ * https://symfony.com/doc/current/frontend.html#adding-more-features
+ */
+ .cleanupOutputBeforeBuild()
+ .enableBuildNotifications()
+ .enableSourceMaps(!Encore.isProduction())
+ // enables hashed filenames (e.g. app.abc123.css)
+ .enableVersioning(Encore.isProduction())
+
+ .configureBabel((config) => {
+ config.plugins.push('@babel/plugin-proposal-class-properties');
+ })
+
+ // enables @babel/preset-env polyfills
+ .configureBabelPresetEnv((config) => {
+ config.useBuiltIns = 'usage';
+ config.corejs = 3;
+ })
+
+ .copyFiles([
+ {from: './assets/fonts', to: 'fonts/[path][name].[ext]'},
+ ])
+
+ .copyFiles([
+ {from: './node_modules/ckeditor4/', to: 'ckeditor/[path][name].[ext]', pattern: /\.(js|css)$/, includeSubdirectories: false},
+ {from: './node_modules/ckeditor4/adapters', to: 'ckeditor/adapters/[path][name].[ext]'},
+ {from: './node_modules/ckeditor4/lang', to: 'ckeditor/lang/[path][name].[ext]'},
+ {from: './node_modules/ckeditor4/plugins', to: 'ckeditor/plugins/[path][name].[ext]'},
+ {from: './node_modules/ckeditor4/skins', to: 'ckeditor/skins/[path][name].[ext]'},
+ {from: './assets/ckplugins', to: 'ckeditor/plugins/[path][name].[ext]'},
+ ])
+
+ // enables Sass/SCSS support
+ //.enableSassLoader()
+
+ // uncomment if you use TypeScript
+ //.enableTypeScriptLoader()
+
+ // uncomment if you use React
+ //.enableReactPreset()
+
+ // uncomment to get integrity="..." attributes on your script & link tags
+ // requires WebpackEncoreBundle 1.4 or higher
+ //.enableIntegrityHashes(Encore.isProduction())
+
+ // uncomment if you're having problems with a jQuery plugin
+ //.autoProvidejQuery()
+;
+
+module.exports = Encore.getWebpackConfig();
diff --git a/yarn.lock b/yarn.lock
new file mode 100644
index 0000000..850011d
--- /dev/null
+++ b/yarn.lock
@@ -0,0 +1,4325 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@ampproject/remapping@^2.1.0":
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d"
+ integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==
+ dependencies:
+ "@jridgewell/gen-mapping" "^0.1.0"
+ "@jridgewell/trace-mapping" "^0.3.9"
+
+"@babel/code-frame@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789"
+ integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==
+ dependencies:
+ "@babel/highlight" "^7.16.7"
+
+"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.17.10":
+ version "7.18.5"
+ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.5.tgz#acac0c839e317038c73137fbb6ef71a1d6238471"
+ integrity sha512-BxhE40PVCBxVEJsSBhB6UWyAuqJRxGsAw8BdHMJ3AKGydcwuWW4kOO3HmqBQAdcq/OP+/DlTVxLvsCzRTnZuGg==
+
+"@babel/core@^7.17.0":
+ version "7.18.5"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.5.tgz#c597fa680e58d571c28dda9827669c78cdd7f000"
+ integrity sha512-MGY8vg3DxMnctw0LdvSEojOsumc70g0t18gNyUdAZqB1Rpd1Bqo/svHGvt+UJ6JcGX+DIekGFDxxIWofBxLCnQ==
+ dependencies:
+ "@ampproject/remapping" "^2.1.0"
+ "@babel/code-frame" "^7.16.7"
+ "@babel/generator" "^7.18.2"
+ "@babel/helper-compilation-targets" "^7.18.2"
+ "@babel/helper-module-transforms" "^7.18.0"
+ "@babel/helpers" "^7.18.2"
+ "@babel/parser" "^7.18.5"
+ "@babel/template" "^7.16.7"
+ "@babel/traverse" "^7.18.5"
+ "@babel/types" "^7.18.4"
+ convert-source-map "^1.7.0"
+ debug "^4.1.0"
+ gensync "^1.0.0-beta.2"
+ json5 "^2.2.1"
+ semver "^6.3.0"
+
+"@babel/generator@^7.18.2":
+ version "7.18.2"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.2.tgz#33873d6f89b21efe2da63fe554460f3df1c5880d"
+ integrity sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==
+ dependencies:
+ "@babel/types" "^7.18.2"
+ "@jridgewell/gen-mapping" "^0.3.0"
+ jsesc "^2.5.1"
+
+"@babel/helper-annotate-as-pure@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862"
+ integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==
+ dependencies:
+ "@babel/types" "^7.16.7"
+
+"@babel/helper-builder-binary-assignment-operator-visitor@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz#38d138561ea207f0f69eb1626a418e4f7e6a580b"
+ integrity sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==
+ dependencies:
+ "@babel/helper-explode-assignable-expression" "^7.16.7"
+ "@babel/types" "^7.16.7"
+
+"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.17.10", "@babel/helper-compilation-targets@^7.18.2":
+ version "7.18.2"
+ resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.2.tgz#67a85a10cbd5fc7f1457fec2e7f45441dc6c754b"
+ integrity sha512-s1jnPotJS9uQnzFtiZVBUxe67CuBa679oWFHpxYYnTpRL/1ffhyX44R9uYiXoa/pLXcY9H2moJta0iaanlk/rQ==
+ dependencies:
+ "@babel/compat-data" "^7.17.10"
+ "@babel/helper-validator-option" "^7.16.7"
+ browserslist "^4.20.2"
+ semver "^6.3.0"
+
+"@babel/helper-create-class-features-plugin@^7.17.12", "@babel/helper-create-class-features-plugin@^7.18.0":
+ version "7.18.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.0.tgz#fac430912606331cb075ea8d82f9a4c145a4da19"
+ integrity sha512-Kh8zTGR9de3J63e5nS0rQUdRs/kbtwoeQQ0sriS0lItjC96u8XXZN6lKpuyWd2coKSU13py/y+LTmThLuVX0Pg==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.16.7"
+ "@babel/helper-environment-visitor" "^7.16.7"
+ "@babel/helper-function-name" "^7.17.9"
+ "@babel/helper-member-expression-to-functions" "^7.17.7"
+ "@babel/helper-optimise-call-expression" "^7.16.7"
+ "@babel/helper-replace-supers" "^7.16.7"
+ "@babel/helper-split-export-declaration" "^7.16.7"
+
+"@babel/helper-create-regexp-features-plugin@^7.16.7", "@babel/helper-create-regexp-features-plugin@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.12.tgz#bb37ca467f9694bbe55b884ae7a5cc1e0084e4fd"
+ integrity sha512-b2aZrV4zvutr9AIa6/gA3wsZKRwTKYoDxYiFKcESS3Ug2GTXzwBEvMuuFLhCQpEnRXs1zng4ISAXSUxxKBIcxw==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.16.7"
+ regexpu-core "^5.0.1"
+
+"@babel/helper-define-polyfill-provider@^0.3.1":
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665"
+ integrity sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==
+ dependencies:
+ "@babel/helper-compilation-targets" "^7.13.0"
+ "@babel/helper-module-imports" "^7.12.13"
+ "@babel/helper-plugin-utils" "^7.13.0"
+ "@babel/traverse" "^7.13.0"
+ debug "^4.1.1"
+ lodash.debounce "^4.0.8"
+ resolve "^1.14.2"
+ semver "^6.1.2"
+
+"@babel/helper-environment-visitor@^7.16.7", "@babel/helper-environment-visitor@^7.18.2":
+ version "7.18.2"
+ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.2.tgz#8a6d2dedb53f6bf248e31b4baf38739ee4a637bd"
+ integrity sha512-14GQKWkX9oJzPiQQ7/J36FTXcD4kSp8egKjO9nINlSKiHITRA9q/R74qu8S9xlc/b/yjsJItQUeeh3xnGN0voQ==
+
+"@babel/helper-explode-assignable-expression@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a"
+ integrity sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==
+ dependencies:
+ "@babel/types" "^7.16.7"
+
+"@babel/helper-function-name@^7.16.7", "@babel/helper-function-name@^7.17.9":
+ version "7.17.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz#136fcd54bc1da82fcb47565cf16fd8e444b1ff12"
+ integrity sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==
+ dependencies:
+ "@babel/template" "^7.16.7"
+ "@babel/types" "^7.17.0"
+
+"@babel/helper-hoist-variables@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246"
+ integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==
+ dependencies:
+ "@babel/types" "^7.16.7"
+
+"@babel/helper-member-expression-to-functions@^7.17.7":
+ version "7.17.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz#a34013b57d8542a8c4ff8ba3f747c02452a4d8c4"
+ integrity sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==
+ dependencies:
+ "@babel/types" "^7.17.0"
+
+"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437"
+ integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==
+ dependencies:
+ "@babel/types" "^7.16.7"
+
+"@babel/helper-module-transforms@^7.18.0":
+ version "7.18.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.0.tgz#baf05dec7a5875fb9235bd34ca18bad4e21221cd"
+ integrity sha512-kclUYSUBIjlvnzN2++K9f2qzYKFgjmnmjwL4zlmU5f8ZtzgWe8s0rUPSTGy2HmK4P8T52MQsS+HTQAgZd3dMEA==
+ dependencies:
+ "@babel/helper-environment-visitor" "^7.16.7"
+ "@babel/helper-module-imports" "^7.16.7"
+ "@babel/helper-simple-access" "^7.17.7"
+ "@babel/helper-split-export-declaration" "^7.16.7"
+ "@babel/helper-validator-identifier" "^7.16.7"
+ "@babel/template" "^7.16.7"
+ "@babel/traverse" "^7.18.0"
+ "@babel/types" "^7.18.0"
+
+"@babel/helper-optimise-call-expression@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2"
+ integrity sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==
+ dependencies:
+ "@babel/types" "^7.16.7"
+
+"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.17.12", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.17.12.tgz#86c2347da5acbf5583ba0a10aed4c9bf9da9cf96"
+ integrity sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA==
+
+"@babel/helper-remap-async-to-generator@^7.16.8":
+ version "7.16.8"
+ resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz#29ffaade68a367e2ed09c90901986918d25e57e3"
+ integrity sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.16.7"
+ "@babel/helper-wrap-function" "^7.16.8"
+ "@babel/types" "^7.16.8"
+
+"@babel/helper-replace-supers@^7.16.7", "@babel/helper-replace-supers@^7.18.2":
+ version "7.18.2"
+ resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.2.tgz#41fdfcc9abaf900e18ba6e5931816d9062a7b2e0"
+ integrity sha512-XzAIyxx+vFnrOxiQrToSUOzUOn0e1J2Li40ntddek1Y69AXUTXoDJ40/D5RdjFu7s7qHiaeoTiempZcbuVXh2Q==
+ dependencies:
+ "@babel/helper-environment-visitor" "^7.18.2"
+ "@babel/helper-member-expression-to-functions" "^7.17.7"
+ "@babel/helper-optimise-call-expression" "^7.16.7"
+ "@babel/traverse" "^7.18.2"
+ "@babel/types" "^7.18.2"
+
+"@babel/helper-simple-access@^7.17.7", "@babel/helper-simple-access@^7.18.2":
+ version "7.18.2"
+ resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.2.tgz#4dc473c2169ac3a1c9f4a51cfcd091d1c36fcff9"
+ integrity sha512-7LIrjYzndorDY88MycupkpQLKS1AFfsVRm2k/9PtKScSy5tZq0McZTj+DiMRynboZfIqOKvo03pmhTaUgiD6fQ==
+ dependencies:
+ "@babel/types" "^7.18.2"
+
+"@babel/helper-skip-transparent-expression-wrappers@^7.16.0":
+ version "7.16.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09"
+ integrity sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==
+ dependencies:
+ "@babel/types" "^7.16.0"
+
+"@babel/helper-split-export-declaration@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b"
+ integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==
+ dependencies:
+ "@babel/types" "^7.16.7"
+
+"@babel/helper-validator-identifier@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad"
+ integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==
+
+"@babel/helper-validator-option@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23"
+ integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==
+
+"@babel/helper-wrap-function@^7.16.8":
+ version "7.16.8"
+ resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz#58afda087c4cd235de92f7ceedebca2c41274200"
+ integrity sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==
+ dependencies:
+ "@babel/helper-function-name" "^7.16.7"
+ "@babel/template" "^7.16.7"
+ "@babel/traverse" "^7.16.8"
+ "@babel/types" "^7.16.8"
+
+"@babel/helpers@^7.18.2":
+ version "7.18.2"
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.2.tgz#970d74f0deadc3f5a938bfa250738eb4ac889384"
+ integrity sha512-j+d+u5xT5utcQSzrh9p+PaJX94h++KN+ng9b9WEJq7pkUPAd61FGqhjuUEdfknb3E/uDBb7ruwEeKkIxNJPIrg==
+ dependencies:
+ "@babel/template" "^7.16.7"
+ "@babel/traverse" "^7.18.2"
+ "@babel/types" "^7.18.2"
+
+"@babel/highlight@^7.16.7":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.17.12.tgz#257de56ee5afbd20451ac0a75686b6b404257351"
+ integrity sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.16.7"
+ chalk "^2.0.0"
+ js-tokens "^4.0.0"
+
+"@babel/parser@^7.16.7", "@babel/parser@^7.18.5":
+ version "7.18.5"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.5.tgz#337062363436a893a2d22faa60be5bb37091c83c"
+ integrity sha512-YZWVaglMiplo7v8f1oMQ5ZPQr0vn7HPeZXxXWsxXJRjGVrzUFn9OxFQl1sb5wzfootjA/yChhW84BV+383FSOw==
+
+"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.17.12.tgz#1dca338caaefca368639c9ffb095afbd4d420b1e"
+ integrity sha512-xCJQXl4EeQ3J9C4yOmpTrtVGmzpm2iSzyxbkZHw7UCnZBftHpF/hpII80uWVyVrc40ytIClHjgWGTG1g/yB+aw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.17.12.tgz#0d498ec8f0374b1e2eb54b9cb2c4c78714c77753"
+ integrity sha512-/vt0hpIw0x4b6BLKUkwlvEoiGZYYLNZ96CzyHYPbtG2jZGz6LBe7/V+drYrc/d+ovrF9NBi0pmtvmNb/FsWtRQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0"
+ "@babel/plugin-proposal-optional-chaining" "^7.17.12"
+
+"@babel/plugin-proposal-async-generator-functions@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.17.12.tgz#094a417e31ce7e692d84bab06c8e2a607cbeef03"
+ integrity sha512-RWVvqD1ooLKP6IqWTA5GyFVX2isGEgC5iFxKzfYOIy/QEFdxYyCybBDtIGjipHpb9bDWHzcqGqFakf+mVmBTdQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/helper-remap-async-to-generator" "^7.16.8"
+ "@babel/plugin-syntax-async-generators" "^7.8.4"
+
+"@babel/plugin-proposal-class-properties@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.17.12.tgz#84f65c0cc247d46f40a6da99aadd6438315d80a4"
+ integrity sha512-U0mI9q8pW5Q9EaTHFPwSVusPMV/DV9Mm8p7csqROFLtIE9rBF5piLqyrBGigftALrBcsBGu4m38JneAe7ZDLXw==
+ dependencies:
+ "@babel/helper-create-class-features-plugin" "^7.17.12"
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-proposal-class-static-block@^7.18.0":
+ version "7.18.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.0.tgz#7d02253156e3c3793bdb9f2faac3a1c05f0ba710"
+ integrity sha512-t+8LsRMMDE74c6sV7KShIw13sqbqd58tlqNrsWoWBTIMw7SVQ0cZ905wLNS/FBCy/3PyooRHLFFlfrUNyyz5lA==
+ dependencies:
+ "@babel/helper-create-class-features-plugin" "^7.18.0"
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/plugin-syntax-class-static-block" "^7.14.5"
+
+"@babel/plugin-proposal-dynamic-import@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz#c19c897eaa46b27634a00fee9fb7d829158704b2"
+ integrity sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.16.7"
+ "@babel/plugin-syntax-dynamic-import" "^7.8.3"
+
+"@babel/plugin-proposal-export-namespace-from@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.17.12.tgz#b22864ccd662db9606edb2287ea5fd1709f05378"
+ integrity sha512-j7Ye5EWdwoXOpRmo5QmRyHPsDIe6+u70ZYZrd7uz+ebPYFKfRcLcNu3Ro0vOlJ5zuv8rU7xa+GttNiRzX56snQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/plugin-syntax-export-namespace-from" "^7.8.3"
+
+"@babel/plugin-proposal-json-strings@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.17.12.tgz#f4642951792437233216d8c1af370bb0fbff4664"
+ integrity sha512-rKJ+rKBoXwLnIn7n6o6fulViHMrOThz99ybH+hKHcOZbnN14VuMnH9fo2eHE69C8pO4uX1Q7t2HYYIDmv8VYkg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/plugin-syntax-json-strings" "^7.8.3"
+
+"@babel/plugin-proposal-logical-assignment-operators@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.17.12.tgz#c64a1bcb2b0a6d0ed2ff674fd120f90ee4b88a23"
+ integrity sha512-EqFo2s1Z5yy+JeJu7SFfbIUtToJTVlC61/C7WLKDntSw4Sz6JNAIfL7zQ74VvirxpjB5kz/kIx0gCcb+5OEo2Q==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
+
+"@babel/plugin-proposal-nullish-coalescing-operator@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.17.12.tgz#1e93079bbc2cbc756f6db6a1925157c4a92b94be"
+ integrity sha512-ws/g3FSGVzv+VH86+QvgtuJL/kR67xaEIF2x0iPqdDfYW6ra6JF3lKVBkWynRLcNtIC1oCTfDRVxmm2mKzy+ag==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
+
+"@babel/plugin-proposal-numeric-separator@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz#d6b69f4af63fb38b6ca2558442a7fb191236eba9"
+ integrity sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.16.7"
+ "@babel/plugin-syntax-numeric-separator" "^7.10.4"
+
+"@babel/plugin-proposal-object-rest-spread@^7.18.0":
+ version "7.18.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.0.tgz#79f2390c892ba2a68ec112eb0d895cfbd11155e8"
+ integrity sha512-nbTv371eTrFabDfHLElkn9oyf9VG+VKK6WMzhY2o4eHKaG19BToD9947zzGMO6I/Irstx9d8CwX6njPNIAR/yw==
+ dependencies:
+ "@babel/compat-data" "^7.17.10"
+ "@babel/helper-compilation-targets" "^7.17.10"
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/plugin-syntax-object-rest-spread" "^7.8.3"
+ "@babel/plugin-transform-parameters" "^7.17.12"
+
+"@babel/plugin-proposal-optional-catch-binding@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz#c623a430674ffc4ab732fd0a0ae7722b67cb74cf"
+ integrity sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.16.7"
+ "@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
+
+"@babel/plugin-proposal-optional-chaining@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.17.12.tgz#f96949e9bacace3a9066323a5cf90cfb9de67174"
+ integrity sha512-7wigcOs/Z4YWlK7xxjkvaIw84vGhDv/P1dFGQap0nHkc8gFKY/r+hXc8Qzf5k1gY7CvGIcHqAnOagVKJJ1wVOQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0"
+ "@babel/plugin-syntax-optional-chaining" "^7.8.3"
+
+"@babel/plugin-proposal-private-methods@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.17.12.tgz#c2ca3a80beb7539289938da005ad525a038a819c"
+ integrity sha512-SllXoxo19HmxhDWm3luPz+cPhtoTSKLJE9PXshsfrOzBqs60QP0r8OaJItrPhAj0d7mZMnNF0Y1UUggCDgMz1A==
+ dependencies:
+ "@babel/helper-create-class-features-plugin" "^7.17.12"
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-proposal-private-property-in-object@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.17.12.tgz#b02efb7f106d544667d91ae97405a9fd8c93952d"
+ integrity sha512-/6BtVi57CJfrtDNKfK5b66ydK2J5pXUKBKSPD2G1whamMuEnZWgoOIfO8Vf9F/DoD4izBLD/Au4NMQfruzzykg==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.16.7"
+ "@babel/helper-create-class-features-plugin" "^7.17.12"
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/plugin-syntax-private-property-in-object" "^7.14.5"
+
+"@babel/plugin-proposal-unicode-property-regex@^7.17.12", "@babel/plugin-proposal-unicode-property-regex@^7.4.4":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.17.12.tgz#3dbd7a67bd7f94c8238b394da112d86aaf32ad4d"
+ integrity sha512-Wb9qLjXf3ZazqXA7IvI7ozqRIXIGPtSo+L5coFmEkhTQK18ao4UDDD0zdTGAarmbLj2urpRwrc6893cu5Bfh0A==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.17.12"
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-syntax-async-generators@^7.8.4":
+ version "7.8.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d"
+ integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-class-properties@^7.12.13":
+ version "7.12.13"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10"
+ integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.12.13"
+
+"@babel/plugin-syntax-class-static-block@^7.14.5":
+ version "7.14.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406"
+ integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.14.5"
+
+"@babel/plugin-syntax-dynamic-import@^7.8.0", "@babel/plugin-syntax-dynamic-import@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3"
+ integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-export-namespace-from@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a"
+ integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-syntax-import-assertions@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.17.12.tgz#58096a92b11b2e4e54b24c6a0cc0e5e607abcedd"
+ integrity sha512-n/loy2zkq9ZEM8tEOwON9wTQSTNDTDEz6NujPtJGLU7qObzT1N4c4YZZf8E6ATB2AjNQg/Ib2AIpO03EZaCehw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-syntax-json-strings@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a"
+ integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-logical-assignment-operators@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699"
+ integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9"
+ integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-numeric-separator@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97"
+ integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-syntax-object-rest-spread@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871"
+ integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-optional-catch-binding@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1"
+ integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-optional-chaining@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a"
+ integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-private-property-in-object@^7.14.5":
+ version "7.14.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad"
+ integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.14.5"
+
+"@babel/plugin-syntax-top-level-await@^7.14.5":
+ version "7.14.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c"
+ integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.14.5"
+
+"@babel/plugin-transform-arrow-functions@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.17.12.tgz#dddd783b473b1b1537ef46423e3944ff24898c45"
+ integrity sha512-PHln3CNi/49V+mza4xMwrg+WGYevSF1oaiXaC2EQfdp4HWlSjRsrDXWJiQBKpP7749u6vQ9mcry2uuFOv5CXvA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-async-to-generator@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.17.12.tgz#dbe5511e6b01eee1496c944e35cdfe3f58050832"
+ integrity sha512-J8dbrWIOO3orDzir57NRsjg4uxucvhby0L/KZuGsWDj0g7twWK3g7JhJhOrXtuXiw8MeiSdJ3E0OW9H8LYEzLQ==
+ dependencies:
+ "@babel/helper-module-imports" "^7.16.7"
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/helper-remap-async-to-generator" "^7.16.8"
+
+"@babel/plugin-transform-block-scoped-functions@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620"
+ integrity sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.16.7"
+
+"@babel/plugin-transform-block-scoping@^7.17.12":
+ version "7.18.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.4.tgz#7988627b3e9186a13e4d7735dc9c34a056613fb9"
+ integrity sha512-+Hq10ye+jlvLEogSOtq4mKvtk7qwcUQ1f0Mrueai866C82f844Yom2cttfJdMdqRLTxWpsbfbkIkOIfovyUQXw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-classes@^7.17.12":
+ version "7.18.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.4.tgz#51310b812a090b846c784e47087fa6457baef814"
+ integrity sha512-e42NSG2mlKWgxKUAD9EJJSkZxR67+wZqzNxLSpc51T8tRU5SLFHsPmgYR5yr7sdgX4u+iHA1C5VafJ6AyImV3A==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.16.7"
+ "@babel/helper-environment-visitor" "^7.18.2"
+ "@babel/helper-function-name" "^7.17.9"
+ "@babel/helper-optimise-call-expression" "^7.16.7"
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/helper-replace-supers" "^7.18.2"
+ "@babel/helper-split-export-declaration" "^7.16.7"
+ globals "^11.1.0"
+
+"@babel/plugin-transform-computed-properties@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.17.12.tgz#bca616a83679698f3258e892ed422546e531387f"
+ integrity sha512-a7XINeplB5cQUWMg1E/GI1tFz3LfK021IjV1rj1ypE+R7jHm+pIHmHl25VNkZxtx9uuYp7ThGk8fur1HHG7PgQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-destructuring@^7.18.0":
+ version "7.18.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.0.tgz#dc4f92587e291b4daa78aa20cc2d7a63aa11e858"
+ integrity sha512-Mo69klS79z6KEfrLg/1WkmVnB8javh75HX4pi2btjvlIoasuxilEyjtsQW6XPrubNd7AQy0MMaNIaQE4e7+PQw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-dotall-regex@^7.16.7", "@babel/plugin-transform-dotall-regex@^7.4.4":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz#6b2d67686fab15fb6a7fd4bd895d5982cfc81241"
+ integrity sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.16.7"
+ "@babel/helper-plugin-utils" "^7.16.7"
+
+"@babel/plugin-transform-duplicate-keys@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.17.12.tgz#a09aa709a3310013f8e48e0e23bc7ace0f21477c"
+ integrity sha512-EA5eYFUG6xeerdabina/xIoB95jJ17mAkR8ivx6ZSu9frKShBjpOGZPn511MTDTkiCO+zXnzNczvUM69YSf3Zw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-exponentiation-operator@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz#efa9862ef97e9e9e5f653f6ddc7b665e8536fe9b"
+ integrity sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==
+ dependencies:
+ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7"
+ "@babel/helper-plugin-utils" "^7.16.7"
+
+"@babel/plugin-transform-for-of@^7.18.1":
+ version "7.18.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.1.tgz#ed14b657e162b72afbbb2b4cdad277bf2bb32036"
+ integrity sha512-+TTB5XwvJ5hZbO8xvl2H4XaMDOAK57zF4miuC9qQJgysPNEAZZ9Z69rdF5LJkozGdZrjBIUAIyKUWRMmebI7vg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-function-name@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf"
+ integrity sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==
+ dependencies:
+ "@babel/helper-compilation-targets" "^7.16.7"
+ "@babel/helper-function-name" "^7.16.7"
+ "@babel/helper-plugin-utils" "^7.16.7"
+
+"@babel/plugin-transform-literals@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.17.12.tgz#97131fbc6bbb261487105b4b3edbf9ebf9c830ae"
+ integrity sha512-8iRkvaTjJciWycPIZ9k9duu663FT7VrBdNqNgxnVXEFwOIp55JWcZd23VBRySYbnS3PwQ3rGiabJBBBGj5APmQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-member-expression-literals@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384"
+ integrity sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.16.7"
+
+"@babel/plugin-transform-modules-amd@^7.18.0":
+ version "7.18.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.0.tgz#7ef1002e67e36da3155edc8bf1ac9398064c02ed"
+ integrity sha512-h8FjOlYmdZwl7Xm2Ug4iX2j7Qy63NANI+NQVWQzv6r25fqgg7k2dZl03p95kvqNclglHs4FZ+isv4p1uXMA+QA==
+ dependencies:
+ "@babel/helper-module-transforms" "^7.18.0"
+ "@babel/helper-plugin-utils" "^7.17.12"
+ babel-plugin-dynamic-import-node "^2.3.3"
+
+"@babel/plugin-transform-modules-commonjs@^7.18.2":
+ version "7.18.2"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.2.tgz#1aa8efa2e2a6e818b6a7f2235fceaf09bdb31e9e"
+ integrity sha512-f5A865gFPAJAEE0K7F/+nm5CmAE3y8AWlMBG9unu5j9+tk50UQVK0QS8RNxSp7MJf0wh97uYyLWt3Zvu71zyOQ==
+ dependencies:
+ "@babel/helper-module-transforms" "^7.18.0"
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/helper-simple-access" "^7.18.2"
+ babel-plugin-dynamic-import-node "^2.3.3"
+
+"@babel/plugin-transform-modules-systemjs@^7.18.0":
+ version "7.18.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.5.tgz#87f11c44fbfd3657be000d4897e192d9cb535996"
+ integrity sha512-SEewrhPpcqMF1V7DhnEbhVJLrC+nnYfe1E0piZMZXBpxi9WvZqWGwpsk7JYP7wPWeqaBh4gyKlBhHJu3uz5g4Q==
+ dependencies:
+ "@babel/helper-hoist-variables" "^7.16.7"
+ "@babel/helper-module-transforms" "^7.18.0"
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/helper-validator-identifier" "^7.16.7"
+ babel-plugin-dynamic-import-node "^2.3.3"
+
+"@babel/plugin-transform-modules-umd@^7.18.0":
+ version "7.18.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.0.tgz#56aac64a2c2a1922341129a4597d1fd5c3ff020f"
+ integrity sha512-d/zZ8I3BWli1tmROLxXLc9A6YXvGK8egMxHp+E/rRwMh1Kip0AP77VwZae3snEJ33iiWwvNv2+UIIhfalqhzZA==
+ dependencies:
+ "@babel/helper-module-transforms" "^7.18.0"
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-named-capturing-groups-regex@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.17.12.tgz#9c4a5a5966e0434d515f2675c227fd8cc8606931"
+ integrity sha512-vWoWFM5CKaTeHrdUJ/3SIOTRV+MBVGybOC9mhJkaprGNt5demMymDW24yC74avb915/mIRe3TgNb/d8idvnCRA==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.17.12"
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-new-target@^7.17.12":
+ version "7.18.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.5.tgz#8c228c4a07501dd12c95c5f23d1622131cc23931"
+ integrity sha512-TuRL5uGW4KXU6OsRj+mLp9BM7pO8e7SGNTEokQRRxHFkXYMFiy2jlKSZPFtI/mKORDzciH+hneskcSOp0gU8hg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-object-super@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94"
+ integrity sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.16.7"
+ "@babel/helper-replace-supers" "^7.16.7"
+
+"@babel/plugin-transform-parameters@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.17.12.tgz#eb467cd9586ff5ff115a9880d6fdbd4a846b7766"
+ integrity sha512-6qW4rWo1cyCdq1FkYri7AHpauchbGLXpdwnYsfxFb+KtddHENfsY5JZb35xUwkK5opOLcJ3BNd2l7PhRYGlwIA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-property-literals@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55"
+ integrity sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.16.7"
+
+"@babel/plugin-transform-regenerator@^7.18.0":
+ version "7.18.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.0.tgz#44274d655eb3f1af3f3a574ba819d3f48caf99d5"
+ integrity sha512-C8YdRw9uzx25HSIzwA7EM7YP0FhCe5wNvJbZzjVNHHPGVcDJ3Aie+qGYYdS1oVQgn+B3eAIJbWFLrJ4Jipv7nw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+ regenerator-transform "^0.15.0"
+
+"@babel/plugin-transform-reserved-words@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.17.12.tgz#7dbd349f3cdffba751e817cf40ca1386732f652f"
+ integrity sha512-1KYqwbJV3Co03NIi14uEHW8P50Md6KqFgt0FfpHdK6oyAHQVTosgPuPSiWud1HX0oYJ1hGRRlk0fP87jFpqXZA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-shorthand-properties@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a"
+ integrity sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.16.7"
+
+"@babel/plugin-transform-spread@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.17.12.tgz#c112cad3064299f03ea32afed1d659223935d1f5"
+ integrity sha512-9pgmuQAtFi3lpNUstvG9nGfk9DkrdmWNp9KeKPFmuZCpEnxRzYlS8JgwPjYj+1AWDOSvoGN0H30p1cBOmT/Svg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0"
+
+"@babel/plugin-transform-sticky-regex@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz#c84741d4f4a38072b9a1e2e3fd56d359552e8660"
+ integrity sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.16.7"
+
+"@babel/plugin-transform-template-literals@^7.18.2":
+ version "7.18.2"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.2.tgz#31ed6915721864847c48b656281d0098ea1add28"
+ integrity sha512-/cmuBVw9sZBGZVOMkpAEaVLwm4JmK2GZ1dFKOGGpMzEHWFmyZZ59lUU0PdRr8YNYeQdNzTDwuxP2X2gzydTc9g==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-typeof-symbol@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.17.12.tgz#0f12f57ac35e98b35b4ed34829948d42bd0e6889"
+ integrity sha512-Q8y+Jp7ZdtSPXCThB6zjQ74N3lj0f6TDh1Hnf5B+sYlzQ8i5Pjp8gW0My79iekSpT4WnI06blqP6DT0OmaXXmw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-unicode-escapes@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz#da8717de7b3287a2c6d659750c964f302b31ece3"
+ integrity sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.16.7"
+
+"@babel/plugin-transform-unicode-regex@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz#0f7aa4a501198976e25e82702574c34cfebe9ef2"
+ integrity sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.16.7"
+ "@babel/helper-plugin-utils" "^7.16.7"
+
+"@babel/preset-env@^7.16.0":
+ version "7.18.2"
+ resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.18.2.tgz#f47d3000a098617926e674c945d95a28cb90977a"
+ integrity sha512-PfpdxotV6afmXMU47S08F9ZKIm2bJIQ0YbAAtDfIENX7G1NUAXigLREh69CWDjtgUy7dYn7bsMzkgdtAlmS68Q==
+ dependencies:
+ "@babel/compat-data" "^7.17.10"
+ "@babel/helper-compilation-targets" "^7.18.2"
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/helper-validator-option" "^7.16.7"
+ "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.17.12"
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.17.12"
+ "@babel/plugin-proposal-async-generator-functions" "^7.17.12"
+ "@babel/plugin-proposal-class-properties" "^7.17.12"
+ "@babel/plugin-proposal-class-static-block" "^7.18.0"
+ "@babel/plugin-proposal-dynamic-import" "^7.16.7"
+ "@babel/plugin-proposal-export-namespace-from" "^7.17.12"
+ "@babel/plugin-proposal-json-strings" "^7.17.12"
+ "@babel/plugin-proposal-logical-assignment-operators" "^7.17.12"
+ "@babel/plugin-proposal-nullish-coalescing-operator" "^7.17.12"
+ "@babel/plugin-proposal-numeric-separator" "^7.16.7"
+ "@babel/plugin-proposal-object-rest-spread" "^7.18.0"
+ "@babel/plugin-proposal-optional-catch-binding" "^7.16.7"
+ "@babel/plugin-proposal-optional-chaining" "^7.17.12"
+ "@babel/plugin-proposal-private-methods" "^7.17.12"
+ "@babel/plugin-proposal-private-property-in-object" "^7.17.12"
+ "@babel/plugin-proposal-unicode-property-regex" "^7.17.12"
+ "@babel/plugin-syntax-async-generators" "^7.8.4"
+ "@babel/plugin-syntax-class-properties" "^7.12.13"
+ "@babel/plugin-syntax-class-static-block" "^7.14.5"
+ "@babel/plugin-syntax-dynamic-import" "^7.8.3"
+ "@babel/plugin-syntax-export-namespace-from" "^7.8.3"
+ "@babel/plugin-syntax-import-assertions" "^7.17.12"
+ "@babel/plugin-syntax-json-strings" "^7.8.3"
+ "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
+ "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
+ "@babel/plugin-syntax-numeric-separator" "^7.10.4"
+ "@babel/plugin-syntax-object-rest-spread" "^7.8.3"
+ "@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
+ "@babel/plugin-syntax-optional-chaining" "^7.8.3"
+ "@babel/plugin-syntax-private-property-in-object" "^7.14.5"
+ "@babel/plugin-syntax-top-level-await" "^7.14.5"
+ "@babel/plugin-transform-arrow-functions" "^7.17.12"
+ "@babel/plugin-transform-async-to-generator" "^7.17.12"
+ "@babel/plugin-transform-block-scoped-functions" "^7.16.7"
+ "@babel/plugin-transform-block-scoping" "^7.17.12"
+ "@babel/plugin-transform-classes" "^7.17.12"
+ "@babel/plugin-transform-computed-properties" "^7.17.12"
+ "@babel/plugin-transform-destructuring" "^7.18.0"
+ "@babel/plugin-transform-dotall-regex" "^7.16.7"
+ "@babel/plugin-transform-duplicate-keys" "^7.17.12"
+ "@babel/plugin-transform-exponentiation-operator" "^7.16.7"
+ "@babel/plugin-transform-for-of" "^7.18.1"
+ "@babel/plugin-transform-function-name" "^7.16.7"
+ "@babel/plugin-transform-literals" "^7.17.12"
+ "@babel/plugin-transform-member-expression-literals" "^7.16.7"
+ "@babel/plugin-transform-modules-amd" "^7.18.0"
+ "@babel/plugin-transform-modules-commonjs" "^7.18.2"
+ "@babel/plugin-transform-modules-systemjs" "^7.18.0"
+ "@babel/plugin-transform-modules-umd" "^7.18.0"
+ "@babel/plugin-transform-named-capturing-groups-regex" "^7.17.12"
+ "@babel/plugin-transform-new-target" "^7.17.12"
+ "@babel/plugin-transform-object-super" "^7.16.7"
+ "@babel/plugin-transform-parameters" "^7.17.12"
+ "@babel/plugin-transform-property-literals" "^7.16.7"
+ "@babel/plugin-transform-regenerator" "^7.18.0"
+ "@babel/plugin-transform-reserved-words" "^7.17.12"
+ "@babel/plugin-transform-shorthand-properties" "^7.16.7"
+ "@babel/plugin-transform-spread" "^7.17.12"
+ "@babel/plugin-transform-sticky-regex" "^7.16.7"
+ "@babel/plugin-transform-template-literals" "^7.18.2"
+ "@babel/plugin-transform-typeof-symbol" "^7.17.12"
+ "@babel/plugin-transform-unicode-escapes" "^7.16.7"
+ "@babel/plugin-transform-unicode-regex" "^7.16.7"
+ "@babel/preset-modules" "^0.1.5"
+ "@babel/types" "^7.18.2"
+ babel-plugin-polyfill-corejs2 "^0.3.0"
+ babel-plugin-polyfill-corejs3 "^0.5.0"
+ babel-plugin-polyfill-regenerator "^0.3.0"
+ core-js-compat "^3.22.1"
+ semver "^6.3.0"
+
+"@babel/preset-modules@^0.1.5":
+ version "0.1.5"
+ resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9"
+ integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/plugin-proposal-unicode-property-regex" "^7.4.4"
+ "@babel/plugin-transform-dotall-regex" "^7.4.4"
+ "@babel/types" "^7.4.4"
+ esutils "^2.0.2"
+
+"@babel/runtime@^7.8.4":
+ version "7.18.3"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.3.tgz#c7b654b57f6f63cf7f8b418ac9ca04408c4579f4"
+ integrity sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==
+ dependencies:
+ regenerator-runtime "^0.13.4"
+
+"@babel/template@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155"
+ integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==
+ dependencies:
+ "@babel/code-frame" "^7.16.7"
+ "@babel/parser" "^7.16.7"
+ "@babel/types" "^7.16.7"
+
+"@babel/traverse@^7.13.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.18.0", "@babel/traverse@^7.18.2", "@babel/traverse@^7.18.5":
+ version "7.18.5"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.5.tgz#94a8195ad9642801837988ab77f36e992d9a20cd"
+ integrity sha512-aKXj1KT66sBj0vVzk6rEeAO6Z9aiiQ68wfDgge3nHhA/my6xMM/7HGQUNumKZaoa2qUPQ5whJG9aAifsxUKfLA==
+ dependencies:
+ "@babel/code-frame" "^7.16.7"
+ "@babel/generator" "^7.18.2"
+ "@babel/helper-environment-visitor" "^7.18.2"
+ "@babel/helper-function-name" "^7.17.9"
+ "@babel/helper-hoist-variables" "^7.16.7"
+ "@babel/helper-split-export-declaration" "^7.16.7"
+ "@babel/parser" "^7.18.5"
+ "@babel/types" "^7.18.4"
+ debug "^4.1.0"
+ globals "^11.1.0"
+
+"@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.18.0", "@babel/types@^7.18.2", "@babel/types@^7.18.4", "@babel/types@^7.4.4":
+ version "7.18.4"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.4.tgz#27eae9b9fd18e9dccc3f9d6ad051336f307be354"
+ integrity sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.16.7"
+ to-fast-properties "^2.0.0"
+
+"@discoveryjs/json-ext@^0.5.0":
+ version "0.5.7"
+ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
+ integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==
+
+"@fortawesome/fontawesome-free@^6.1.1":
+ version "6.1.1"
+ resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-6.1.1.tgz#bf5d45611ab74890be386712a0e5d998c65ee2a1"
+ integrity sha512-J/3yg2AIXc9wznaVqpHVX3Wa5jwKovVF0AMYSnbmcXTiL3PpRPfF58pzWucCwEiCJBp+hCNRLWClTomD8SseKg==
+
+"@hotwired/stimulus-webpack-helpers@^1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@hotwired/stimulus-webpack-helpers/-/stimulus-webpack-helpers-1.0.1.tgz#4cd74487adeca576c9865ac2b9fe5cb20cef16dd"
+ integrity sha512-wa/zupVG0eWxRYJjC1IiPBdt3Lruv0RqGN+/DTMmUWUyMAEB27KXmVY6a8YpUVTM7QwVuaLNGW4EqDgrS2upXQ==
+
+"@hotwired/stimulus@^3.0.0":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/@hotwired/stimulus/-/stimulus-3.0.1.tgz#141f15645acaa3b133b7c247cad58ae252ffae85"
+ integrity sha512-oHsJhgY2cip+K2ED7vKUNd2P+BEswVhrCYcJ802DSsblJFv7mPFVk3cQKvm2vHgHeDVdnj7oOKrBbzp1u8D+KA==
+
+"@jridgewell/gen-mapping@^0.1.0":
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996"
+ integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==
+ dependencies:
+ "@jridgewell/set-array" "^1.0.0"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+
+"@jridgewell/gen-mapping@^0.3.0":
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz#cf92a983c83466b8c0ce9124fadeaf09f7c66ea9"
+ integrity sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==
+ dependencies:
+ "@jridgewell/set-array" "^1.0.0"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+ "@jridgewell/trace-mapping" "^0.3.9"
+
+"@jridgewell/resolve-uri@^3.0.3":
+ version "3.0.7"
+ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz#30cd49820a962aff48c8fffc5cd760151fca61fe"
+ integrity sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==
+
+"@jridgewell/set-array@^1.0.0":
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.1.tgz#36a6acc93987adcf0ba50c66908bd0b70de8afea"
+ integrity sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==
+
+"@jridgewell/source-map@^0.3.2":
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb"
+ integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==
+ dependencies:
+ "@jridgewell/gen-mapping" "^0.3.0"
+ "@jridgewell/trace-mapping" "^0.3.9"
+
+"@jridgewell/sourcemap-codec@^1.4.10":
+ version "1.4.13"
+ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c"
+ integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==
+
+"@jridgewell/trace-mapping@^0.3.7", "@jridgewell/trace-mapping@^0.3.9":
+ version "0.3.13"
+ resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz#dcfe3e95f224c8fe97a87a5235defec999aa92ea"
+ integrity sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==
+ dependencies:
+ "@jridgewell/resolve-uri" "^3.0.3"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+
+"@leichtgewicht/ip-codec@^2.0.1":
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b"
+ integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==
+
+"@nuxt/friendly-errors-webpack-plugin@^2.5.1":
+ version "2.5.2"
+ resolved "https://registry.yarnpkg.com/@nuxt/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-2.5.2.tgz#982a43ee2da61611f7396439e57038392d3944d5"
+ integrity sha512-LLc+90lnxVbpKkMqk5z1EWpXoODhc6gRkqqXJCInJwF5xabHAE7biFvbULfvTRmtaTzAaP8IV4HQDLUgeAUTTw==
+ dependencies:
+ chalk "^2.3.2"
+ consola "^2.6.0"
+ error-stack-parser "^2.0.0"
+ string-width "^4.2.3"
+
+"@popperjs/core@^2.11.5":
+ version "2.11.5"
+ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.5.tgz#db5a11bf66bdab39569719555b0f76e138d7bd64"
+ integrity sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==
+
+"@swc/helpers@^0.2.13":
+ version "0.2.14"
+ resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.2.14.tgz#20288c3627442339dd3d743c944f7043ee3590f0"
+ integrity sha512-wpCQMhf5p5GhNg2MmGKXzUNwxe7zRiCsmqYsamez2beP7mKPCSiu+BjZcdN95yYSzO857kr0VfQewmGpS77nqA==
+
+"@symfony/stimulus-bridge@^3.0.0":
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/@symfony/stimulus-bridge/-/stimulus-bridge-3.2.1.tgz#b9c261ad72830fd17898cf27c97862d1cc15b46a"
+ integrity sha512-eawmVu+tLVkiTz7ewkcsxFvaSZKxFWXmdWxIsxr2jIfQ64PSJg7PIcd7GsPMDxX8sLwg3LGxXfJv5bASL1es+A==
+ dependencies:
+ "@hotwired/stimulus-webpack-helpers" "^1.0.1"
+ "@types/webpack-env" "^1.16.4"
+ acorn "^8.0.5"
+ loader-utils "^2.0.0"
+ schema-utils "^3.0.0"
+
+"@symfony/webpack-encore@^2.0.0":
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/@symfony/webpack-encore/-/webpack-encore-2.1.0.tgz#353a1b8bc38022046cbbc3d627c4076aca2e28c3"
+ integrity sha512-Cl4VhFUsiaG+R5vAntnpOqi0eBXyk2ccxR+7DatpJ9eq+qRtnxBD84/+q4jHM9AJ9gMqHzWTtBVcDmSj1rKuPA==
+ dependencies:
+ "@babel/core" "^7.17.0"
+ "@babel/plugin-syntax-dynamic-import" "^7.8.0"
+ "@babel/preset-env" "^7.16.0"
+ "@nuxt/friendly-errors-webpack-plugin" "^2.5.1"
+ assets-webpack-plugin "7.0.*"
+ babel-loader "^8.2.2"
+ chalk "^4.0.0"
+ clean-webpack-plugin "^4.0.0"
+ css-loader "^6.7.0"
+ css-minimizer-webpack-plugin "^3.4.0"
+ fast-levenshtein "^3.0.0"
+ mini-css-extract-plugin "^2.6.0"
+ pkg-up "^3.1.0"
+ pretty-error "^4.0.0"
+ resolve-url-loader "^5.0.0"
+ semver "^7.3.2"
+ style-loader "^3.3.0"
+ sync-rpc "^1.3.6"
+ terser-webpack-plugin "^5.3.0"
+ tmp "^0.2.1"
+ webpack "^5.72"
+ webpack-cli "^4.9.1"
+ webpack-dev-server "^4.8.0"
+ yargs-parser "^21.0.0"
+
+"@trysound/sax@0.2.0":
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
+ integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
+
+"@ttskch/select2-bootstrap4-theme@^1.5.2":
+ version "1.5.2"
+ resolved "https://registry.yarnpkg.com/@ttskch/select2-bootstrap4-theme/-/select2-bootstrap4-theme-1.5.2.tgz#3b4519b349f3e7831c28752a1e9617312a192656"
+ integrity sha512-gAj8qNy/VYwQDBkACm0USM66kxFai8flX83ayRXPNhzZckEgSqIBB9sM74SCM3ssgeX+ZVy4BifTnLis+KpIyg==
+
+"@types/body-parser@*":
+ version "1.19.2"
+ resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0"
+ integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==
+ dependencies:
+ "@types/connect" "*"
+ "@types/node" "*"
+
+"@types/bonjour@^3.5.9":
+ version "3.5.10"
+ resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.10.tgz#0f6aadfe00ea414edc86f5d106357cda9701e275"
+ integrity sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==
+ dependencies:
+ "@types/node" "*"
+
+"@types/connect-history-api-fallback@^1.3.5":
+ version "1.3.5"
+ resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz#d1f7a8a09d0ed5a57aee5ae9c18ab9b803205dae"
+ integrity sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==
+ dependencies:
+ "@types/express-serve-static-core" "*"
+ "@types/node" "*"
+
+"@types/connect@*":
+ version "3.4.35"
+ resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1"
+ integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==
+ dependencies:
+ "@types/node" "*"
+
+"@types/eslint-scope@^3.7.3":
+ version "3.7.3"
+ resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.3.tgz#125b88504b61e3c8bc6f870882003253005c3224"
+ integrity sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==
+ dependencies:
+ "@types/eslint" "*"
+ "@types/estree" "*"
+
+"@types/eslint@*":
+ version "8.4.3"
+ resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.3.tgz#5c92815a3838b1985c90034cd85f26f59d9d0ece"
+ integrity sha512-YP1S7YJRMPs+7KZKDb9G63n8YejIwW9BALq7a5j2+H4yl6iOv9CB29edho+cuFRrvmJbbaH2yiVChKLJVysDGw==
+ dependencies:
+ "@types/estree" "*"
+ "@types/json-schema" "*"
+
+"@types/estree@*", "@types/estree@^0.0.51":
+ version "0.0.51"
+ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40"
+ integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==
+
+"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.18":
+ version "4.17.29"
+ resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.29.tgz#2a1795ea8e9e9c91b4a4bbe475034b20c1ec711c"
+ integrity sha512-uMd++6dMKS32EOuw1Uli3e3BPgdLIXmezcfHv7N4c1s3gkhikBplORPpMq3fuWkxncZN1reb16d5n8yhQ80x7Q==
+ dependencies:
+ "@types/node" "*"
+ "@types/qs" "*"
+ "@types/range-parser" "*"
+
+"@types/express@*", "@types/express@^4.17.13":
+ version "4.17.13"
+ resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034"
+ integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==
+ dependencies:
+ "@types/body-parser" "*"
+ "@types/express-serve-static-core" "^4.17.18"
+ "@types/qs" "*"
+ "@types/serve-static" "*"
+
+"@types/glob@^7.1.1":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb"
+ integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==
+ dependencies:
+ "@types/minimatch" "*"
+ "@types/node" "*"
+
+"@types/http-proxy@^1.17.8":
+ version "1.17.9"
+ resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.9.tgz#7f0e7931343761efde1e2bf48c40f02f3f75705a"
+ integrity sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==
+ dependencies:
+ "@types/node" "*"
+
+"@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
+ version "7.0.11"
+ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
+ integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
+
+"@types/mime@^1":
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
+ integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==
+
+"@types/minimatch@*":
+ version "3.0.5"
+ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40"
+ integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==
+
+"@types/node@*":
+ version "18.0.0"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.0.tgz#67c7b724e1bcdd7a8821ce0d5ee184d3b4dd525a"
+ integrity sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA==
+
+"@types/qs@*":
+ version "6.9.7"
+ resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
+ integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
+
+"@types/range-parser@*":
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc"
+ integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==
+
+"@types/retry@0.12.0":
+ version "0.12.0"
+ resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
+ integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==
+
+"@types/serve-index@^1.9.1":
+ version "1.9.1"
+ resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278"
+ integrity sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==
+ dependencies:
+ "@types/express" "*"
+
+"@types/serve-static@*", "@types/serve-static@^1.13.10":
+ version "1.13.10"
+ resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9"
+ integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==
+ dependencies:
+ "@types/mime" "^1"
+ "@types/node" "*"
+
+"@types/sockjs@^0.3.33":
+ version "0.3.33"
+ resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.33.tgz#570d3a0b99ac995360e3136fd6045113b1bd236f"
+ integrity sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==
+ dependencies:
+ "@types/node" "*"
+
+"@types/webpack-env@^1.16.4":
+ version "1.17.0"
+ resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.17.0.tgz#f99ce359f1bfd87da90cc4a57cab0a18f34a48d0"
+ integrity sha512-eHSaNYEyxRA5IAG0Ym/yCyf86niZUIF/TpWKofQI/CVfh5HsMEUyfE2kwFxha4ow0s5g0LfISQxpDKjbRDrizw==
+
+"@types/ws@^8.5.1":
+ version "8.5.3"
+ resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d"
+ integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==
+ dependencies:
+ "@types/node" "*"
+
+"@webassemblyjs/ast@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7"
+ integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==
+ dependencies:
+ "@webassemblyjs/helper-numbers" "1.11.1"
+ "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
+
+"@webassemblyjs/floating-point-hex-parser@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f"
+ integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==
+
+"@webassemblyjs/helper-api-error@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16"
+ integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==
+
+"@webassemblyjs/helper-buffer@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5"
+ integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==
+
+"@webassemblyjs/helper-numbers@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae"
+ integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==
+ dependencies:
+ "@webassemblyjs/floating-point-hex-parser" "1.11.1"
+ "@webassemblyjs/helper-api-error" "1.11.1"
+ "@xtuc/long" "4.2.2"
+
+"@webassemblyjs/helper-wasm-bytecode@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1"
+ integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==
+
+"@webassemblyjs/helper-wasm-section@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a"
+ integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==
+ dependencies:
+ "@webassemblyjs/ast" "1.11.1"
+ "@webassemblyjs/helper-buffer" "1.11.1"
+ "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
+ "@webassemblyjs/wasm-gen" "1.11.1"
+
+"@webassemblyjs/ieee754@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614"
+ integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==
+ dependencies:
+ "@xtuc/ieee754" "^1.2.0"
+
+"@webassemblyjs/leb128@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5"
+ integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==
+ dependencies:
+ "@xtuc/long" "4.2.2"
+
+"@webassemblyjs/utf8@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff"
+ integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==
+
+"@webassemblyjs/wasm-edit@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6"
+ integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==
+ dependencies:
+ "@webassemblyjs/ast" "1.11.1"
+ "@webassemblyjs/helper-buffer" "1.11.1"
+ "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
+ "@webassemblyjs/helper-wasm-section" "1.11.1"
+ "@webassemblyjs/wasm-gen" "1.11.1"
+ "@webassemblyjs/wasm-opt" "1.11.1"
+ "@webassemblyjs/wasm-parser" "1.11.1"
+ "@webassemblyjs/wast-printer" "1.11.1"
+
+"@webassemblyjs/wasm-gen@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76"
+ integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==
+ dependencies:
+ "@webassemblyjs/ast" "1.11.1"
+ "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
+ "@webassemblyjs/ieee754" "1.11.1"
+ "@webassemblyjs/leb128" "1.11.1"
+ "@webassemblyjs/utf8" "1.11.1"
+
+"@webassemblyjs/wasm-opt@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2"
+ integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==
+ dependencies:
+ "@webassemblyjs/ast" "1.11.1"
+ "@webassemblyjs/helper-buffer" "1.11.1"
+ "@webassemblyjs/wasm-gen" "1.11.1"
+ "@webassemblyjs/wasm-parser" "1.11.1"
+
+"@webassemblyjs/wasm-parser@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199"
+ integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==
+ dependencies:
+ "@webassemblyjs/ast" "1.11.1"
+ "@webassemblyjs/helper-api-error" "1.11.1"
+ "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
+ "@webassemblyjs/ieee754" "1.11.1"
+ "@webassemblyjs/leb128" "1.11.1"
+ "@webassemblyjs/utf8" "1.11.1"
+
+"@webassemblyjs/wast-printer@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0"
+ integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==
+ dependencies:
+ "@webassemblyjs/ast" "1.11.1"
+ "@xtuc/long" "4.2.2"
+
+"@webpack-cli/configtest@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5"
+ integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==
+
+"@webpack-cli/info@^1.5.0":
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1"
+ integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==
+ dependencies:
+ envinfo "^7.7.3"
+
+"@webpack-cli/serve@^1.7.0":
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1"
+ integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==
+
+"@xtuc/ieee754@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
+ integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==
+
+"@xtuc/long@4.2.2":
+ version "4.2.2"
+ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d"
+ integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
+
+accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8:
+ version "1.3.8"
+ resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
+ integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
+ dependencies:
+ mime-types "~2.1.34"
+ negotiator "0.6.3"
+
+acorn-import-assertions@^1.7.6:
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9"
+ integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==
+
+acorn@^8.0.5, acorn@^8.4.1, acorn@^8.5.0:
+ version "8.7.1"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30"
+ integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==
+
+adjust-sourcemap-loader@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz#fc4a0fd080f7d10471f30a7320f25560ade28c99"
+ integrity sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==
+ dependencies:
+ loader-utils "^2.0.0"
+ regex-parser "^2.2.11"
+
+ajv-formats@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520"
+ integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==
+ dependencies:
+ ajv "^8.0.0"
+
+ajv-keywords@^3.5.2:
+ version "3.5.2"
+ resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
+ integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
+
+ajv-keywords@^5.0.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16"
+ integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==
+ dependencies:
+ fast-deep-equal "^3.1.3"
+
+ajv@^6.12.4, ajv@^6.12.5:
+ version "6.12.6"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
+ integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
+ dependencies:
+ fast-deep-equal "^3.1.1"
+ fast-json-stable-stringify "^2.0.0"
+ json-schema-traverse "^0.4.1"
+ uri-js "^4.2.2"
+
+ajv@^8.0.0, ajv@^8.8.0:
+ version "8.11.0"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f"
+ integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==
+ dependencies:
+ fast-deep-equal "^3.1.1"
+ json-schema-traverse "^1.0.0"
+ require-from-string "^2.0.2"
+ uri-js "^4.2.2"
+
+ansi-html-community@^0.0.8:
+ version "0.0.8"
+ resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41"
+ integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==
+
+ansi-regex@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+ integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
+ansi-styles@^3.2.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+ integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
+ dependencies:
+ color-convert "^1.9.0"
+
+ansi-styles@^4.1.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+ integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+ dependencies:
+ color-convert "^2.0.1"
+
+anymatch@~3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
+ integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
+ dependencies:
+ normalize-path "^3.0.0"
+ picomatch "^2.0.4"
+
+array-flatten@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
+ integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==
+
+array-flatten@^2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099"
+ integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==
+
+array-union@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
+ integrity sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==
+ dependencies:
+ array-uniq "^1.0.1"
+
+array-uniq@^1.0.1:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
+ integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==
+
+assets-webpack-plugin@7.0.*:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/assets-webpack-plugin/-/assets-webpack-plugin-7.0.0.tgz#c61ed7466f35ff7a4d90d7070948736f471b8804"
+ integrity sha512-DMZ9r6HFxynWeONRMhSOFTvTrmit5dovdoUKdJgCG03M6CC7XiwNImPH+Ad1jaVrQ2n59e05lBhte52xPt4MSA==
+ dependencies:
+ camelcase "^6.0.0"
+ escape-string-regexp "^4.0.0"
+ lodash "^4.17.20"
+
+babel-loader@^8.2.2:
+ version "8.2.5"
+ resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.5.tgz#d45f585e654d5a5d90f5350a779d7647c5ed512e"
+ integrity sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==
+ dependencies:
+ find-cache-dir "^3.3.1"
+ loader-utils "^2.0.0"
+ make-dir "^3.1.0"
+ schema-utils "^2.6.5"
+
+babel-plugin-dynamic-import-node@^2.3.3:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3"
+ integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==
+ dependencies:
+ object.assign "^4.1.0"
+
+babel-plugin-polyfill-corejs2@^0.3.0:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz#440f1b70ccfaabc6b676d196239b138f8a2cfba5"
+ integrity sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==
+ dependencies:
+ "@babel/compat-data" "^7.13.11"
+ "@babel/helper-define-polyfill-provider" "^0.3.1"
+ semver "^6.1.1"
+
+babel-plugin-polyfill-corejs3@^0.5.0:
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz#aabe4b2fa04a6e038b688c5e55d44e78cd3a5f72"
+ integrity sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==
+ dependencies:
+ "@babel/helper-define-polyfill-provider" "^0.3.1"
+ core-js-compat "^3.21.0"
+
+babel-plugin-polyfill-regenerator@^0.3.0:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990"
+ integrity sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==
+ dependencies:
+ "@babel/helper-define-polyfill-provider" "^0.3.1"
+
+balanced-match@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
+ integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
+
+batch@0.6.1:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16"
+ integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==
+
+big.js@^5.2.2:
+ version "5.2.2"
+ resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
+ integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
+
+binary-extensions@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
+ integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
+
+body-parser@1.20.0:
+ version "1.20.0"
+ resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.0.tgz#3de69bd89011c11573d7bfee6a64f11b6bd27cc5"
+ integrity sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==
+ dependencies:
+ bytes "3.1.2"
+ content-type "~1.0.4"
+ debug "2.6.9"
+ depd "2.0.0"
+ destroy "1.2.0"
+ http-errors "2.0.0"
+ iconv-lite "0.4.24"
+ on-finished "2.4.1"
+ qs "6.10.3"
+ raw-body "2.5.1"
+ type-is "~1.6.18"
+ unpipe "1.0.0"
+
+bonjour-service@^1.0.11:
+ version "1.0.13"
+ resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.0.13.tgz#4ac003dc1626023252d58adf2946f57e5da450c1"
+ integrity sha512-LWKRU/7EqDUC9CTAQtuZl5HzBALoCYwtLhffW3et7vZMwv3bWLpJf8bRYlMD5OCcDpTfnPgNCV4yo9ZIaJGMiA==
+ dependencies:
+ array-flatten "^2.1.2"
+ dns-equal "^1.0.0"
+ fast-deep-equal "^3.1.3"
+ multicast-dns "^7.2.5"
+
+boolbase@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
+ integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==
+
+bootstrap@^5.1.3:
+ version "5.1.3"
+ resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.1.3.tgz#ba081b0c130f810fa70900acbc1c6d3c28fa8f34"
+ integrity sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==
+
+brace-expansion@^1.1.7:
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+ integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+ dependencies:
+ balanced-match "^1.0.0"
+ concat-map "0.0.1"
+
+braces@^3.0.2, braces@~3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
+ integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
+ dependencies:
+ fill-range "^7.0.1"
+
+browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.6, browserslist@^4.20.2, browserslist@^4.20.3, browserslist@^4.20.4:
+ version "4.20.4"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.4.tgz#98096c9042af689ee1e0271333dbc564b8ce4477"
+ integrity sha512-ok1d+1WpnU24XYN7oC3QWgTyMhY/avPJ/r9T00xxvUOIparA/gc+UPUMaod3i+G6s+nI2nUb9xZ5k794uIwShw==
+ dependencies:
+ caniuse-lite "^1.0.30001349"
+ electron-to-chromium "^1.4.147"
+ escalade "^3.1.1"
+ node-releases "^2.0.5"
+ picocolors "^1.0.0"
+
+buffer-from@^1.0.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
+ integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
+
+bytes@3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
+ integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==
+
+bytes@3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
+ integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
+
+call-bind@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
+ integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==
+ dependencies:
+ function-bind "^1.1.1"
+ get-intrinsic "^1.0.2"
+
+camelcase@^6.0.0:
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
+ integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
+
+caniuse-api@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0"
+ integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==
+ dependencies:
+ browserslist "^4.0.0"
+ caniuse-lite "^1.0.0"
+ lodash.memoize "^4.1.2"
+ lodash.uniq "^4.5.0"
+
+caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001349:
+ version "1.0.30001356"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001356.tgz#cbf5fe7b33f90962bfbca532212ea478d4ec9de8"
+ integrity sha512-/30854bktMLhxtjieIxsrJBfs2gTM1pel6MXKF3K+RdIVJZcsn2A2QdhsuR4/p9+R204fZw0zCBBhktX8xWuyQ==
+
+chalk@^2.0.0, chalk@^2.3.2:
+ version "2.4.2"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
+ integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
+ dependencies:
+ ansi-styles "^3.2.1"
+ escape-string-regexp "^1.0.5"
+ supports-color "^5.3.0"
+
+chalk@^4.0.0:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+ integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+ dependencies:
+ ansi-styles "^4.1.0"
+ supports-color "^7.1.0"
+
+chokidar@^3.5.3:
+ version "3.5.3"
+ resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
+ integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
+ dependencies:
+ anymatch "~3.1.2"
+ braces "~3.0.2"
+ glob-parent "~5.1.2"
+ is-binary-path "~2.1.0"
+ is-glob "~4.0.1"
+ normalize-path "~3.0.0"
+ readdirp "~3.6.0"
+ optionalDependencies:
+ fsevents "~2.3.2"
+
+chrome-trace-event@^1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac"
+ integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==
+
+ckeditor4@^4.19.0:
+ version "4.19.0"
+ resolved "https://registry.yarnpkg.com/ckeditor4/-/ckeditor4-4.19.0.tgz#eba0af7e3e044e1f14d914ff53a128cd8d09e8a0"
+ integrity sha512-hh+SslqHmqr9xmcUVMgdmWjO4tdxW+Lzd6jfxDzzjf6zwIFIoNSflgCqUvg2wcry4w0dHgdEoxVMdoJK9E7kUw==
+
+clean-webpack-plugin@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/clean-webpack-plugin/-/clean-webpack-plugin-4.0.0.tgz#72947d4403d452f38ed61a9ff0ada8122aacd729"
+ integrity sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w==
+ dependencies:
+ del "^4.1.1"
+
+clone-deep@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
+ integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==
+ dependencies:
+ is-plain-object "^2.0.4"
+ kind-of "^6.0.2"
+ shallow-clone "^3.0.0"
+
+color-convert@^1.9.0:
+ version "1.9.3"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+ integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
+ dependencies:
+ color-name "1.1.3"
+
+color-convert@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+ integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+ dependencies:
+ color-name "~1.1.4"
+
+color-name@1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+ integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
+
+color-name@~1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+ integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+colord@^2.9.1:
+ version "2.9.2"
+ resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.2.tgz#25e2bacbbaa65991422c07ea209e2089428effb1"
+ integrity sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==
+
+colorette@^2.0.10, colorette@^2.0.14:
+ version "2.0.19"
+ resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798"
+ integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==
+
+commander@^2.20.0:
+ version "2.20.3"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
+ integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+
+commander@^7.0.0, commander@^7.2.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
+ integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
+
+commondir@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
+ integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==
+
+compressible@~2.0.16:
+ version "2.0.18"
+ resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
+ integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==
+ dependencies:
+ mime-db ">= 1.43.0 < 2"
+
+compression@^1.7.4:
+ version "1.7.4"
+ resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f"
+ integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==
+ dependencies:
+ accepts "~1.3.5"
+ bytes "3.0.0"
+ compressible "~2.0.16"
+ debug "2.6.9"
+ on-headers "~1.0.2"
+ safe-buffer "5.1.2"
+ vary "~1.1.2"
+
+concat-map@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+ integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
+
+connect-history-api-fallback@^1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc"
+ integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==
+
+consola@^2.6.0:
+ version "2.15.3"
+ resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550"
+ integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==
+
+content-disposition@0.5.4:
+ version "0.5.4"
+ resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
+ integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
+ dependencies:
+ safe-buffer "5.2.1"
+
+content-type@~1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
+ integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
+
+convert-source-map@^1.7.0:
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369"
+ integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==
+ dependencies:
+ safe-buffer "~5.1.1"
+
+cookie-signature@1.0.6:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
+ integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
+
+cookie@0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
+ integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
+
+core-js-compat@^3.21.0, core-js-compat@^3.22.1:
+ version "3.23.1"
+ resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.23.1.tgz#23d44d9f209086e60dabf9130cea7719af6e199b"
+ integrity sha512-KeYrEc8t6FJsKYB2qnDwRHWaC0cJNaqlHfCpMe5q3j/W1nje3moib/txNklddLPCtGb+etcBIyJ8zuMa/LN5/A==
+ dependencies:
+ browserslist "^4.20.4"
+ semver "7.0.0"
+
+core-js@^3.0.0:
+ version "3.23.1"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.23.1.tgz#9f9a9255115f62c512db56d567f636da32ca0b78"
+ integrity sha512-wfMYHWi1WQjpgZNC9kAlN4ut04TM9fUTdi7CqIoTVM7yaiOUQTklOzfb+oWH3r9edQcT3F887swuVmxrV+CC8w==
+
+core-util-is@~1.0.0:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
+ integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
+
+crop-select-js@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/crop-select-js/-/crop-select-js-1.0.1.tgz#e078ec99533b4ac254a866691adb78aa34f6febe"
+ integrity sha512-spYDZnzUXxZvSmtcj1iMP4sR2f0ccYzUFnjd4MdxJNe9vaId0AQ/BQAVAtEaIyxz6BDE1zJbaVOPmy44uIbBDw==
+
+cross-spawn@^7.0.3:
+ version "7.0.3"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
+ integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
+ dependencies:
+ path-key "^3.1.0"
+ shebang-command "^2.0.0"
+ which "^2.0.1"
+
+css-declaration-sorter@^6.3.0:
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.3.0.tgz#72ebd995c8f4532ff0036631f7365cce9759df14"
+ integrity sha512-OGT677UGHJTAVMRhPO+HJ4oKln3wkBTwtDFH0ojbqm+MJm6xuDMHp2nkhh/ThaBqq20IbraBQSWKfSLNHQO9Og==
+
+css-loader@^6.7.0:
+ version "6.7.1"
+ resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.7.1.tgz#e98106f154f6e1baf3fc3bc455cb9981c1d5fd2e"
+ integrity sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==
+ dependencies:
+ icss-utils "^5.1.0"
+ postcss "^8.4.7"
+ postcss-modules-extract-imports "^3.0.0"
+ postcss-modules-local-by-default "^4.0.0"
+ postcss-modules-scope "^3.0.0"
+ postcss-modules-values "^4.0.0"
+ postcss-value-parser "^4.2.0"
+ semver "^7.3.5"
+
+css-minimizer-webpack-plugin@^3.4.0:
+ version "3.4.1"
+ resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz#ab78f781ced9181992fe7b6e4f3422e76429878f"
+ integrity sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==
+ dependencies:
+ cssnano "^5.0.6"
+ jest-worker "^27.0.2"
+ postcss "^8.3.5"
+ schema-utils "^4.0.0"
+ serialize-javascript "^6.0.0"
+ source-map "^0.6.1"
+
+css-select@^4.1.3:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b"
+ integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==
+ dependencies:
+ boolbase "^1.0.0"
+ css-what "^6.0.1"
+ domhandler "^4.3.1"
+ domutils "^2.8.0"
+ nth-check "^2.0.1"
+
+css-tree@^1.1.2, css-tree@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d"
+ integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==
+ dependencies:
+ mdn-data "2.0.14"
+ source-map "^0.6.1"
+
+css-what@^6.0.1:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4"
+ integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==
+
+cssesc@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
+ integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
+
+cssnano-preset-default@^5.2.12:
+ version "5.2.12"
+ resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.2.12.tgz#ebe6596ec7030e62c3eb2b3c09f533c0644a9a97"
+ integrity sha512-OyCBTZi+PXgylz9HAA5kHyoYhfGcYdwFmyaJzWnzxuGRtnMw/kR6ilW9XzlzlRAtB6PLT/r+prYgkef7hngFew==
+ dependencies:
+ css-declaration-sorter "^6.3.0"
+ cssnano-utils "^3.1.0"
+ postcss-calc "^8.2.3"
+ postcss-colormin "^5.3.0"
+ postcss-convert-values "^5.1.2"
+ postcss-discard-comments "^5.1.2"
+ postcss-discard-duplicates "^5.1.0"
+ postcss-discard-empty "^5.1.1"
+ postcss-discard-overridden "^5.1.0"
+ postcss-merge-longhand "^5.1.6"
+ postcss-merge-rules "^5.1.2"
+ postcss-minify-font-values "^5.1.0"
+ postcss-minify-gradients "^5.1.1"
+ postcss-minify-params "^5.1.3"
+ postcss-minify-selectors "^5.2.1"
+ postcss-normalize-charset "^5.1.0"
+ postcss-normalize-display-values "^5.1.0"
+ postcss-normalize-positions "^5.1.1"
+ postcss-normalize-repeat-style "^5.1.1"
+ postcss-normalize-string "^5.1.0"
+ postcss-normalize-timing-functions "^5.1.0"
+ postcss-normalize-unicode "^5.1.0"
+ postcss-normalize-url "^5.1.0"
+ postcss-normalize-whitespace "^5.1.1"
+ postcss-ordered-values "^5.1.3"
+ postcss-reduce-initial "^5.1.0"
+ postcss-reduce-transforms "^5.1.0"
+ postcss-svgo "^5.1.0"
+ postcss-unique-selectors "^5.1.1"
+
+cssnano-utils@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-3.1.0.tgz#95684d08c91511edfc70d2636338ca37ef3a6861"
+ integrity sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==
+
+cssnano@^5.0.6:
+ version "5.1.12"
+ resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.1.12.tgz#bcd0b64d6be8692de79332c501daa7ece969816c"
+ integrity sha512-TgvArbEZu0lk/dvg2ja+B7kYoD7BBCmn3+k58xD0qjrGHsFzXY/wKTo9M5egcUCabPol05e/PVoIu79s2JN4WQ==
+ dependencies:
+ cssnano-preset-default "^5.2.12"
+ lilconfig "^2.0.3"
+ yaml "^1.10.2"
+
+csso@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529"
+ integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==
+ dependencies:
+ css-tree "^1.1.2"
+
+datatables.net-bs4@^1.12.1:
+ version "1.12.1"
+ resolved "https://registry.yarnpkg.com/datatables.net-bs4/-/datatables.net-bs4-1.12.1.tgz#652e0701788109bf7c24170e643fe6f128cc8eca"
+ integrity sha512-LBeC8zUNVYyQT7ytC2lYqyXDn+k2kYpqvijC83oOjlcnEtb/8Tduzgquox5FrNKUJPcUrj9r+h5B0TDBbob/Gg==
+ dependencies:
+ datatables.net ">=1.11.3"
+ jquery ">=1.7"
+
+datatables.net@>=1.11.3:
+ version "1.12.1"
+ resolved "https://registry.yarnpkg.com/datatables.net/-/datatables.net-1.12.1.tgz#3e625e49a3341f605b0efb519fae94e37b278f24"
+ integrity sha512-e6XAMUoV41JdQPS/r9YRfRcmTPcCVvyZbWI+xog1Zg+kjVliMQbEkvWK5XFItmi64Cvwg+IqsZbTUJ1KSY3umA==
+ dependencies:
+ jquery ">=1.7"
+
+debug@2.6.9:
+ version "2.6.9"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+ integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
+ dependencies:
+ ms "2.0.0"
+
+debug@^4.1.0, debug@^4.1.1:
+ version "4.3.4"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
+ integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
+ dependencies:
+ ms "2.1.2"
+
+default-gateway@^6.0.3:
+ version "6.0.3"
+ resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71"
+ integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==
+ dependencies:
+ execa "^5.0.0"
+
+define-lazy-prop@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f"
+ integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==
+
+define-properties@^1.1.3:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1"
+ integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==
+ dependencies:
+ has-property-descriptors "^1.0.0"
+ object-keys "^1.1.1"
+
+del@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4"
+ integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==
+ dependencies:
+ "@types/glob" "^7.1.1"
+ globby "^6.1.0"
+ is-path-cwd "^2.0.0"
+ is-path-in-cwd "^2.0.0"
+ p-map "^2.0.0"
+ pify "^4.0.1"
+ rimraf "^2.6.3"
+
+depd@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
+ integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
+
+depd@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
+ integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==
+
+destroy@1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
+ integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
+
+detect-node@^2.0.4:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"
+ integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==
+
+dns-equal@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d"
+ integrity sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==
+
+dns-packet@^5.2.2:
+ version "5.4.0"
+ resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.4.0.tgz#1f88477cf9f27e78a213fb6d118ae38e759a879b"
+ integrity sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==
+ dependencies:
+ "@leichtgewicht/ip-codec" "^2.0.1"
+
+dom-converter@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768"
+ integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==
+ dependencies:
+ utila "~0.4"
+
+dom-serializer@^1.0.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30"
+ integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==
+ dependencies:
+ domelementtype "^2.0.1"
+ domhandler "^4.2.0"
+ entities "^2.0.0"
+
+domelementtype@^2.0.1, domelementtype@^2.2.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
+ integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
+
+domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c"
+ integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==
+ dependencies:
+ domelementtype "^2.2.0"
+
+domutils@^2.5.2, domutils@^2.8.0:
+ version "2.8.0"
+ resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
+ integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
+ dependencies:
+ dom-serializer "^1.0.1"
+ domelementtype "^2.2.0"
+ domhandler "^4.2.0"
+
+dropzone@^6.0.0-beta.2:
+ version "6.0.0-beta.2"
+ resolved "https://registry.yarnpkg.com/dropzone/-/dropzone-6.0.0-beta.2.tgz#098be8fa84bdc08674cf0b74f4c889e2679083d6"
+ integrity sha512-k44yLuFFhRk53M8zP71FaaNzJYIzr99SKmpbO/oZKNslDjNXQsBTdfLs+iONd0U0L94zzlFzRnFdqbLcs7h9fQ==
+ dependencies:
+ "@swc/helpers" "^0.2.13"
+ just-extend "^5.0.0"
+
+ee-first@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
+ integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
+
+electron-to-chromium@^1.4.147:
+ version "1.4.161"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.161.tgz#49cb5b35385bfee6cc439d0a04fbba7a7a7f08a1"
+ integrity sha512-sTjBRhqh6wFodzZtc5Iu8/R95OkwaPNn7tj/TaDU5nu/5EFiQDtADGAXdR4tJcTEHlYfJpHqigzJqHvPgehP8A==
+
+emoji-regex@^8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
+ integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
+
+emojis-list@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
+ integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
+
+encodeurl@~1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
+ integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
+
+enhanced-resolve@^5.9.3:
+ version "5.9.3"
+ resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz#44a342c012cbc473254af5cc6ae20ebd0aae5d88"
+ integrity sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow==
+ dependencies:
+ graceful-fs "^4.2.4"
+ tapable "^2.2.0"
+
+entities@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
+ integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
+
+envinfo@^7.7.3:
+ version "7.8.1"
+ resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475"
+ integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==
+
+error-stack-parser@^2.0.0:
+ version "2.1.4"
+ resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286"
+ integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==
+ dependencies:
+ stackframe "^1.3.4"
+
+es-module-lexer@^0.9.0:
+ version "0.9.3"
+ resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19"
+ integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==
+
+escalade@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
+ integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
+
+escape-html@~1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
+ integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
+
+escape-string-regexp@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+ integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
+
+escape-string-regexp@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
+ integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
+
+eslint-scope@5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
+ integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
+ dependencies:
+ esrecurse "^4.3.0"
+ estraverse "^4.1.1"
+
+esrecurse@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
+ integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
+ dependencies:
+ estraverse "^5.2.0"
+
+estraverse@^4.1.1:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
+ integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
+
+estraverse@^5.2.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
+ integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
+
+esutils@^2.0.2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
+ integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+
+etag@~1.8.1:
+ version "1.8.1"
+ resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
+ integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
+
+eventemitter3@^4.0.0:
+ version "4.0.7"
+ resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
+ integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
+
+events@^3.2.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
+ integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
+
+execa@^5.0.0:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
+ integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==
+ dependencies:
+ cross-spawn "^7.0.3"
+ get-stream "^6.0.0"
+ human-signals "^2.1.0"
+ is-stream "^2.0.0"
+ merge-stream "^2.0.0"
+ npm-run-path "^4.0.1"
+ onetime "^5.1.2"
+ signal-exit "^3.0.3"
+ strip-final-newline "^2.0.0"
+
+express@^4.17.3:
+ version "4.18.1"
+ resolved "https://registry.yarnpkg.com/express/-/express-4.18.1.tgz#7797de8b9c72c857b9cd0e14a5eea80666267caf"
+ integrity sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==
+ dependencies:
+ accepts "~1.3.8"
+ array-flatten "1.1.1"
+ body-parser "1.20.0"
+ content-disposition "0.5.4"
+ content-type "~1.0.4"
+ cookie "0.5.0"
+ cookie-signature "1.0.6"
+ debug "2.6.9"
+ depd "2.0.0"
+ encodeurl "~1.0.2"
+ escape-html "~1.0.3"
+ etag "~1.8.1"
+ finalhandler "1.2.0"
+ fresh "0.5.2"
+ http-errors "2.0.0"
+ merge-descriptors "1.0.1"
+ methods "~1.1.2"
+ on-finished "2.4.1"
+ parseurl "~1.3.3"
+ path-to-regexp "0.1.7"
+ proxy-addr "~2.0.7"
+ qs "6.10.3"
+ range-parser "~1.2.1"
+ safe-buffer "5.2.1"
+ send "0.18.0"
+ serve-static "1.15.0"
+ setprototypeof "1.2.0"
+ statuses "2.0.1"
+ type-is "~1.6.18"
+ utils-merge "1.0.1"
+ vary "~1.1.2"
+
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+ integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
+fast-json-stable-stringify@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
+ integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+
+fast-levenshtein@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz#37b899ae47e1090e40e3fd2318e4d5f0142ca912"
+ integrity sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ==
+ dependencies:
+ fastest-levenshtein "^1.0.7"
+
+fastest-levenshtein@^1.0.12, fastest-levenshtein@^1.0.7:
+ version "1.0.12"
+ resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2"
+ integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==
+
+faye-websocket@^0.11.3:
+ version "0.11.4"
+ resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da"
+ integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==
+ dependencies:
+ websocket-driver ">=0.5.1"
+
+file-loader@^6.2.0:
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d"
+ integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==
+ dependencies:
+ loader-utils "^2.0.0"
+ schema-utils "^3.0.0"
+
+fill-range@^7.0.1:
+ version "7.0.1"
+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
+ integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
+ dependencies:
+ to-regex-range "^5.0.1"
+
+finalhandler@1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32"
+ integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==
+ dependencies:
+ debug "2.6.9"
+ encodeurl "~1.0.2"
+ escape-html "~1.0.3"
+ on-finished "2.4.1"
+ parseurl "~1.3.3"
+ statuses "2.0.1"
+ unpipe "~1.0.0"
+
+find-cache-dir@^3.3.1:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b"
+ integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==
+ dependencies:
+ commondir "^1.0.1"
+ make-dir "^3.0.2"
+ pkg-dir "^4.1.0"
+
+find-up@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
+ integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
+ dependencies:
+ locate-path "^3.0.0"
+
+find-up@^4.0.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
+ integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
+ dependencies:
+ locate-path "^5.0.0"
+ path-exists "^4.0.0"
+
+follow-redirects@^1.0.0:
+ version "1.15.1"
+ resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5"
+ integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==
+
+forwarded@0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
+ integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
+
+fresh@0.5.2:
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
+ integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
+
+fs-monkey@1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3"
+ integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==
+
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+ integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
+
+fsevents@~2.3.2:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
+ integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
+
+function-bind@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
+ integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
+
+gensync@^1.0.0-beta.2:
+ version "1.0.0-beta.2"
+ resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
+ integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
+
+get-intrinsic@^1.0.2, get-intrinsic@^1.1.1:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598"
+ integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==
+ dependencies:
+ function-bind "^1.1.1"
+ has "^1.0.3"
+ has-symbols "^1.0.3"
+
+get-port@^3.1.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc"
+ integrity sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==
+
+get-stream@^6.0.0:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
+ integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
+
+glob-parent@~5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
+ integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
+ dependencies:
+ is-glob "^4.0.1"
+
+glob-to-regexp@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
+ integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
+
+glob@^7.0.3, glob@^7.1.3:
+ version "7.2.3"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
+ integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.1.1"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+globals@^11.1.0:
+ version "11.12.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
+ integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
+
+globby@^6.1.0:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c"
+ integrity sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==
+ dependencies:
+ array-union "^1.0.1"
+ glob "^7.0.3"
+ object-assign "^4.0.1"
+ pify "^2.0.0"
+ pinkie-promise "^2.0.0"
+
+graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9:
+ version "4.2.10"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
+ integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
+
+growly@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
+ integrity sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==
+
+handle-thing@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e"
+ integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==
+
+has-flag@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+ integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==
+
+has-flag@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+ integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+has-property-descriptors@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861"
+ integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==
+ dependencies:
+ get-intrinsic "^1.1.1"
+
+has-symbols@^1.0.1, has-symbols@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
+ integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
+
+has@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
+ integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
+ dependencies:
+ function-bind "^1.1.1"
+
+hpack.js@^2.1.6:
+ version "2.1.6"
+ resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2"
+ integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==
+ dependencies:
+ inherits "^2.0.1"
+ obuf "^1.0.0"
+ readable-stream "^2.0.1"
+ wbuf "^1.1.0"
+
+html-entities@^2.3.2:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.3.tgz#117d7626bece327fc8baace8868fa6f5ef856e46"
+ integrity sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==
+
+htmlparser2@^6.1.0:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7"
+ integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==
+ dependencies:
+ domelementtype "^2.0.1"
+ domhandler "^4.0.0"
+ domutils "^2.5.2"
+ entities "^2.0.0"
+
+http-deceiver@^1.2.7:
+ version "1.2.7"
+ resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
+ integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==
+
+http-errors@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
+ integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
+ dependencies:
+ depd "2.0.0"
+ inherits "2.0.4"
+ setprototypeof "1.2.0"
+ statuses "2.0.1"
+ toidentifier "1.0.1"
+
+http-errors@~1.6.2:
+ version "1.6.3"
+ resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
+ integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==
+ dependencies:
+ depd "~1.1.2"
+ inherits "2.0.3"
+ setprototypeof "1.1.0"
+ statuses ">= 1.4.0 < 2"
+
+http-parser-js@>=0.5.1:
+ version "0.5.6"
+ resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.6.tgz#2e02406ab2df8af8a7abfba62e0da01c62b95afd"
+ integrity sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==
+
+http-proxy-middleware@^2.0.3:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f"
+ integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==
+ dependencies:
+ "@types/http-proxy" "^1.17.8"
+ http-proxy "^1.18.1"
+ is-glob "^4.0.1"
+ is-plain-obj "^3.0.0"
+ micromatch "^4.0.2"
+
+http-proxy@^1.18.1:
+ version "1.18.1"
+ resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"
+ integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==
+ dependencies:
+ eventemitter3 "^4.0.0"
+ follow-redirects "^1.0.0"
+ requires-port "^1.0.0"
+
+human-signals@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
+ integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
+
+iconv-lite@0.4.24:
+ version "0.4.24"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
+ integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
+ dependencies:
+ safer-buffer ">= 2.1.2 < 3"
+
+icss-utils@^5.0.0, icss-utils@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae"
+ integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==
+
+import-local@^3.0.2:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4"
+ integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==
+ dependencies:
+ pkg-dir "^4.2.0"
+ resolve-cwd "^3.0.0"
+
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+ integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+inherits@2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
+ integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==
+
+interpret@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9"
+ integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==
+
+ipaddr.js@1.9.1:
+ version "1.9.1"
+ resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
+ integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
+
+ipaddr.js@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0"
+ integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==
+
+is-binary-path@~2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
+ integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
+ dependencies:
+ binary-extensions "^2.0.0"
+
+is-core-module@^2.9.0:
+ version "2.9.0"
+ resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69"
+ integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==
+ dependencies:
+ has "^1.0.3"
+
+is-docker@^2.0.0, is-docker@^2.1.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa"
+ integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==
+
+is-extglob@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+ integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
+
+is-fullwidth-code-point@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
+ integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
+
+is-glob@^4.0.1, is-glob@~4.0.1:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+ integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+ dependencies:
+ is-extglob "^2.1.1"
+
+is-number@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
+ integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+
+is-path-cwd@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb"
+ integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==
+
+is-path-in-cwd@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb"
+ integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==
+ dependencies:
+ is-path-inside "^2.1.0"
+
+is-path-inside@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2"
+ integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==
+ dependencies:
+ path-is-inside "^1.0.2"
+
+is-plain-obj@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7"
+ integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==
+
+is-plain-object@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
+ integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
+ dependencies:
+ isobject "^3.0.1"
+
+is-stream@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
+ integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
+
+is-wsl@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271"
+ integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==
+ dependencies:
+ is-docker "^2.0.0"
+
+isarray@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+ integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==
+
+isexe@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+ integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
+
+isobject@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
+ integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==
+
+jest-worker@^27.0.2, jest-worker@^27.4.5:
+ version "27.5.1"
+ resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0"
+ integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==
+ dependencies:
+ "@types/node" "*"
+ merge-stream "^2.0.0"
+ supports-color "^8.0.0"
+
+jquery-ui@^1.13.1:
+ version "1.13.1"
+ resolved "https://registry.yarnpkg.com/jquery-ui/-/jquery-ui-1.13.1.tgz#d0b7a42e73a04c31bb5706adf86f6f8942f64eaa"
+ integrity sha512-2VlU59N5P4HaumDK1Z3XEVjSvegFbEOQRgpHUBaB2Ak98Axl3hFhJ6RFcNQNuk9SfL6WxIbuLst8dW/U56NSiA==
+ dependencies:
+ jquery ">=1.8.0 <4.0.0"
+
+jquery@>=1.7, "jquery@>=1.8.0 <4.0.0", jquery@^3.6.0:
+ version "3.6.0"
+ resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.0.tgz#c72a09f15c1bdce142f49dbf1170bdf8adac2470"
+ integrity sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==
+
+js-tokens@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+ integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+jsesc@^2.5.1:
+ version "2.5.2"
+ resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
+ integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
+
+jsesc@~0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
+ integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==
+
+json-parse-even-better-errors@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
+ integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
+
+json-schema-traverse@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+ integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-schema-traverse@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
+ integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
+
+json5@^2.1.2, json5@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c"
+ integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==
+
+just-extend@^5.0.0:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-5.1.1.tgz#4f33b1fc719964f816df55acc905776694b713ab"
+ integrity sha512-b+z6yF1d4EOyDgylzQo5IminlUmzSeqR1hs/bzjBNjuGras4FXq/6TrzjxfN0j+TmI0ltJzTNlqXUMCniciwKQ==
+
+kind-of@^6.0.2:
+ version "6.0.3"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
+ integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
+
+lilconfig@^2.0.3:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.5.tgz#19e57fd06ccc3848fd1891655b5a447092225b25"
+ integrity sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==
+
+loader-runner@^4.2.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1"
+ integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==
+
+loader-utils@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129"
+ integrity sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==
+ dependencies:
+ big.js "^5.2.2"
+ emojis-list "^3.0.0"
+ json5 "^2.1.2"
+
+locate-path@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
+ integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==
+ dependencies:
+ p-locate "^3.0.0"
+ path-exists "^3.0.0"
+
+locate-path@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
+ integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
+ dependencies:
+ p-locate "^4.1.0"
+
+lodash.debounce@^4.0.8:
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
+ integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
+
+lodash.memoize@^4.1.2:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
+ integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==
+
+lodash.uniq@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
+ integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==
+
+lodash@^4.17.20, lodash@^4.17.21:
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+ integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
+
+lru-cache@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
+ integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
+ dependencies:
+ yallist "^4.0.0"
+
+make-dir@^3.0.2, make-dir@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
+ integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
+ dependencies:
+ semver "^6.0.0"
+
+mdn-data@2.0.14:
+ version "2.0.14"
+ resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
+ integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
+
+media-typer@0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
+ integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
+
+memfs@^3.4.3:
+ version "3.4.4"
+ resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.4.4.tgz#e8973cd8060548916adcca58a248e7805c715e89"
+ integrity sha512-W4gHNUE++1oSJVn8Y68jPXi+mkx3fXR5ITE/Ubz6EQ3xRpCN5k2CQ4AUR8094Z7211F876TyoBACGsIveqgiGA==
+ dependencies:
+ fs-monkey "1.0.3"
+
+merge-descriptors@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
+ integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
+
+merge-stream@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
+ integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
+
+methods@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
+ integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
+
+micromatch@^4.0.2:
+ version "4.0.5"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
+ integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
+ dependencies:
+ braces "^3.0.2"
+ picomatch "^2.3.1"
+
+mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
+ version "1.52.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
+ integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
+
+mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34:
+ version "2.1.35"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
+ integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
+ dependencies:
+ mime-db "1.52.0"
+
+mime@1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
+ integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
+
+mimic-fn@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
+ integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
+
+mini-css-extract-plugin@^2.6.0:
+ version "2.6.1"
+ resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz#9a1251d15f2035c342d99a468ab9da7a0451b71e"
+ integrity sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==
+ dependencies:
+ schema-utils "^4.0.0"
+
+minimalistic-assert@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
+ integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
+
+minimatch@^3.1.1:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
+ integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
+ dependencies:
+ brace-expansion "^1.1.7"
+
+ms@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+ integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
+
+ms@2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
+ integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+
+ms@2.1.3:
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
+ integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
+multicast-dns@^7.2.5:
+ version "7.2.5"
+ resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced"
+ integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==
+ dependencies:
+ dns-packet "^5.2.2"
+ thunky "^1.0.2"
+
+nanoid@^3.3.4:
+ version "3.3.4"
+ resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
+ integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
+
+negotiator@0.6.3:
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
+ integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
+
+neo-async@^2.6.2:
+ version "2.6.2"
+ resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
+ integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
+
+node-forge@^1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3"
+ integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==
+
+node-notifier@^9.0.0:
+ version "9.0.1"
+ resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-9.0.1.tgz#cea837f4c5e733936c7b9005e6545cea825d1af4"
+ integrity sha512-fPNFIp2hF/Dq7qLDzSg4vZ0J4e9v60gJR+Qx7RbjbWqzPDdEqeVpEx5CFeDAELIl+A/woaaNn1fQ5nEVerMxJg==
+ dependencies:
+ growly "^1.3.0"
+ is-wsl "^2.2.0"
+ semver "^7.3.2"
+ shellwords "^0.1.1"
+ uuid "^8.3.0"
+ which "^2.0.2"
+
+node-releases@^2.0.5:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.5.tgz#280ed5bc3eba0d96ce44897d8aee478bfb3d9666"
+ integrity sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q==
+
+normalize-path@^3.0.0, normalize-path@~3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
+ integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
+
+normalize-url@^6.0.1:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
+ integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
+
+npm-run-path@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
+ integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
+ dependencies:
+ path-key "^3.0.0"
+
+nth-check@^2.0.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
+ integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==
+ dependencies:
+ boolbase "^1.0.0"
+
+object-assign@^4.0.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+ integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
+
+object-inspect@^1.9.0:
+ version "1.12.2"
+ resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea"
+ integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==
+
+object-keys@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
+ integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
+
+object.assign@^4.1.0:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940"
+ integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==
+ dependencies:
+ call-bind "^1.0.0"
+ define-properties "^1.1.3"
+ has-symbols "^1.0.1"
+ object-keys "^1.1.1"
+
+obuf@^1.0.0, obuf@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e"
+ integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==
+
+on-finished@2.4.1:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
+ integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==
+ dependencies:
+ ee-first "1.1.1"
+
+on-headers@~1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
+ integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
+
+once@^1.3.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
+ dependencies:
+ wrappy "1"
+
+onetime@^5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
+ integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
+ dependencies:
+ mimic-fn "^2.1.0"
+
+open@^8.0.9:
+ version "8.4.0"
+ resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8"
+ integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==
+ dependencies:
+ define-lazy-prop "^2.0.0"
+ is-docker "^2.1.1"
+ is-wsl "^2.2.0"
+
+p-limit@^2.0.0, p-limit@^2.2.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
+ integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
+ dependencies:
+ p-try "^2.0.0"
+
+p-locate@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
+ integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
+ dependencies:
+ p-limit "^2.0.0"
+
+p-locate@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
+ integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
+ dependencies:
+ p-limit "^2.2.0"
+
+p-map@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175"
+ integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==
+
+p-retry@^4.5.0:
+ version "4.6.2"
+ resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16"
+ integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==
+ dependencies:
+ "@types/retry" "0.12.0"
+ retry "^0.13.1"
+
+p-try@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
+ integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
+
+parseurl@~1.3.2, parseurl@~1.3.3:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
+ integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
+
+path-exists@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
+ integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==
+
+path-exists@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
+ integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
+
+path-is-absolute@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+ integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
+
+path-is-inside@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
+ integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==
+
+path-key@^3.0.0, path-key@^3.1.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
+ integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
+
+path-parse@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
+ integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
+
+path-to-regexp@0.1.7:
+ version "0.1.7"
+ resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
+ integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==
+
+picocolors@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
+ integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+
+picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+ integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
+pify@^2.0.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
+ integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==
+
+pify@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
+ integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
+
+pinkie-promise@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
+ integrity sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==
+ dependencies:
+ pinkie "^2.0.0"
+
+pinkie@^2.0.0:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
+ integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==
+
+pkg-dir@^4.1.0, pkg-dir@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
+ integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
+ dependencies:
+ find-up "^4.0.0"
+
+pkg-up@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5"
+ integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==
+ dependencies:
+ find-up "^3.0.0"
+
+postcss-calc@^8.2.3:
+ version "8.2.4"
+ resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-8.2.4.tgz#77b9c29bfcbe8a07ff6693dc87050828889739a5"
+ integrity sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==
+ dependencies:
+ postcss-selector-parser "^6.0.9"
+ postcss-value-parser "^4.2.0"
+
+postcss-colormin@^5.3.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.3.0.tgz#3cee9e5ca62b2c27e84fce63affc0cfb5901956a"
+ integrity sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==
+ dependencies:
+ browserslist "^4.16.6"
+ caniuse-api "^3.0.0"
+ colord "^2.9.1"
+ postcss-value-parser "^4.2.0"
+
+postcss-convert-values@^5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-5.1.2.tgz#31586df4e184c2e8890e8b34a0b9355313f503ab"
+ integrity sha512-c6Hzc4GAv95B7suy4udszX9Zy4ETyMCgFPUDtWjdFTKH1SE9eFY/jEpHSwTH1QPuwxHpWslhckUQWbNRM4ho5g==
+ dependencies:
+ browserslist "^4.20.3"
+ postcss-value-parser "^4.2.0"
+
+postcss-discard-comments@^5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz#8df5e81d2925af2780075840c1526f0660e53696"
+ integrity sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==
+
+postcss-discard-duplicates@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz#9eb4fe8456706a4eebd6d3b7b777d07bad03e848"
+ integrity sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==
+
+postcss-discard-empty@^5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz#e57762343ff7f503fe53fca553d18d7f0c369c6c"
+ integrity sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==
+
+postcss-discard-overridden@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz#7e8c5b53325747e9d90131bb88635282fb4a276e"
+ integrity sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==
+
+postcss-merge-longhand@^5.1.6:
+ version "5.1.6"
+ resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-5.1.6.tgz#f378a8a7e55766b7b644f48e5d8c789ed7ed51ce"
+ integrity sha512-6C/UGF/3T5OE2CEbOuX7iNO63dnvqhGZeUnKkDeifebY0XqkkvrctYSZurpNE902LDf2yKwwPFgotnfSoPhQiw==
+ dependencies:
+ postcss-value-parser "^4.2.0"
+ stylehacks "^5.1.0"
+
+postcss-merge-rules@^5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.1.2.tgz#7049a14d4211045412116d79b751def4484473a5"
+ integrity sha512-zKMUlnw+zYCWoPN6yhPjtcEdlJaMUZ0WyVcxTAmw3lkkN/NDMRkOkiuctQEoWAOvH7twaxUUdvBWl0d4+hifRQ==
+ dependencies:
+ browserslist "^4.16.6"
+ caniuse-api "^3.0.0"
+ cssnano-utils "^3.1.0"
+ postcss-selector-parser "^6.0.5"
+
+postcss-minify-font-values@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz#f1df0014a726083d260d3bd85d7385fb89d1f01b"
+ integrity sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==
+ dependencies:
+ postcss-value-parser "^4.2.0"
+
+postcss-minify-gradients@^5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz#f1fe1b4f498134a5068240c2f25d46fcd236ba2c"
+ integrity sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==
+ dependencies:
+ colord "^2.9.1"
+ cssnano-utils "^3.1.0"
+ postcss-value-parser "^4.2.0"
+
+postcss-minify-params@^5.1.3:
+ version "5.1.3"
+ resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-5.1.3.tgz#ac41a6465be2db735099bbd1798d85079a6dc1f9"
+ integrity sha512-bkzpWcjykkqIujNL+EVEPOlLYi/eZ050oImVtHU7b4lFS82jPnsCb44gvC6pxaNt38Els3jWYDHTjHKf0koTgg==
+ dependencies:
+ browserslist "^4.16.6"
+ cssnano-utils "^3.1.0"
+ postcss-value-parser "^4.2.0"
+
+postcss-minify-selectors@^5.2.1:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz#d4e7e6b46147b8117ea9325a915a801d5fe656c6"
+ integrity sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==
+ dependencies:
+ postcss-selector-parser "^6.0.5"
+
+postcss-modules-extract-imports@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d"
+ integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==
+
+postcss-modules-local-by-default@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c"
+ integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==
+ dependencies:
+ icss-utils "^5.0.0"
+ postcss-selector-parser "^6.0.2"
+ postcss-value-parser "^4.1.0"
+
+postcss-modules-scope@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06"
+ integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==
+ dependencies:
+ postcss-selector-parser "^6.0.4"
+
+postcss-modules-values@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c"
+ integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==
+ dependencies:
+ icss-utils "^5.0.0"
+
+postcss-normalize-charset@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz#9302de0b29094b52c259e9b2cf8dc0879879f0ed"
+ integrity sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==
+
+postcss-normalize-display-values@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz#72abbae58081960e9edd7200fcf21ab8325c3da8"
+ integrity sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==
+ dependencies:
+ postcss-value-parser "^4.2.0"
+
+postcss-normalize-positions@^5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz#ef97279d894087b59325b45c47f1e863daefbb92"
+ integrity sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==
+ dependencies:
+ postcss-value-parser "^4.2.0"
+
+postcss-normalize-repeat-style@^5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz#e9eb96805204f4766df66fd09ed2e13545420fb2"
+ integrity sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==
+ dependencies:
+ postcss-value-parser "^4.2.0"
+
+postcss-normalize-string@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz#411961169e07308c82c1f8c55f3e8a337757e228"
+ integrity sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==
+ dependencies:
+ postcss-value-parser "^4.2.0"
+
+postcss-normalize-timing-functions@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz#d5614410f8f0b2388e9f240aa6011ba6f52dafbb"
+ integrity sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==
+ dependencies:
+ postcss-value-parser "^4.2.0"
+
+postcss-normalize-unicode@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.0.tgz#3d23aede35e160089a285e27bf715de11dc9db75"
+ integrity sha512-J6M3MizAAZ2dOdSjy2caayJLQT8E8K9XjLce8AUQMwOrCvjCHv24aLC/Lps1R1ylOfol5VIDMaM/Lo9NGlk1SQ==
+ dependencies:
+ browserslist "^4.16.6"
+ postcss-value-parser "^4.2.0"
+
+postcss-normalize-url@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz#ed9d88ca82e21abef99f743457d3729a042adcdc"
+ integrity sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==
+ dependencies:
+ normalize-url "^6.0.1"
+ postcss-value-parser "^4.2.0"
+
+postcss-normalize-whitespace@^5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz#08a1a0d1ffa17a7cc6efe1e6c9da969cc4493cfa"
+ integrity sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==
+ dependencies:
+ postcss-value-parser "^4.2.0"
+
+postcss-ordered-values@^5.1.3:
+ version "5.1.3"
+ resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz#b6fd2bd10f937b23d86bc829c69e7732ce76ea38"
+ integrity sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==
+ dependencies:
+ cssnano-utils "^3.1.0"
+ postcss-value-parser "^4.2.0"
+
+postcss-reduce-initial@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.1.0.tgz#fc31659ea6e85c492fb2a7b545370c215822c5d6"
+ integrity sha512-5OgTUviz0aeH6MtBjHfbr57tml13PuedK/Ecg8szzd4XRMbYxH4572JFG067z+FqBIf6Zp/d+0581glkvvWMFw==
+ dependencies:
+ browserslist "^4.16.6"
+ caniuse-api "^3.0.0"
+
+postcss-reduce-transforms@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz#333b70e7758b802f3dd0ddfe98bb1ccfef96b6e9"
+ integrity sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==
+ dependencies:
+ postcss-value-parser "^4.2.0"
+
+postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.9:
+ version "6.0.10"
+ resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d"
+ integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==
+ dependencies:
+ cssesc "^3.0.0"
+ util-deprecate "^1.0.2"
+
+postcss-svgo@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.1.0.tgz#0a317400ced789f233a28826e77523f15857d80d"
+ integrity sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==
+ dependencies:
+ postcss-value-parser "^4.2.0"
+ svgo "^2.7.0"
+
+postcss-unique-selectors@^5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz#a9f273d1eacd09e9aa6088f4b0507b18b1b541b6"
+ integrity sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==
+ dependencies:
+ postcss-selector-parser "^6.0.5"
+
+postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
+ integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
+
+postcss@^8.2.14, postcss@^8.3.5, postcss@^8.4.7:
+ version "8.4.14"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf"
+ integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==
+ dependencies:
+ nanoid "^3.3.4"
+ picocolors "^1.0.0"
+ source-map-js "^1.0.2"
+
+pretty-error@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-4.0.0.tgz#90a703f46dd7234adb46d0f84823e9d1cb8f10d6"
+ integrity sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==
+ dependencies:
+ lodash "^4.17.20"
+ renderkid "^3.0.0"
+
+process-nextick-args@~2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
+ integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
+
+proxy-addr@~2.0.7:
+ version "2.0.7"
+ resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
+ integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
+ dependencies:
+ forwarded "0.2.0"
+ ipaddr.js "1.9.1"
+
+punycode@^2.1.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
+ integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+
+qs@6.10.3:
+ version "6.10.3"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e"
+ integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==
+ dependencies:
+ side-channel "^1.0.4"
+
+randombytes@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
+ integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
+ dependencies:
+ safe-buffer "^5.1.0"
+
+range-parser@^1.2.1, range-parser@~1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
+ integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
+
+raw-body@2.5.1:
+ version "2.5.1"
+ resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857"
+ integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==
+ dependencies:
+ bytes "3.1.2"
+ http-errors "2.0.0"
+ iconv-lite "0.4.24"
+ unpipe "1.0.0"
+
+readable-stream@^2.0.1:
+ version "2.3.7"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
+ integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
+ dependencies:
+ core-util-is "~1.0.0"
+ inherits "~2.0.3"
+ isarray "~1.0.0"
+ process-nextick-args "~2.0.0"
+ safe-buffer "~5.1.1"
+ string_decoder "~1.1.1"
+ util-deprecate "~1.0.1"
+
+readable-stream@^3.0.6:
+ version "3.6.0"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
+ integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
+ dependencies:
+ inherits "^2.0.3"
+ string_decoder "^1.1.1"
+ util-deprecate "^1.0.1"
+
+readdirp@~3.6.0:
+ version "3.6.0"
+ resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
+ integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
+ dependencies:
+ picomatch "^2.2.1"
+
+rechoir@^0.7.0:
+ version "0.7.1"
+ resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686"
+ integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==
+ dependencies:
+ resolve "^1.9.0"
+
+regenerate-unicode-properties@^10.0.1:
+ version "10.0.1"
+ resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56"
+ integrity sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==
+ dependencies:
+ regenerate "^1.4.2"
+
+regenerate@^1.4.2:
+ version "1.4.2"
+ resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a"
+ integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==
+
+regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4:
+ version "0.13.9"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
+ integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
+
+regenerator-transform@^0.15.0:
+ version "0.15.0"
+ resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.0.tgz#cbd9ead5d77fae1a48d957cf889ad0586adb6537"
+ integrity sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==
+ dependencies:
+ "@babel/runtime" "^7.8.4"
+
+regex-parser@^2.2.11:
+ version "2.2.11"
+ resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.11.tgz#3b37ec9049e19479806e878cabe7c1ca83ccfe58"
+ integrity sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==
+
+regexpu-core@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.0.1.tgz#c531122a7840de743dcf9c83e923b5560323ced3"
+ integrity sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==
+ dependencies:
+ regenerate "^1.4.2"
+ regenerate-unicode-properties "^10.0.1"
+ regjsgen "^0.6.0"
+ regjsparser "^0.8.2"
+ unicode-match-property-ecmascript "^2.0.0"
+ unicode-match-property-value-ecmascript "^2.0.0"
+
+regjsgen@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.6.0.tgz#83414c5354afd7d6627b16af5f10f41c4e71808d"
+ integrity sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==
+
+regjsparser@^0.8.2:
+ version "0.8.4"
+ resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.8.4.tgz#8a14285ffcc5de78c5b95d62bbf413b6bc132d5f"
+ integrity sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==
+ dependencies:
+ jsesc "~0.5.0"
+
+renderkid@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-3.0.0.tgz#5fd823e4d6951d37358ecc9a58b1f06836b6268a"
+ integrity sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==
+ dependencies:
+ css-select "^4.1.3"
+ dom-converter "^0.2.0"
+ htmlparser2 "^6.1.0"
+ lodash "^4.17.21"
+ strip-ansi "^6.0.1"
+
+require-from-string@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
+ integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
+
+requires-port@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
+ integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
+
+resolve-cwd@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
+ integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==
+ dependencies:
+ resolve-from "^5.0.0"
+
+resolve-from@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
+ integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
+
+resolve-url-loader@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz#ee3142fb1f1e0d9db9524d539cfa166e9314f795"
+ integrity sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==
+ dependencies:
+ adjust-sourcemap-loader "^4.0.0"
+ convert-source-map "^1.7.0"
+ loader-utils "^2.0.0"
+ postcss "^8.2.14"
+ source-map "0.6.1"
+
+resolve@^1.14.2, resolve@^1.9.0:
+ version "1.22.1"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
+ integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
+ dependencies:
+ is-core-module "^2.9.0"
+ path-parse "^1.0.7"
+ supports-preserve-symlinks-flag "^1.0.0"
+
+retry@^0.13.1:
+ version "0.13.1"
+ resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658"
+ integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==
+
+rimraf@^2.6.3:
+ version "2.7.1"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
+ integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
+ dependencies:
+ glob "^7.1.3"
+
+rimraf@^3.0.0, rimraf@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
+ integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
+ dependencies:
+ glob "^7.1.3"
+
+safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+ integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
+
+safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+ integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
+"safer-buffer@>= 2.1.2 < 3":
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+ integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
+schema-utils@^2.6.5:
+ version "2.7.1"
+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7"
+ integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==
+ dependencies:
+ "@types/json-schema" "^7.0.5"
+ ajv "^6.12.4"
+ ajv-keywords "^3.5.2"
+
+schema-utils@^3.0.0, schema-utils@^3.1.0, schema-utils@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281"
+ integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==
+ dependencies:
+ "@types/json-schema" "^7.0.8"
+ ajv "^6.12.5"
+ ajv-keywords "^3.5.2"
+
+schema-utils@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.0.tgz#60331e9e3ae78ec5d16353c467c34b3a0a1d3df7"
+ integrity sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==
+ dependencies:
+ "@types/json-schema" "^7.0.9"
+ ajv "^8.8.0"
+ ajv-formats "^2.1.1"
+ ajv-keywords "^5.0.0"
+
+select-hose@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
+ integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==
+
+select2@^4.1.0-rc.0:
+ version "4.1.0-rc.0"
+ resolved "https://registry.yarnpkg.com/select2/-/select2-4.1.0-rc.0.tgz#ba3cd3901dda0155e1c0219ab41b74ba51ea22d8"
+ integrity sha512-Hr9TdhyHCZUtwznEH2CBf7967mEM0idtJ5nMtjvk3Up5tPukOLXbHUNmh10oRfeNIhj+3GD3niu+g6sVK+gK0A==
+
+selfsigned@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.0.1.tgz#8b2df7fa56bf014d19b6007655fff209c0ef0a56"
+ integrity sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ==
+ dependencies:
+ node-forge "^1"
+
+semver@7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
+ integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
+
+semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
+ integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
+
+semver@^7.3.2, semver@^7.3.5:
+ version "7.3.7"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
+ integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
+ dependencies:
+ lru-cache "^6.0.0"
+
+send@0.18.0:
+ version "0.18.0"
+ resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be"
+ integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==
+ dependencies:
+ debug "2.6.9"
+ depd "2.0.0"
+ destroy "1.2.0"
+ encodeurl "~1.0.2"
+ escape-html "~1.0.3"
+ etag "~1.8.1"
+ fresh "0.5.2"
+ http-errors "2.0.0"
+ mime "1.6.0"
+ ms "2.1.3"
+ on-finished "2.4.1"
+ range-parser "~1.2.1"
+ statuses "2.0.1"
+
+serialize-javascript@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8"
+ integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==
+ dependencies:
+ randombytes "^2.1.0"
+
+serve-index@^1.9.1:
+ version "1.9.1"
+ resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239"
+ integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==
+ dependencies:
+ accepts "~1.3.4"
+ batch "0.6.1"
+ debug "2.6.9"
+ escape-html "~1.0.3"
+ http-errors "~1.6.2"
+ mime-types "~2.1.17"
+ parseurl "~1.3.2"
+
+serve-static@1.15.0:
+ version "1.15.0"
+ resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540"
+ integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==
+ dependencies:
+ encodeurl "~1.0.2"
+ escape-html "~1.0.3"
+ parseurl "~1.3.3"
+ send "0.18.0"
+
+setprototypeof@1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
+ integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==
+
+setprototypeof@1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
+ integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
+
+shallow-clone@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
+ integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==
+ dependencies:
+ kind-of "^6.0.2"
+
+shebang-command@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
+ integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
+ dependencies:
+ shebang-regex "^3.0.0"
+
+shebang-regex@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
+ integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
+
+shellwords@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
+ integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==
+
+side-channel@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
+ integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==
+ dependencies:
+ call-bind "^1.0.0"
+ get-intrinsic "^1.0.2"
+ object-inspect "^1.9.0"
+
+signal-exit@^3.0.3:
+ version "3.0.7"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
+ integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
+
+sockjs@^0.3.24:
+ version "0.3.24"
+ resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce"
+ integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==
+ dependencies:
+ faye-websocket "^0.11.3"
+ uuid "^8.3.2"
+ websocket-driver "^0.7.4"
+
+source-map-js@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
+ integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
+
+source-map-support@~0.5.20:
+ version "0.5.21"
+ resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
+ integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
+ dependencies:
+ buffer-from "^1.0.0"
+ source-map "^0.6.0"
+
+source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+ integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+spdy-transport@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31"
+ integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==
+ dependencies:
+ debug "^4.1.0"
+ detect-node "^2.0.4"
+ hpack.js "^2.1.6"
+ obuf "^1.1.2"
+ readable-stream "^3.0.6"
+ wbuf "^1.7.3"
+
+spdy@^4.0.2:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b"
+ integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==
+ dependencies:
+ debug "^4.1.0"
+ handle-thing "^2.0.0"
+ http-deceiver "^1.2.7"
+ select-hose "^2.0.0"
+ spdy-transport "^3.0.0"
+
+spectrum-colorpicker2@^2.0.9:
+ version "2.0.9"
+ resolved "https://registry.yarnpkg.com/spectrum-colorpicker2/-/spectrum-colorpicker2-2.0.9.tgz#1fd0a8582b47e14d333189eeb78ee7b108527f0e"
+ integrity sha512-tyNppC56VQHdoKtWdkEkqQfLg9D1hhBea/laO3ffJGrBs3uwFmCxpcBK3a2IOpxZnioykiT+bGlT3SsI7UySRw==
+
+stable@^0.1.8:
+ version "0.1.8"
+ resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
+ integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
+
+stackframe@^1.3.4:
+ version "1.3.4"
+ resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310"
+ integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==
+
+statuses@2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
+ integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
+
+"statuses@>= 1.4.0 < 2":
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
+ integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==
+
+string-width@^4.2.3:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
+
+string_decoder@^1.1.1:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
+ integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
+ dependencies:
+ safe-buffer "~5.2.0"
+
+string_decoder@~1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
+ integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
+ dependencies:
+ safe-buffer "~5.1.0"
+
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
+strip-final-newline@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
+ integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
+
+style-loader@^3.3.0:
+ version "3.3.1"
+ resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.1.tgz#057dfa6b3d4d7c7064462830f9113ed417d38575"
+ integrity sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==
+
+stylehacks@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.1.0.tgz#a40066490ca0caca04e96c6b02153ddc39913520"
+ integrity sha512-SzLmvHQTrIWfSgljkQCw2++C9+Ne91d/6Sp92I8c5uHTcy/PgeHamwITIbBW9wnFTY/3ZfSXR9HIL6Ikqmcu6Q==
+ dependencies:
+ browserslist "^4.16.6"
+ postcss-selector-parser "^6.0.4"
+
+supports-color@^5.3.0:
+ version "5.5.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+ integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
+ dependencies:
+ has-flag "^3.0.0"
+
+supports-color@^7.1.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+ integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+ dependencies:
+ has-flag "^4.0.0"
+
+supports-color@^8.0.0:
+ version "8.1.1"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
+ integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
+ dependencies:
+ has-flag "^4.0.0"
+
+supports-preserve-symlinks-flag@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
+ integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
+
+svgo@^2.7.0:
+ version "2.8.0"
+ resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24"
+ integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==
+ dependencies:
+ "@trysound/sax" "0.2.0"
+ commander "^7.2.0"
+ css-select "^4.1.3"
+ css-tree "^1.1.3"
+ csso "^4.2.0"
+ picocolors "^1.0.0"
+ stable "^0.1.8"
+
+sync-rpc@^1.3.6:
+ version "1.3.6"
+ resolved "https://registry.yarnpkg.com/sync-rpc/-/sync-rpc-1.3.6.tgz#b2e8b2550a12ccbc71df8644810529deb68665a7"
+ integrity sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==
+ dependencies:
+ get-port "^3.1.0"
+
+tapable@^2.1.1, tapable@^2.2.0:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0"
+ integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==
+
+terser-webpack-plugin@^5.1.3, terser-webpack-plugin@^5.3.0:
+ version "5.3.3"
+ resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz#8033db876dd5875487213e87c627bca323e5ed90"
+ integrity sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==
+ dependencies:
+ "@jridgewell/trace-mapping" "^0.3.7"
+ jest-worker "^27.4.5"
+ schema-utils "^3.1.1"
+ serialize-javascript "^6.0.0"
+ terser "^5.7.2"
+
+terser@^5.7.2:
+ version "5.14.1"
+ resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.1.tgz#7c95eec36436cb11cf1902cc79ac564741d19eca"
+ integrity sha512-+ahUAE+iheqBTDxXhTisdA8hgvbEG1hHOQ9xmNjeUJSoi6DU/gMrKNcfZjHkyY6Alnuyc+ikYJaxxfHkT3+WuQ==
+ dependencies:
+ "@jridgewell/source-map" "^0.3.2"
+ acorn "^8.5.0"
+ commander "^2.20.0"
+ source-map-support "~0.5.20"
+
+thunky@^1.0.2:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d"
+ integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==
+
+tmp@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14"
+ integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==
+ dependencies:
+ rimraf "^3.0.0"
+
+to-fast-properties@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
+ integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==
+
+to-regex-range@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
+ integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+ dependencies:
+ is-number "^7.0.0"
+
+toidentifier@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
+ integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
+
+type-is@~1.6.18:
+ version "1.6.18"
+ resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
+ integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
+ dependencies:
+ media-typer "0.3.0"
+ mime-types "~2.1.24"
+
+unicode-canonical-property-names-ecmascript@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc"
+ integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==
+
+unicode-match-property-ecmascript@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3"
+ integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==
+ dependencies:
+ unicode-canonical-property-names-ecmascript "^2.0.0"
+ unicode-property-aliases-ecmascript "^2.0.0"
+
+unicode-match-property-value-ecmascript@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714"
+ integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==
+
+unicode-property-aliases-ecmascript@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8"
+ integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==
+
+unpipe@1.0.0, unpipe@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
+ integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
+
+uri-js@^4.2.2:
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
+ integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
+ dependencies:
+ punycode "^2.1.0"
+
+util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+ integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
+
+utila@~0.4:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c"
+ integrity sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==
+
+utils-merge@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
+ integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
+
+uuid@^8.3.0, uuid@^8.3.2:
+ version "8.3.2"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
+ integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
+
+vary@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
+ integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
+
+watchpack@^2.3.1:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"
+ integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==
+ dependencies:
+ glob-to-regexp "^0.4.1"
+ graceful-fs "^4.1.2"
+
+wbuf@^1.1.0, wbuf@^1.7.3:
+ version "1.7.3"
+ resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df"
+ integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==
+ dependencies:
+ minimalistic-assert "^1.0.0"
+
+webpack-cli@^4.9.1:
+ version "4.10.0"
+ resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.10.0.tgz#37c1d69c8d85214c5a65e589378f53aec64dab31"
+ integrity sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==
+ dependencies:
+ "@discoveryjs/json-ext" "^0.5.0"
+ "@webpack-cli/configtest" "^1.2.0"
+ "@webpack-cli/info" "^1.5.0"
+ "@webpack-cli/serve" "^1.7.0"
+ colorette "^2.0.14"
+ commander "^7.0.0"
+ cross-spawn "^7.0.3"
+ fastest-levenshtein "^1.0.12"
+ import-local "^3.0.2"
+ interpret "^2.2.0"
+ rechoir "^0.7.0"
+ webpack-merge "^5.7.3"
+
+webpack-dev-middleware@^5.3.1:
+ version "5.3.3"
+ resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz#efae67c2793908e7311f1d9b06f2a08dcc97e51f"
+ integrity sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==
+ dependencies:
+ colorette "^2.0.10"
+ memfs "^3.4.3"
+ mime-types "^2.1.31"
+ range-parser "^1.2.1"
+ schema-utils "^4.0.0"
+
+webpack-dev-server@^4.8.0:
+ version "4.9.2"
+ resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.9.2.tgz#c188db28c7bff12f87deda2a5595679ebbc3c9bc"
+ integrity sha512-H95Ns95dP24ZsEzO6G9iT+PNw4Q7ltll1GfJHV4fKphuHWgKFzGHWi4alTlTnpk1SPPk41X+l2RB7rLfIhnB9Q==
+ dependencies:
+ "@types/bonjour" "^3.5.9"
+ "@types/connect-history-api-fallback" "^1.3.5"
+ "@types/express" "^4.17.13"
+ "@types/serve-index" "^1.9.1"
+ "@types/serve-static" "^1.13.10"
+ "@types/sockjs" "^0.3.33"
+ "@types/ws" "^8.5.1"
+ ansi-html-community "^0.0.8"
+ bonjour-service "^1.0.11"
+ chokidar "^3.5.3"
+ colorette "^2.0.10"
+ compression "^1.7.4"
+ connect-history-api-fallback "^1.6.0"
+ default-gateway "^6.0.3"
+ express "^4.17.3"
+ graceful-fs "^4.2.6"
+ html-entities "^2.3.2"
+ http-proxy-middleware "^2.0.3"
+ ipaddr.js "^2.0.1"
+ open "^8.0.9"
+ p-retry "^4.5.0"
+ rimraf "^3.0.2"
+ schema-utils "^4.0.0"
+ selfsigned "^2.0.1"
+ serve-index "^1.9.1"
+ sockjs "^0.3.24"
+ spdy "^4.0.2"
+ webpack-dev-middleware "^5.3.1"
+ ws "^8.4.2"
+
+webpack-merge@^5.7.3:
+ version "5.8.0"
+ resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61"
+ integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==
+ dependencies:
+ clone-deep "^4.0.1"
+ wildcard "^2.0.0"
+
+webpack-notifier@^1.6.0:
+ version "1.15.0"
+ resolved "https://registry.yarnpkg.com/webpack-notifier/-/webpack-notifier-1.15.0.tgz#72644a1a4ec96b3528704d28f79da5e70048e8ee"
+ integrity sha512-N2V8UMgRB5komdXQRavBsRpw0hPhJq2/SWNOGuhrXpIgRhcMexzkGQysUyGStHLV5hkUlgpRiF7IUXoBqyMmzQ==
+ dependencies:
+ node-notifier "^9.0.0"
+ strip-ansi "^6.0.0"
+
+webpack-sources@^3.2.3:
+ version "3.2.3"
+ resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
+ integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
+
+webpack@^5.72:
+ version "5.73.0"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.73.0.tgz#bbd17738f8a53ee5760ea2f59dce7f3431d35d38"
+ integrity sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA==
+ dependencies:
+ "@types/eslint-scope" "^3.7.3"
+ "@types/estree" "^0.0.51"
+ "@webassemblyjs/ast" "1.11.1"
+ "@webassemblyjs/wasm-edit" "1.11.1"
+ "@webassemblyjs/wasm-parser" "1.11.1"
+ acorn "^8.4.1"
+ acorn-import-assertions "^1.7.6"
+ browserslist "^4.14.5"
+ chrome-trace-event "^1.0.2"
+ enhanced-resolve "^5.9.3"
+ es-module-lexer "^0.9.0"
+ eslint-scope "5.1.1"
+ events "^3.2.0"
+ glob-to-regexp "^0.4.1"
+ graceful-fs "^4.2.9"
+ json-parse-even-better-errors "^2.3.1"
+ loader-runner "^4.2.0"
+ mime-types "^2.1.27"
+ neo-async "^2.6.2"
+ schema-utils "^3.1.0"
+ tapable "^2.1.1"
+ terser-webpack-plugin "^5.1.3"
+ watchpack "^2.3.1"
+ webpack-sources "^3.2.3"
+
+websocket-driver@>=0.5.1, websocket-driver@^0.7.4:
+ version "0.7.4"
+ resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760"
+ integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==
+ dependencies:
+ http-parser-js ">=0.5.1"
+ safe-buffer ">=5.1.0"
+ websocket-extensions ">=0.1.1"
+
+websocket-extensions@>=0.1.1:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42"
+ integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==
+
+which@^2.0.1, which@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
+ integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
+ dependencies:
+ isexe "^2.0.0"
+
+wildcard@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec"
+ integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+ integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
+
+ws@^8.4.2:
+ version "8.8.0"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.0.tgz#8e71c75e2f6348dbf8d78005107297056cb77769"
+ integrity sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==
+
+yallist@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
+ integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
+
+yaml@^1.10.2:
+ version "1.10.2"
+ resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
+ integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
+
+yargs-parser@^21.0.0:
+ version "21.0.1"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.1.tgz#0267f286c877a4f0f728fceb6f8a3e4cb95c6e35"
+ integrity sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==