瀏覽代碼

parameters

Vincent GUFFON 3 年之前
父節點
當前提交
117c92ae91

+ 16 - 0
src/ApiResources/Profile/OrganizationProfile.php

@@ -57,6 +57,9 @@ class OrganizationProfile implements ApiResourcesInterface
     #[Groups('access_profile_read')]
     private ?int $currentYear = null;
 
+    #[Groups('access_profile_read')]
+    private ?int $parametersId = null;
+
     public function getId(): ?int
     {
         return $this->id;
@@ -200,4 +203,17 @@ class OrganizationProfile implements ApiResourcesInterface
 
         return $this;
     }
+
+
+    public function getParametersId(): ?int
+    {
+        return $this->parametersId;
+    }
+
+    public function setParametersId(?int $parametersId): self
+    {
+        $this->parametersId = $parametersId;
+
+        return $this;
+    }
 }

+ 0 - 22
src/Doctrine/Access/Extensions/AdminExtension.php

@@ -1,22 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace App\Doctrine\Access\Extensions;
-
-use App\Doctrine\Access\AccessExtensionInterface;
-use Doctrine\ORM\QueryBuilder;
-
-class AdminExtension implements AccessExtensionInterface {
-    public function support(string $name): bool
-    {
-        return $name === 'cget_admin';
-    }
-
-    public function addWhere(QueryBuilder $queryBuilder)
-    {
-        $rootAlias = $queryBuilder->getRootAliases()[0];
-        $queryBuilder
-            ->andWhere(sprintf('%s.adminAccess = :adminAccess', $rootAlias))
-            ->setParameter('adminAccess', true);
-    }
-}

+ 19 - 1
src/Entity/Access/Access.php

@@ -4,6 +4,7 @@ declare(strict_types=1);
 namespace App\Entity\Access;
 
 use ApiPlatform\Core\Annotation\ApiFilter;
+use App\Entity\Organization\Parameters;
 use App\Filter\Person\FullNameFilter;
 use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\BooleanFilter;
 use ApiPlatform\Core\Annotation\ApiResource;
@@ -14,6 +15,7 @@ use App\Entity\Core\Notification;
 use App\Entity\Core\NotificationUser;
 use App\Entity\Organization\Organization;
 use App\Entity\Organization\OrganizationLicence;
+use App\Filter\Utils\InFilter;
 use App\Repository\Access\AccessRepository;
 use App\Entity\Person\Person;
 use App\Entity\Person\PersonActivity;
@@ -31,6 +33,7 @@ use Symfony\Component\Serializer\Annotation\Groups;
 #[ORM\Entity(repositoryClass: AccessRepository::class)]
 #[ApiFilter(BooleanFilter::class, properties: ['person.isPhysical'])]
 #[ApiFilter(FullNameFilter::class)]
+#[ApiFilter(InFilter::class, properties: ['id'])]
 class Access implements UserInterface
 {
     #[ORM\Id]
@@ -55,7 +58,7 @@ class Access implements UserInterface
     #[Groups(["access_people_ref", "access_address"])]
     private Person $person;
 
-    #[ORM\ManyToOne]
+    #[ORM\ManyToOne(inversedBy: 'accesses')]
     #[ORM\JoinColumn(nullable: false)]
     private Organization $organization;
 
@@ -107,6 +110,9 @@ class Access implements UserInterface
     #[ORM\OneToMany(mappedBy: 'access', targetEntity: AccessIntangible::class, cascade: ['persist'], orphanRemoval: true)]
     private Collection $accessIntangibles;
 
+    #[ORM\ManyToOne(inversedBy: 'publicationDirectors')]
+    private ?Parameters $publicationDirector;
+
     #[Pure] public function __construct()
     {
         $this->personActivity = new ArrayCollection();
@@ -500,6 +506,18 @@ class Access implements UserInterface
         return $this;
     }
 
+    public function getPublicationDirector(): ?Parameters
+    {
+        return $this->publicationDirector;
+    }
+
+    public function setPublicationDirector(?Parameters $parameters): self
+    {
+        $this->publicationDirector = $parameters;
+
+        return $this;
+    }
+
     #[Pure] public function getUserIdentifier(): string
     {
         return $this->person->getUsername();

+ 1 - 1
src/Entity/Core/ContactPoint.php

@@ -140,7 +140,7 @@ class ContactPoint
     {
         $this->faxNumber = $faxNumber;
 
-        if(!is_null($this->faxNumber && !is_null($this->getFaxNumberInvalid())))
+        if(!is_null($this->faxNumber) && !is_null($this->getFaxNumberInvalid()))
             $this->setFaxNumberInvalid(null);
 
         return $this;

+ 15 - 0
src/Entity/Core/File.php

@@ -7,6 +7,7 @@ use ApiPlatform\Core\Annotation\ApiResource;
 use App\Entity\Organization\Organization;
 use App\Entity\Person\Person;
 use App\Repository\Core\FileRepository;
+use App\Entity\Organization\Parameters;
 use DateTime;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\Common\Collections\Collection;
@@ -171,6 +172,9 @@ class File
     #[ORM\OneToMany(mappedBy: 'image', targetEntity: Organization::class, orphanRemoval: true)]
     private Collection $organizationImages;
 
+    #[ORM\OneToOne(mappedBy: "qrCode", targetEntity: Parameters::class, fetch: 'EAGER')]
+    private Parameters $qrCode;
+
     #[Pure] public function __construct()
     {
         $this->personImages = new ArrayCollection();
@@ -495,4 +499,15 @@ class File
 
         return $this;
     }
+
+    public function getQrCode(): Parameters
+    {
+        return $this->qrCode;
+    }
+
+    public function setQrCode(Parameters $qrCode): self
+    {
+        $this->qrCode = $qrCode;
+        return $this;
+    }
 }

+ 22 - 1
src/Entity/Organization/Organization.php

@@ -5,10 +5,12 @@ namespace App\Entity\Organization;
 
 use ApiPlatform\Core\Annotation\ApiResource;
 use ApiPlatform\Core\Annotation\ApiSubresource;
+use App\Entity\Access\Access;
 use App\Entity\Billing\BillingSetting;
 use App\Entity\Core\BankAccount;
 use App\Entity\Core\ContactPoint;
 use App\Entity\Core\File;
+use App\Entity\Education\Cycle;
 use App\Entity\Network\NetworkOrganization;
 use App\Repository\Organization\OrganizationRepository;
 use Doctrine\Common\Collections\ArrayCollection;
@@ -55,6 +57,9 @@ class Organization
     #[ORM\OneToOne(mappedBy: 'organization', cascade: ['persist', 'remove'])]
     private Settings $settings;
 
+    #[ORM\OneToMany(mappedBy: 'organization', targetEntity: Access::class, orphanRemoval: true)]
+    private Collection $accesses;
+
     #[ORM\OneToMany(mappedBy: 'organization', targetEntity: NetworkOrganization::class, orphanRemoval: true)]
     #[ApiSubresource]
     private Collection $networkOrganizations;
@@ -62,11 +67,12 @@ class Organization
     #[ORM\OneToMany(mappedBy: 'parent', targetEntity: NetworkOrganization::class, orphanRemoval: true)]
     private Collection $networkOrganizationChildren;
 
-    #[ORM\OneToOne(cascade: ['persist', 'remove'])]
+    #[ORM\OneToOne( inversedBy: 'organization', targetEntity: Parameters::class)]
     #[ORM\JoinColumn(nullable: false)]
     private Parameters $parameters;
 
     #[ORM\OneToOne(mappedBy: 'organization', cascade: ['persist', 'remove'], orphanRemoval: true)]
+    #[ApiSubresource]
     private BillingSetting $billingSetting;
 
     #[ORM\Column(length: 255, nullable: true)]
@@ -207,8 +213,12 @@ class Organization
     #[ApiSubresource]
     private Collection $organizationArticles;
 
+    #[ORM\OneToMany(mappedBy: 'organization', targetEntity: Cycle::class, orphanRemoval: true)]
+    private Collection $cycles;
+
     #[Pure] public function __construct()
     {
+        $this->accesses = new ArrayCollection();
         $this->networkOrganizations = new ArrayCollection();
         $this->networkOrganizationChildren = new ArrayCollection();
         $this->typeOfPractices = new ArrayCollection();
@@ -217,6 +227,7 @@ class Organization
         $this->organizationAddressPostals = new ArrayCollection();
         $this->organizationLicences = new ArrayCollection();
         $this->organizationArticles = new ArrayCollection();
+        $this->cycles = new ArrayCollection();
     }
 
     public function getId(): ?int
@@ -289,6 +300,11 @@ class Organization
         return $this;
     }
 
+    public function getAccesses(): Collection
+    {
+        return $this->accesses;
+    }
+
     public function getNetworkOrganizations(): Collection
     {
         return $this->networkOrganizations;
@@ -954,4 +970,9 @@ class Organization
 
         return $this;
     }
+
+    public function getCycles(): Collection
+    {
+        return $this->cycles;
+    }
 }

+ 157 - 16
src/Entity/Organization/Parameters.php

@@ -4,16 +4,30 @@ declare(strict_types=1);
 namespace App\Entity\Organization;
 
 use ApiPlatform\Core\Annotation\ApiResource;
+use ApiPlatform\Core\Annotation\ApiSubresource;
+use App\Entity\Access\Access;
 use App\Entity\Core\File;
 use App\Repository\Organization\ParametersRepository;
+use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
+use JetBrains\PhpStorm\Pure;
 use Symfony\Component\Validator\Constraints as Assert;
+use App\Validator\Organization\Parameters as OpentalentAssert;
 
 #[ApiResource(
     collectionOperations: [],
-    itemOperations: ['get']
+    itemOperations: [
+        'get' => [
+            'security' => '(is_granted("ROLE_ORGANIZATION_VIEW") or is_granted("ROLE_ORGANIZATION")) and object.getOrganization().getId() == user.getOrganization().getId()'
+        ],
+        'put' => [
+            'security' => 'is_granted("ROLE_ORGANIZATION") and object.getOrganization().getId() == user.getOrganization().getId()'
+        ]
+    ]
 )]
 #[ORM\Entity(repositoryClass: ParametersRepository::class)]
+#[OpentalentAssert\MobytCredentials]
 class Parameters
 {
     #[ORM\Id]
@@ -21,6 +35,9 @@ class Parameters
     #[ORM\GeneratedValue]
     private ?int $id = null;
 
+    #[ORM\OneToOne(mappedBy: 'parameters', targetEntity: Organization::class)]
+    private Organization $organization;
+
     #[ORM\Column(type: 'date', nullable: true)]
     private ?\DateTimeInterface $financialDate = null;
 
@@ -36,29 +53,39 @@ class Parameters
     #[ORM\Column(options: ['default' => false])]
     private bool $trackingValidation = false;
 
+    #[ORM\Column(options: ['default' => 20])]
+    #[Assert\GreaterThanOrEqual(0, message: 'greaterThanOrEqual0')]
+    #[Assert\LessThanOrEqual(100, message: 'lessThanOrEqual100')]
+    private int $average = 20;
+
     #[ORM\Column(options: ['default' => true])]
     private bool $editCriteriaNotationByAdminOnly = true;
 
     #[ORM\Column(length: 255, nullable: true)]
+    #[Assert\Regex('/^[a-zA-Z0-9]+$/i', message: 'smsSenderName_error')]
     private ?string $smsSenderName = null;
 
     #[ORM\Column(options: ['default' => false])]
     private bool $logoDonorsMove = false;
 
-    #[ORM\Column(length: 255, nullable: true)]
+    #[ORM\Column(length: 60, nullable: true)]
     private ?string $subDomain = null;
 
-    #[ORM\Column(length: 255, nullable: true)]
+    #[ORM\Column(length: 100, nullable: true)]
     private ?string $website = null;
 
-    #[ORM\Column(length: 255, nullable: true)]
+    #[ORM\Column(length: 150, nullable: true)]
     private ?string $otherWebsite = null;
 
     #[ORM\Column(options: ['default' => false])]
     private bool $desactivateOpentalentSiteWeb = false;
 
+    #[ORM\OneToMany( mappedBy: 'publicationDirector', targetEntity: Access::class)]
+    #[ApiSubresource]
+    private Collection $publicationDirectors;
+
     #[ORM\Column(length: 255, nullable: true)]
-    #[Assert\Choice(callback: ['\App\Enum\Organization\OpcBulletinPeriodEnumaEnum', 'toArray'], message: 'invalid-bulletin-period')]
+    #[Assert\Choice(callback: ['\App\Enum\Organization\BulletinPeriodEnum', 'toArray'], message: 'invalid-bulletin-period')]
     private ?string $bulletinPeriod = null;
 
     #[ORM\Column(options: ['default' => false])]
@@ -89,12 +116,6 @@ class Parameters
     #[Assert\Choice(callback: ['\App\Enum\Organization\BulletinOutputEnum', 'toArray'], message: 'invalid-bulletin-output')]
     private ?string $bulletinOutput = null;
 
-    #[ORM\Column(length: 255, nullable: true)]
-    private ?string $usernameSMS = null;
-
-    #[ORM\Column(length: 255, nullable: true)]
-    private ?string $passwordSMS = null;
-
     #[ORM\Column(options: ['default' => true])]
     private bool $bulletinEditWithoutEvaluation = true;
 
@@ -102,28 +123,62 @@ class Parameters
     #[Assert\Choice(callback: ['\App\Enum\Organization\SendToBulletinEnum', 'toArray'], message: 'invalid-send-to-bulletin')]
     private ?string $bulletinReceiver = null;
 
+    #[ORM\Column(length: 255, nullable: true)]
+    private ?string $usernameSMS = null;
+
+    #[ORM\Column(length: 255, nullable: true)]
+    private ?string $passwordSMS = null;
+
     #[ORM\Column(options: ['default' => true])]
     private bool $showAdherentList = true;
 
     #[ORM\Column(options: ['default' => false])]
     private bool $studentsAreAdherents = false;
 
+    #[ORM\OneToOne(inversedBy: 'qrCode', targetEntity: File::class, cascade: ['persist'], fetch: 'EAGER')]
+    #[ORM\JoinColumn(referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
+    private ?File $qrCode = null;
+
     #[ORM\Column(length: 255, options: ['default' => 'Europe/Paris'])]
-    #[Assert\Choice(callback: ['\App\Enum\Organization\TimeZoneEnum', 'toArray'], message: 'invalid-timezone')]
+    #[Assert\Choice(callback: ['\App\Enum\Core\TimeZoneEnum', 'toArray'], message: 'invalid-timezone')]
     private ?string $timezone = "Europe/Paris";
 
-    #[ORM\Column(length: 255, nullable: true)]
-    #[Assert\Choice(callback: ['\App\Enum\Organization\PeriodicityEnum', 'toArray'], message: 'invalid-periodicity')]
+    #[ORM\Column(length: 255, nullable: true, options: ['default' => 'ANNUAL'])]
+    #[Assert\Choice(callback: ['\App\Enum\Education\PeriodicityEnum', 'toArray'], message: 'invalid-periodicity')]
     private ?string $educationPeriodicity = null;
 
-    #[ORM\OneToOne(cascade: ['persist', 'remove'])]
-    private File $qrCode;
+    #[ORM\Column(length: 255, nullable: true, options: ['default' => 'BY_EDUCATION'])]
+    #[Assert\Choice(callback: ['\App\Enum\Education\AdvancedEducationNotationTypeEnum', 'toArray'], message: 'invalid-advanced-education-notation-type')]
+    private ?string $advancedEducationNotationType = null;
+
+    #[ORM\Column(options: ['default' => false])]
+    private bool $sendAttendanceEmail = false;
+
+    #[ORM\Column(options: ['default' => false])]
+    private bool $sendAttendanceSms = false;
+
+    #[Pure] public function __construct()
+    {
+        $this->publicationDirectors = new ArrayCollection();
+    }
 
     public function getId(): ?int
     {
         return $this->id;
     }
 
+    public function getOrganization(): Organization
+    {
+        return $this->organization;
+    }
+
+    public function setOrganization(Organization $organization): self
+    {
+        $this->organization = $organization;
+
+        return $this;
+    }
+
     public function getFinancialDate(): ?\DateTimeInterface
     {
         return $this->financialDate;
@@ -184,6 +239,18 @@ class Parameters
         return $this;
     }
 
+    public function getAverage(): int
+    {
+        return $this->average;
+    }
+
+    public function setAverage(int $average)
+    {
+        $this->average = $average;
+
+        return $this;
+    }
+
     public function getEditCriteriaNotationByAdminOnly(): bool
     {
         return $this->editCriteriaNotationByAdminOnly;
@@ -280,6 +347,33 @@ class Parameters
         return $this;
     }
 
+    public function getPublicationDirectors(): Collection
+    {
+        return $this->publicationDirectors;
+    }
+
+    public function addPublicationDirector(Access $access): self
+    {
+        if (!$this->publicationDirectors->contains($access)) {
+            $this->publicationDirectors[] = $access;
+            $access->setPublicationDirector($this);
+        }
+
+        return $this;
+    }
+
+    public function removePublicationDirector(Access $access): self
+    {
+        if ($this->publicationDirectors->removeElement($access)) {
+            // set the owning side to null (unless already changed)
+            if ($access->getPublicationDirector() === $this) {
+                $access->setPublicationDirector(null);
+            }
+        }
+
+        return $this;
+    }
+
     public function getBulletinWithTeacher(): bool
     {
         return $this->bulletinWithTeacher;
@@ -484,6 +578,53 @@ class Parameters
         return $this;
     }
 
+    public function getAdvancedEducationNotationType(): ?string
+    {
+        return $this->advancedEducationNotationType;
+    }
+
+    public function setAdvancedEducationNotationType(?string $advancedEducationNotationType): self
+    {
+        $this->advancedEducationNotationType = $advancedEducationNotationType;
+
+        return $this;
+    }
+
+    public function getQrCode(): ?File
+    {
+        return $this->qrCode;
+    }
+
+    public function setQrCode(?File $qrCode): self
+    {
+        $this->qrCode = $qrCode;
+        return $this;
+    }
+
+    public function getSendAttendanceSms(): bool
+    {
+        return $this->sendAttendanceSms;
+    }
+
+    public function setSendAttendanceSms(bool $sendAttendanceSms): self
+    {
+        $this->sendAttendanceSms = $sendAttendanceSms;
+
+        return $this;
+    }
+
+    public function getSendAttendanceEmail(): bool
+    {
+        return $this->sendAttendanceEmail;
+    }
+
+    public function setSendAttendanceEmail(bool $sendAttendanceEmail): self
+    {
+        $this->sendAttendanceEmail = $sendAttendanceEmail;
+
+        return $this;
+    }
+
     /**
      * @return File
      */

+ 56 - 0
src/Filter/Utils/InFilter.php

@@ -0,0 +1,56 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Filter\Utils;
+
+use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\AbstractContextAwareFilter;
+use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
+use Doctrine\ORM\QueryBuilder;
+use Symfony\Component\PropertyInfo\Type;
+
+final class InFilter extends AbstractContextAwareFilter {
+    protected function filterProperty(string $property, $value, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null)
+    {
+        // otherwise filter is applied to order and page as well
+        if (
+            !$this->isPropertyEnabled($property, $resourceClass) ||
+            !$this->isPropertyMapped($property, $resourceClass)
+        ) {
+            return;
+        }
+
+        $alias = $queryBuilder->getRootAliases()[0];
+        $parameterName = $queryNameGenerator->generateParameterName($property); // Generate a unique parameter name to avoid collisions with other filters
+        $queryBuilder
+            ->andWhere(sprintf('%s.%s IN (:%s)', $alias, $property, $parameterName))
+            ->setParameter($parameterName, explode(',', $value));
+    }
+
+    /**
+     * API docs
+     * @param string $resourceClass
+     * @return array[]
+     */
+    public function getDescription(string $resourceClass): array
+    {
+        if (!$this->properties) {
+            return [];
+        }
+
+        $description = [];
+        foreach ($this->properties as $property => $strategy) {
+            $description["$property"] = [
+                'property' => $property,
+                'type' => Type::BUILTIN_TYPE_STRING,
+                'required' => false,
+                'swagger' => [
+                    'description' => 'Filtre permettant d\'utiliser les IN',
+                    'name' => 'In Filter',
+                    'type' => 'Utils Filter',
+                ],
+            ];
+        }
+
+        return $description;
+    }
+}

+ 4 - 4
src/Repository/Core/ContactPointRepository.php

@@ -26,9 +26,9 @@ class ContactPointRepository extends ServiceEntityRepository
      * Récupération des points de contacts d'une organization et d'un type précis
      * @param String $type
      * @param Organization $organization
-     * @return array|null
+     * @return array
      */
-    public function getByTypeAndOrganization(String $type, Organization $organization): array | null{
+    public function getByTypeAndOrganization(String $type, Organization $organization): array{
         return $this->createQueryBuilder('contact_point')
             ->innerJoin('contact_point.organization', 'organization')
             ->where('contact_point.contactType = :type')
@@ -44,9 +44,9 @@ class ContactPointRepository extends ServiceEntityRepository
      * Récupération des points de contacts d'une person et d'un type précis
      * @param String $type
      * @param Person $person
-     * @return array|null
+     * @return array
      */
-    public function getByTypeAndPerson(String $type, Person $person): array | null{
+    public function getByTypeAndPerson(String $type, Person $person): array {
         return $this->createQueryBuilder('contact_point')
             ->innerJoin('contact_point.person', 'person')
             ->where('contact_point.contactType = :type')

+ 17 - 0
src/Service/Access/Utils.php

@@ -4,6 +4,8 @@ declare(strict_types=1);
 namespace App\Service\Access;
 
 use App\Entity\Access\Access;
+use App\Entity\Organization\Organization;
+use App\Repository\Access\AccessRepository;
 use App\Service\ServiceIterator\OptionalsRolesIterator;
 use App\Test\Service\Access\UtilsTest;
 use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
@@ -16,6 +18,8 @@ class Utils
 {
     public function __construct(
         private RoleHierarchyInterface $roleHierarchy,
+        private HandleOptionalsRoles $handleOptionalsRoles,
+        private AccessRepository $accessRepository,
         private OptionalsRolesIterator $optionalsRolesIterator
     )
     {}
@@ -55,4 +59,17 @@ class Utils
         $roles = $this->optionalsRolesIterator->getOptionalsRoles($access);
         return $this->roleHierarchy->getReachableRoleNames(array_merge($access->getRoles(), $roles));
     }
+
+    /**
+     * Renvoi l'access de l'organization qui est le "super admin"
+     * @param Organization $organization
+     * @return Access|null
+     * @see UtilsTest::testGetAdminAccess()
+     */
+    public function getAdminAccess(Organization $organization): Access|null{
+        return $this->accessRepository->findOneBy([
+            'adminAccess' => true,
+            'organization' => $organization
+        ]) ?? null;
+    }
 }

+ 1 - 0
src/Service/Organization/OrganizationProfileCreator.php

@@ -33,6 +33,7 @@ class OrganizationProfileCreator
         $organizationProfile = $this->createLightOrganizationProfile($organization);
         $organizationProfile->setModules($this->module->getOrganizationModules($organization));
         $organizationProfile->setProduct($organization->getSettings()->getProduct());
+        $organizationProfile->setParametersId($organization->getParameters()->getId());
         $organizationProfile->setLegalStatus($organization->getLegalStatus());
         $organizationProfile->setHasChildren($organization->getNetworkOrganizationChildren()->count() > 1);
         $organizationProfile->setShowAdherentList($organization->getParameters()->getShowAdherentList() && $organization->getPrincipalType() != PrincipalTypeEnum::ARTISTIC_EDUCATION_ONLY());