From d4a7eabc9e6e1aec493b93edcf9a2f71b57820a7 Mon Sep 17 00:00:00 2001 From: Benjamin Pelissier <127949775+BenjaminP17@users.noreply.github.com> Date: Wed, 12 Feb 2025 20:19:15 +0100 Subject: [PATCH 01/13] =?UTF-8?q?modification=20de=20la=20propri=C3=A9t?= =?UTF-8?q?=C3=A9=20(#1629)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sources/AppBundle/Subscriber/SitemapXmlSubscriber.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/AppBundle/Subscriber/SitemapXmlSubscriber.php b/sources/AppBundle/Subscriber/SitemapXmlSubscriber.php index d7fdbdc42..ce1edf902 100644 --- a/sources/AppBundle/Subscriber/SitemapXmlSubscriber.php +++ b/sources/AppBundle/Subscriber/SitemapXmlSubscriber.php @@ -72,7 +72,7 @@ public function registerTalksUrls(UrlContainerInterface $urls): void sprintf('https://img.youtube.com/vi/%s/0.jpg', $talk->getYoutubeId()), $talk->getTitle(), strip_tags(html_entity_decode($talk->getDescription())), - ['content_loc' => $talk->getYoutubeUrl()] + ['player_loc' => $talk->getYoutubeUrl()] ); $urls->addUrl($urlVideo,'video'); } From 0c683d280099ee36b8a850c518dcb3cef957f6c2 Mon Sep 17 00:00:00 2001 From: Nathan Boiron Date: Thu, 13 Feb 2025 20:08:07 +0100 Subject: [PATCH 02/13] Refactor de l'indexation de Meetup avec GraphQL (#1592) closes #1499 --- app/config/services.yml | 9 +- composer.json | 1 + composer.lock | 76 ++++++++- .../20250209185223_meetup_emojis.php | 14 ++ .../Command/ScrappingMeetupEventsCommand.php | 63 ++------ .../Indexation/Meetups/GraphQL/Edge.php | 18 +++ .../Indexation/Meetups/GraphQL/Events.php | 22 +++ .../Indexation/Meetups/GraphQL/Group.php | 20 +++ .../Indexation/Meetups/GraphQL/Node.php | 28 ++++ .../Meetups/GraphQL/QueryGroupsResponse.php | 22 +++ .../Indexation/Meetups/GraphQL/Venue.php | 18 +++ .../Indexation/Meetups/MeetupClient.php | 121 ++++++++++++++ .../Meetups/MeetupDateTimeConverter.php | 147 ----------------- .../Indexation/Meetups/MeetupScraper.php | 126 --------------- .../Indexation/Meetups/MeetupClient.php | 152 ++++++++++++++++++ .../Meetups/MeetupDateTimeConverter.php | 99 ------------ .../Indexation/Meetups/MeetupScrapper.php | 103 ------------ 17 files changed, 515 insertions(+), 524 deletions(-) create mode 100644 db/migrations/20250209185223_meetup_emojis.php create mode 100644 sources/AppBundle/Indexation/Meetups/GraphQL/Edge.php create mode 100644 sources/AppBundle/Indexation/Meetups/GraphQL/Events.php create mode 100644 sources/AppBundle/Indexation/Meetups/GraphQL/Group.php create mode 100644 sources/AppBundle/Indexation/Meetups/GraphQL/Node.php create mode 100644 sources/AppBundle/Indexation/Meetups/GraphQL/QueryGroupsResponse.php create mode 100644 sources/AppBundle/Indexation/Meetups/GraphQL/Venue.php create mode 100644 sources/AppBundle/Indexation/Meetups/MeetupClient.php delete mode 100644 sources/AppBundle/Indexation/Meetups/MeetupDateTimeConverter.php delete mode 100644 sources/AppBundle/Indexation/Meetups/MeetupScraper.php create mode 100644 tests/units/AppBundle/Indexation/Meetups/MeetupClient.php delete mode 100644 tests/units/AppBundle/Indexation/Meetups/MeetupDateTimeConverter.php delete mode 100644 tests/units/AppBundle/Indexation/Meetups/MeetupScrapper.php diff --git a/app/config/services.yml b/app/config/services.yml index 56736008e..c427f3610 100644 --- a/app/config/services.yml +++ b/app/config/services.yml @@ -707,7 +707,6 @@ services: autowire: true public: true - app.github.http_client: class: GuzzleHttp\Client arguments: @@ -717,3 +716,11 @@ services: AppBundle\Github\GithubClient: arguments: $githubClient: '@app.github.http_client' + + app.meetup.http_client: + class: GuzzleHttp\Client + + AppBundle\Indexation\Meetups\MeetupClient: + arguments: + $httpClient: '@app.meetup.http_client' + $antennesCollection: '@AppBundle\Antennes\AntennesCollection' diff --git a/composer.json b/composer.json index 892a43269..a8b30a001 100644 --- a/composer.json +++ b/composer.json @@ -15,6 +15,7 @@ "captioning/captioning": "^2.6", "ccmbenchmark/ting_bundle": "3.4.1", "cocur/slugify": "^2.3", + "cuyz/valinor": "^0.17.1", "doctrine/dbal": "^2.5", "ekino/newrelic-bundle": "^1.4", "erusev/parsedown": "^1.6", diff --git a/composer.lock b/composer.lock index 00fa2c9c2..3ca321ac9 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6987c8511259e54a95e24b8d9818e0a1", + "content-hash": "e837825c26c89e1148ab591c5c3db9da", "packages": [ { "name": "algolia/algoliasearch-client-php", @@ -492,6 +492,80 @@ }, "time": "2017-03-23T21:52:55+00:00" }, + { + "name": "cuyz/valinor", + "version": "0.17.1", + "source": { + "type": "git", + "url": "https://github.com/CuyZ/Valinor.git", + "reference": "8dbd01df82bf3eaf57246d1493d54b958b00f900" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CuyZ/Valinor/zipball/8dbd01df82bf3eaf57246d1493d54b958b00f900", + "reference": "8dbd01df82bf3eaf57246d1493d54b958b00f900", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.0", + "doctrine/annotations": "^1.11", + "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0", + "psr/simple-cache": "^1.0 || ^2.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.4", + "infection/infection": "^0.26", + "marcocesarato/php-conventional-changelog": "^1.12", + "mikey179/vfsstream": "^1.6.10", + "phpstan/phpstan": "^1.3", + "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.12.23", + "vimeo/psalm": "^4.18.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "CuyZ\\Valinor\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Romain Canon", + "email": "romain.hydrocanon@gmail.com", + "homepage": "https://github.com/romm" + } + ], + "description": "Library that helps to map any input into a strongly-typed value object structure.", + "homepage": "https://github.com/CuyZ/Valinor", + "keywords": [ + "array", + "conversion", + "hydrator", + "json", + "mapper", + "mapping", + "object", + "tree", + "yaml" + ], + "support": { + "issues": "https://github.com/CuyZ/Valinor/issues", + "source": "https://github.com/CuyZ/Valinor/tree/0.17.1" + }, + "funding": [ + { + "url": "https://github.com/romm", + "type": "github" + } + ], + "time": "2023-01-18T09:52:40+00:00" + }, { "name": "doctrine/annotations", "version": "1.14.4", diff --git a/db/migrations/20250209185223_meetup_emojis.php b/db/migrations/20250209185223_meetup_emojis.php new file mode 100644 index 000000000..fed21b7b0 --- /dev/null +++ b/db/migrations/20250209185223_meetup_emojis.php @@ -0,0 +1,14 @@ +execute("ALTER TABLE afup_meetup CHANGE description description text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;"); + $this->execute("ALTER TABLE afup_meetup CHANGE title title varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;"); + } +} diff --git a/sources/AppBundle/Command/ScrappingMeetupEventsCommand.php b/sources/AppBundle/Command/ScrappingMeetupEventsCommand.php index 90640a0a6..8b44b7258 100644 --- a/sources/AppBundle/Command/ScrappingMeetupEventsCommand.php +++ b/sources/AppBundle/Command/ScrappingMeetupEventsCommand.php @@ -5,7 +5,7 @@ namespace AppBundle\Command; use AppBundle\Event\Model\Repository\MeetupRepository; -use AppBundle\Indexation\Meetups\MeetupScraper; +use AppBundle\Indexation\Meetups\MeetupClient; use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; use Symfony\Component\Console\Command\LockableTrait; use Symfony\Component\Console\Input\InputInterface; @@ -16,9 +16,6 @@ class ScrappingMeetupEventsCommand extends ContainerAwareCommand { use LockableTrait; - /** - * @see Command - */ protected function configure(): void { $this @@ -28,12 +25,6 @@ protected function configure(): void ; } - /** - * - * @see Command - * - * @throws \Exception - */ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); @@ -47,54 +38,32 @@ protected function execute(InputInterface $input, OutputInterface $output): int try { $ting = $this->getContainer()->get('ting'); - $meetupScraper = new MeetupScraper(); - $meetups = $meetupScraper->getEvents(); + + /** @var MeetupClient $meetupClient */ + $meetupClient = $this->getContainer()->get(MeetupClient::class); + $meetups = $meetupClient->getEvents(); $meetupRepository = $ting->get(MeetupRepository::class); - $emlementsLength = $this->countAllNestedElements($meetups); - $io->progressStart($emlementsLength); - foreach ($meetups as $antenneMeetups) { - foreach ($antenneMeetups as $meetup) { - $io->progressAdvance(); + $io->progressStart(count($meetups)); + foreach ($meetups as $meetup) { + $io->progressAdvance(); - $id =$meetup->getId(); - $existingMeetup = $meetupRepository->get($id); - if (!$existingMeetup) { - $meetupRepository->save($meetup); - } else { - $io->note(sprintf('Meetup id %d déjà en base.', $id)); - } + $id = $meetup->getId(); + $existingMeetup = $meetupRepository->get($id); + if (!$existingMeetup) { + $meetupRepository->save($meetup); + } else { + $io->note(sprintf('Meetup id %d déjà en base.', $id)); } } + $io->progressFinish(); $io->success('Terminé avec succès'); + return 1; } catch (\Exception $e) { throw new \Exception('Problème lors du scraping ou de la sauvegarde des évènements Meetup', $e->getCode(), $e); } } - - /** - * Permet de faire un count sur un tableau multi-dimensionnel - * - * @param $array - * - * @return int - */ - private function countAllNestedElements($array) - { - $count = 0; - - foreach ($array as $element) { - if (is_array($element)) { - // Si l'élément est un tableau, on appelle récursivement la fonction - $count += $this->countAllNestedElements($element); - } else { - $count++; - } - } - - return $count; - } } diff --git a/sources/AppBundle/Indexation/Meetups/GraphQL/Edge.php b/sources/AppBundle/Indexation/Meetups/GraphQL/Edge.php new file mode 100644 index 000000000..bee40eadc --- /dev/null +++ b/sources/AppBundle/Indexation/Meetups/GraphQL/Edge.php @@ -0,0 +1,18 @@ +node = $node; + } +} diff --git a/sources/AppBundle/Indexation/Meetups/GraphQL/Events.php b/sources/AppBundle/Indexation/Meetups/GraphQL/Events.php new file mode 100644 index 000000000..096e75754 --- /dev/null +++ b/sources/AppBundle/Indexation/Meetups/GraphQL/Events.php @@ -0,0 +1,22 @@ + */ + public array $edges; + + /** + * @param list $edges + */ + public function __construct(array $edges) + { + $this->edges = $edges; + } +} diff --git a/sources/AppBundle/Indexation/Meetups/GraphQL/Group.php b/sources/AppBundle/Indexation/Meetups/GraphQL/Group.php new file mode 100644 index 000000000..d2ed9a953 --- /dev/null +++ b/sources/AppBundle/Indexation/Meetups/GraphQL/Group.php @@ -0,0 +1,20 @@ +upcomingEvents = $upcomingEvents; + $this->pastEvents = $pastEvents; + } +} diff --git a/sources/AppBundle/Indexation/Meetups/GraphQL/Node.php b/sources/AppBundle/Indexation/Meetups/GraphQL/Node.php new file mode 100644 index 000000000..c9abae146 --- /dev/null +++ b/sources/AppBundle/Indexation/Meetups/GraphQL/Node.php @@ -0,0 +1,28 @@ +id = $id; + $this->title = $title; + $this->description = $description; + $this->dateTime = $dateTime; + $this->venue = $venue; + } +} diff --git a/sources/AppBundle/Indexation/Meetups/GraphQL/QueryGroupsResponse.php b/sources/AppBundle/Indexation/Meetups/GraphQL/QueryGroupsResponse.php new file mode 100644 index 000000000..b15145a8f --- /dev/null +++ b/sources/AppBundle/Indexation/Meetups/GraphQL/QueryGroupsResponse.php @@ -0,0 +1,22 @@ + */ + public array $data; + + /** + * @param array $data + */ + public function __construct(array $data) + { + $this->data = $data; + } +} diff --git a/sources/AppBundle/Indexation/Meetups/GraphQL/Venue.php b/sources/AppBundle/Indexation/Meetups/GraphQL/Venue.php new file mode 100644 index 000000000..04e3d7ef1 --- /dev/null +++ b/sources/AppBundle/Indexation/Meetups/GraphQL/Venue.php @@ -0,0 +1,18 @@ +name = $name; + } +} diff --git a/sources/AppBundle/Indexation/Meetups/MeetupClient.php b/sources/AppBundle/Indexation/Meetups/MeetupClient.php new file mode 100644 index 000000000..ea30a28d7 --- /dev/null +++ b/sources/AppBundle/Indexation/Meetups/MeetupClient.php @@ -0,0 +1,121 @@ +httpClient = $httpClient; + $this->antennesCollection = $antennesCollection; + } + + /** + * @return Meetup[] + */ + public function getEvents(): array + { + $response = $this->httpClient->request('POST', 'https://api.meetup.com/gql', [ + 'body' => json_encode([ + 'query' => $this->getEventsQuery(), + 'variables' => [ + 'quantityUpcoming' => self::QUANTITY_UPCOMING_EVENTS, + 'quantityPast' => self::QUANTITY_PAST_EVENTS, + ], + ]), + 'headers' => [ + 'Content-Type' => 'application/json', + ], + ]); + + /** @var QueryGroupsResponse $groupResponse */ + $groupResponse = (new MapperBuilder()) + ->allowSuperfluousKeys() + ->supportDateFormats('Y-m-d\TH:iP') + ->mapper() + ->map(QueryGroupsResponse::class, Source::json($response->getBody()->getContents())); + + $meetups = []; + + foreach ($groupResponse->data as $nameAntenne => $group) { + $edges = array_merge($group->upcomingEvents->edges, $group->pastEvents->edges); + + foreach ($edges as $edge) { + $meetup = new Meetup(); + $meetup->setId((int) $edge->node->id); + $meetup->setTitle($edge->node->title); + $meetup->setDescription($edge->node->description); + $meetup->setDate($edge->node->dateTime); + $meetup->setAntenneName($nameAntenne); + + if ($edge->node->venue !== null) { + $meetup->setLocation($edge->node->venue->name); + } + + $meetups[] = $meetup; + } + } + + return $meetups; + } + + private function getEventsQuery(): string + { + $queries = []; + + foreach ($this->antennesCollection->getAll() as $antenne) { + if ($antenne->meetup === null) { + continue; + } + + $queries[] = sprintf( + "%s: group(id: %s) { ...GroupFragment }\n", + $antenne->code, + $antenne->meetup->id, + ); + } + + $query = 'query($quantityUpcoming: Int, $quantityPast: Int) { + %s +} + +fragment EventFragment on Event { + id + title + description + dateTime + venue { name } +} + +fragment GroupFragment on Group { + upcomingEvents(input: {last: $quantityUpcoming}) { + edges { + node { ... EventFragment } + } + } + + pastEvents(input: {first: $quantityPast}, sortOrder: DESC) { + edges { + node { ... EventFragment } + } + } +}'; + + return sprintf($query, implode("\n", $queries)); + } +} diff --git a/sources/AppBundle/Indexation/Meetups/MeetupDateTimeConverter.php b/sources/AppBundle/Indexation/Meetups/MeetupDateTimeConverter.php deleted file mode 100644 index 482e91133..000000000 --- a/sources/AppBundle/Indexation/Meetups/MeetupDateTimeConverter.php +++ /dev/null @@ -1,147 +0,0 @@ -getDateTimeStringWithoutAccents($dateTimeString); - - $parsedDateTime = $this->parseDateString($unAccentDateString); - $this->validateTime($parsedDateTime['time']); - - $monthNames = [ - 'janv' => 1, 'fevr' => 2, 'mars' => 3, 'avr' => 4, 'mai' => 5, 'juin' => 6, - 'juil' => 7, 'aout' => 8, 'sept' => 9, 'oct' => 10, 'nov' => 11, 'dec' => 12, - ]; - - $monthAbbreviation = $parsedDateTime['monthAbbreviation']; - if (!array_key_exists($monthAbbreviation, $monthNames)) { - throw new Exception('Abbréviation de mois invalide : ' . $monthAbbreviation); - } - - [$year, $day, $hour, $minute, $timezoneAbbreviation] = [ - $parsedDateTime['year'], - $parsedDateTime['day'], - $parsedDateTime['time'][0], - $parsedDateTime['time'][1], - $parsedDateTime['timezoneAbbreviation'], - ]; - - $dateTimeString = sprintf( - '%04d-%02d-%02d %02d:%02d', - $year, - $monthNames[$monthAbbreviation], - $day, - $hour, - $minute - ); - - $timezone = $this->getTimezoneFromAbbreviation($timezoneAbbreviation); - - try { - return new DateTime($dateTimeString, $timezone); - } catch (Exception $e) { - throw new Exception('Format de date invalide', $e->getCode(), $e); - } - } - - /** - * - * @throws Exception - */ - private function parseDateString(string $dateTimeString): array - { - $pattern = '/(\S+)\s+(\d+)\s+(\S+)\s+(\d+),\s+(\d+:\d+)\s+([A-Za-z0-9\s\+\-]+)/u'; - preg_match($pattern, $dateTimeString, $matches); - - if (count($matches) !== 7) { - throw new Exception('Format de date invalide : ' . $dateTimeString); - } - - return [ - 'day' => intval($matches[2]), - 'monthAbbreviation' => mb_strtolower(str_replace('.', '', $matches[3]), 'UTF-8'), - 'year' => intval($matches[4]), - 'time' => explode(':', $matches[5]), - 'timezoneAbbreviation' => $matches[6], - ]; - } - - /** - * @param int $time - * - * - * @throws Exception - */ - private function validateTime($time): void - { - [$hour, $minute] = $time; - - if ($hour >= 24 || $minute >= 60) { - throw new Exception('Heure invalide : ' . implode(':', $time)); - } - } - - /** - * - * - * @throws Exception - */ - public function getTimezoneFromAbbreviation(string $timezoneAbbreviation): \DateTimeZone - { - $timezoneNameMap = $this->getTimezoneNameMap(); - - if (isset($timezoneNameMap[$timezoneAbbreviation])) { - return new DateTimeZone($timezoneNameMap[$timezoneAbbreviation]); - } - - throw new Exception('Fuseau horaire inconnu : ' . $timezoneAbbreviation); - } - - /** - * Mapping des abréviations de fuseau horaire aux noms complets des fuseaux horaires. - * - * Cela permet de gérer les conversions entre les heures d'été (CEST) et l'heure standard (CET) - * pour les événements en France. - * - * @return string[] - */ - private function getTimezoneNameMap(): array - { - return [ - 'UTC+0' => 'UTC', - 'UTC' => 'UTC', - 'GMT' => 'GMT', - 'UTC+1' => 'Europe/Paris', // CET (Central European Time) - 'CET' => 'Europe/Paris', // CET (Central European Time) - 'UTC+2' => 'Europe/Athens', // CEST (Central European Summer Time) - 'CEST' => 'Europe/Athens', // CEST (Central European Summer Time) - ]; - } - - /** - * @param string $dateTimeString - * @return string - */ - private function getDateTimeStringWithoutAccents($dateTimeString): ?string - { - $normalized = Normalizer::normalize($dateTimeString, Normalizer::FORM_KD); - - return preg_replace('/\p{Mn}/u', '', $normalized); - } -} diff --git a/sources/AppBundle/Indexation/Meetups/MeetupScraper.php b/sources/AppBundle/Indexation/Meetups/MeetupScraper.php deleted file mode 100644 index 118802959..000000000 --- a/sources/AppBundle/Indexation/Meetups/MeetupScraper.php +++ /dev/null @@ -1,126 +0,0 @@ - - * - * @throws Exception - * @throws InvalidArgumentException - */ - public function getEvents() - { - try { - $antennes = $this->getAntennesFromOfficesCollection(); - - $eventsArray = []; - foreach ($antennes as $antenneKey => $antenne) { - if ($antenne->meetup === null) { - continue; - } - - $meetupAntenneName = $antenneKey; - - $xpath = $this->getDomContent($antenne->meetup->urlName); - - $events = $xpath->query("//*[contains(@id, 'event-card')]"); - foreach ($events as $event) { - try { - if (!$event instanceof \DOMElement) { - throw new \Exception('Élement DOM de type invalide'); - } - - $eventUrl = $event->getAttribute('href'); - - if (preg_match('/\/events\/(\d+)\//', $eventUrl, $matches)) { - $id = (int) $matches[1]; - } else { - throw new Exception(sprintf('Pas d\'id pour cet évent de l\'antenne %s', $antenne->code)); - } - - $dateString = $xpath->query(".//time", $event)->item(0)->nodeValue; - $dateTime = (new MeetupDateTimeConverter())->convertStringToDateTime($dateString); - - $title = $xpath->query(".//span[contains(@class, 'cardTitle')]", $event)->item(0)->nodeValue; - - $descriptionElements = $xpath->query("//div[contains(@class, 'utils_cardDescription__alO8K')]"); - - $description = ''; - foreach ($descriptionElements as $descriptionElement) { - $description .= ' ' . $descriptionElement->nodeValue; - } - - $eventsArray[$meetupAntenneName][] = (new Meetup()) - ->setId($id) - ->setDate($dateTime) - ->setTitle($title) - ->setDescription($description) - ->setAntenneName($meetupAntenneName); - } catch (Exception $e) { - throw new Exception('Problème à la construction d\'un évenement', $e->getCode(), $e); - } - } - } - } catch (Exception $e) { - throw new Exception('Problème à la construction de la liste des évenements', $e->getCode(), $e); - } - - if ([] === $eventsArray) { - throw new Exception('Le DOM sur Meetup a très certainement changé (aucun event détecté)'); - } - - return $eventsArray; - } - - /** - * Récupère et charge les données de la page pour une meetup url donnée - * - * @throws Exception - */ - public function getDomContent(string $antenneUrl): DOMXPath - { - $url = self::MEETUP_URL . $antenneUrl; - $content = file_get_contents($url); - - if (strpos($content, 'Groupe introuvable')) { - throw new Exception(sprintf('Antenne url icorrecte %s', $url), 500); - } - - // Charger le contenu dans un objet DOMDocument - $dom = new DOMDocument(); - libxml_use_internal_errors(true); // Désactiver les erreurs de libxml pour éviter les messages d'erreur lors de l'analyse HTML - $dom->loadHTML($content); - libxml_clear_errors(); // Effacer les erreurs de libxml après l'analyse HTML - - return new DOMXPath($dom); - } - - /** - * @return array - * @throws Exception - */ - public function getAntennesFromOfficesCollection(): array - { - $antennes = (new AntennesCollection())->getAll(); - if ([] === $antennes) { - throw new Exception("The antennes array is invalid or is empty"); - } - - return $antennes; - } -} diff --git a/tests/units/AppBundle/Indexation/Meetups/MeetupClient.php b/tests/units/AppBundle/Indexation/Meetups/MeetupClient.php new file mode 100644 index 000000000..a0997961c --- /dev/null +++ b/tests/units/AppBundle/Indexation/Meetups/MeetupClient.php @@ -0,0 +1,152 @@ +makeGuzzleMockClient( + new Response(500) + ); + + $meetupClient = new TestedClass($httpClient, new AntennesCollection()); + + $this + ->exception(fn () => $meetupClient->getEvents()) + ->isInstanceOf(Exception::class) + ->hasMessage("Server error: `POST https://api.meetup.com/gql` resulted in a `500 Internal Server Error` response"); + } + + public function testInvalidJsonInResponse(): void + { + $httpClient = $this->makeGuzzleMockClient( + new Response(200, [], 'invalid json') + ); + + $meetupClient = new TestedClass($httpClient, new AntennesCollection()); + + $this + ->exception(fn () => $meetupClient->getEvents()) + ->isInstanceOf(Exception::class) + ->hasMessage('The given value is not a valid JSON entry.'); + } + + public function testReturnsValidResponse(): void + { + $httpClient = $this->makeGuzzleMockClient( + new Response( + 200, + [], + json_encode([ + 'data' => [ + 'lyon' => [ + 'upcomingEvents' => [ + 'edges' => [ + [ + 'node' => [ + 'id' => "12", + 'title' => 'Upcoming 1', + 'description' => 'Desc 1', + 'dateTime' => '2025-02-11T18:30+01:00', + 'venue' => [ + 'name' => 'Lieu 1', + ], + ], + ], + [ + 'node' => [ + 'id' => "34", + 'title' => 'Upcoming 2', + 'description' => 'Desc 2', + 'dateTime' => '2025-03-20T18:30+01:00', + 'venue' => null, + ], + ], + ], + ], + 'pastEvents' => [ + 'edges' => [ + [ + 'node' => [ + 'id' => "56", + 'title' => 'Past 1', + 'description' => 'Desc 3', + 'dateTime' => '2019-04-08T18:30+01:00', + 'venue' => null, + ], + ], + [ + 'node' => [ + 'id' => "78", + 'title' => 'Past 2', + 'description' => 'Desc 4', + 'dateTime' => '2020-10-17T18:30+01:00', + 'venue' => [ + 'name' => 'Lieu 2', + ], + ], + ], + ], + ], + ], + ], + ]), + ), + ); + + $meetupClient = new TestedClass($httpClient, new AntennesCollection()); + + $antennes = $meetupClient->getEvents(); + + $this->integer(count($antennes))->isEqualTo(4); + + $this->integer($antennes[0]->getId())->isEqualTo(12); + $this->string($antennes[0]->getTitle())->isEqualTo('Upcoming 1'); + $this->string($antennes[0]->getDescription())->isEqualTo('Desc 1'); + $this->dateTime($antennes[0]->getDate())->isEqualTo(new \DateTime('2025-02-11T18:30+01:00')); + $this->string($antennes[0]->getAntenneName())->isEqualTo('lyon'); + $this->string($antennes[0]->getLocation())->isEqualTo('Lieu 1'); + + $this->integer($antennes[1]->getId())->isEqualTo(34); + $this->string($antennes[1]->getTitle())->isEqualTo('Upcoming 2'); + $this->string($antennes[1]->getDescription())->isEqualTo('Desc 2'); + $this->dateTime($antennes[1]->getDate())->isEqualTo(new \DateTime('2025-03-20T18:30+01:00')); + $this->string($antennes[1]->getAntenneName())->isEqualTo('lyon'); + $this->variable($antennes[1]->getLocation())->isNull(); + + $this->integer($antennes[2]->getId())->isEqualTo(56); + $this->string($antennes[2]->getTitle())->isEqualTo('Past 1'); + $this->string($antennes[2]->getDescription())->isEqualTo('Desc 3'); + $this->dateTime($antennes[2]->getDate())->isEqualTo(new \DateTime('2019-04-08T18:30+01:00')); + $this->string($antennes[2]->getAntenneName())->isEqualTo('lyon'); + $this->variable($antennes[2]->getLocation())->isNull(); + + $this->integer($antennes[3]->getId())->isEqualTo(78); + $this->string($antennes[3]->getTitle())->isEqualTo('Past 2'); + $this->string($antennes[3]->getDescription())->isEqualTo('Desc 4'); + $this->dateTime($antennes[3]->getDate())->isEqualTo(new \DateTime('2020-10-17T18:30+01:00')); + $this->string($antennes[3]->getAntenneName())->isEqualTo('lyon'); + $this->string($antennes[3]->getLocation())->isEqualTo('Lieu 2'); + } + + private function makeGuzzleMockClient(Response $response): Client + { + return new Client([ + 'handler' => HandlerStack::create( + new MockHandler([$response]) + ) + ]); + } +} diff --git a/tests/units/AppBundle/Indexation/Meetups/MeetupDateTimeConverter.php b/tests/units/AppBundle/Indexation/Meetups/MeetupDateTimeConverter.php deleted file mode 100644 index 33e084be9..000000000 --- a/tests/units/AppBundle/Indexation/Meetups/MeetupDateTimeConverter.php +++ /dev/null @@ -1,99 +0,0 @@ -getTimezoneFromAbbreviation($timezoneName)->getName(); - $timezone = new DateTimeZone($timezoneName); - - $this - ->given($meetupDateTimeConverter) - ->then - ->dateTime($meetupDateTimeConverter->convertStringToDateTime($dateString)) - ->hasDate($year, $month, $day) - ->hasTime($hour, $minute, $seconds) - ->hasTimeZone($timezone); - } - - /** - * Data provider for valid date strings and their expected DateTime objects. - * - * @return array - */ - public function validDateStringProvider() - { - return [ - ['jeu. 03 janv. 2022, 02:30 UTC+1', 2022, 01, 03, 02, 30, 0, 'UTC+1'], - ['lun. 01 févr. 2022, 18:30 UTC+2', 2022, 02, 01, 18, 30, 0, 'UTC+2'], - ['lun. 01 fevr. 2022, 18:30 UTC+2', 2022, 02, 01, 18, 30, 0, 'UTC+2'], - ['ven. 04 mars 2022, 18:30 UTC+1', 2022, 03, 04, 18, 30, 0, 'UTC+1'], - ['dim. 02 avr. 2020, 18:30 UTC+1', 2020, 04, 02, 18, 30, 0, 'UTC+1'], - ['mar. 02 mai 2022, 18:30 UTC', 2022, 05, 02, 18, 30, 0, 'UTC'], - ['ven. 03 juin 2021, 18:30 UTC+1', 2021, 06, 03, 18, 30, 0, 'UTC+1'], - ['dim. 03 juil. 2022, 18:30 UTC+2', 2022, 07, 03, 18, 30, 0, 'UTC+2'], - ['mer. 02 août 2022, 11:30 UTC+1', 2022, 8, 02, 11, 30, 0, 'UTC+1'], - ['mer. 02 aout. 2022, 11:30 UTC+1', 2022, 8, 02, 11, 30, 0, 'UTC+1'], - ['sam. 03 sept. 2022, 18:30 UTC', 2022, 9, 03, 18, 30, 0, 'UTC'], - ['lun. 03 oct. 2025, 18:30 UTC+1', 2025, 10, 03, 18, 30, 0, 'UTC+1'], - ['jeu. 03 nov. 2022, 16:30 UTC+2', 2022, 11, 03, 16, 30, 0, 'UTC+2'], - ['sam. 03 déc. 2019, 18:30 UTC+1', 2019, 12, 03, 18, 30, 0, 'UTC+1'], - ]; - } - - /** - * @dataProvider invalidDateStringProvider - * - * @param string $invalidDateString - */ - public function testConvertInvalidStringToDateTime($invalidDateString): void - { - $this - ->given($meetupDateTimeConverter = new TestedClass()) - ->exception(function () use ($meetupDateTimeConverter, $invalidDateString): void { - $meetupDateTimeConverter->convertStringToDateTime($invalidDateString); - }) - ->isInstanceOf(\Exception::class); // Remplacez par le type de l'exception personnalisée si nécessaire - } - - /** - * Data provider for invalid date strings. - * - * @return array - */ - public function invalidDateStringProvider() - { - return [ - ['invalid_date_format'], - ['sam. 03 déc. 2019, 18:30 UTC+100'], - ['sam. 31 fev. 2019, 18:30 UTC+1'], - ['mar. 31 dec. 2021, 24:00 CET'], - ['jeu. 15 juil. 2020, 10:30 InvalidTimeZone'], - ['dim. 20 sep. 2025, 12:45'], - ['ven. 18 dece. 2023, 15:45 UTC',], - ]; - } -} diff --git a/tests/units/AppBundle/Indexation/Meetups/MeetupScrapper.php b/tests/units/AppBundle/Indexation/Meetups/MeetupScrapper.php deleted file mode 100644 index 2a88b0135..000000000 --- a/tests/units/AppBundle/Indexation/Meetups/MeetupScrapper.php +++ /dev/null @@ -1,103 +0,0 @@ -exception(function () use ($meetupScraper, $antenneUrl): void { - $meetupScraper->getDomContent($antenneUrl); - }) - ->isInstanceOf(\Exception::class) - ->hasMessage(sprintf('Antenne url icorrecte %s', $url)); - } - - /** - * @param string $antenneUrl - * - * @dataProvider validMeetupUrlProvider - */ - public function testGetDomContentWithValidMeetupUrl($antenneUrl): void - { - $meetupScraper = new TestedClass(); - $xpath = $meetupScraper->getDomContent($antenneUrl); - - $this - ->object($xpath) - ->isInstanceOf(\DOMXPath::class); - } - - /** - * @throws \Exception - */ - public function testGetMeetupEvents(): void - { - $meetupScraper = new TestedClass(); - $events = $meetupScraper->getEvents(); - - $eventLength = count($events); - - $this - ->integer($eventLength) - ->isGreaterThan(0); - - foreach ($events as $antenneEvents) { - foreach ($antenneEvents as $event) { - $title = $event->getTitle(); - $date = $event->getDate(); - - $this->string($title)->isNotEmpty(); - $this->dateTime($date)->isInstanceOf(\DateTime::class); - } - } - } - - /** - * @throws \Exception - */ - public function testGetAntennesFromOfficesCollection(): void - { - $meetupScraper = new TestedClass(); - $antennes = $meetupScraper->getAntennesFromOfficesCollection(); - - $this - ->array($antennes) - ->isNotEmpty(); - } - - // Provide valid Meetup URLs for testing - protected function validMeetupUrlProvider() - { - return [ - ['Bordeaux-PHP-Meetup'], - ['afup-lyon-php'], - ]; - } - - // Provide invalid Meetup URLs for testing - protected function invalidMeetupUrlProvider() - { - return [ - ['invalid-url-1'], - ['invalid-url-2'], - ['afuep-lyon-phpe'], - ]; - } -} From e6af0942df9c6398930d686f980df03284f0a9b1 Mon Sep 17 00:00:00 2001 From: Albin Kester <83301974+stakovicz@users.noreply.github.com> Date: Thu, 13 Feb 2025 20:08:27 +0100 Subject: [PATCH 03/13] MySQL fix timezone (#1626) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * MySQL fix timezone * Ajout de la sécurité --- .env.dist | 1 + .../views/admin/healthcheck.html.twig | 45 ++++++++++++++++ app/config/config.yml | 9 ++++ app/config/routing/admin.yml | 4 ++ app/config/security.yml | 2 +- docker/dockerfiles/mysql/my.cnf | 1 + docker/dockerfiles/mysqltest/my.cnf | 1 + sources/Afup/Utils/Base_De_Donnees.php | 1 + .../Admin/HealthcheckController.php | 51 +++++++++++++++++++ .../features/Admin/Divers/Healthcheck.feature | 15 ++++++ 10 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 app/Resources/views/admin/healthcheck.html.twig create mode 100644 sources/AppBundle/Controller/Admin/HealthcheckController.php create mode 100644 tests/behat/features/Admin/Divers/Healthcheck.feature diff --git a/.env.dist b/.env.dist index 30d3520f2..71dc348d8 100644 --- a/.env.dist +++ b/.env.dist @@ -8,6 +8,7 @@ DATABASE_PORT=3306 DATABASE_NAME=web DATABASE_USER=afup DATABASE_PASSWORD=afup +DATABASE_TIMEZONE=+01:00 SECRET=ThisTokenIsNotSoSecretChangeIt diff --git a/app/Resources/views/admin/healthcheck.html.twig b/app/Resources/views/admin/healthcheck.html.twig new file mode 100644 index 000000000..5848309d8 --- /dev/null +++ b/app/Resources/views/admin/healthcheck.html.twig @@ -0,0 +1,45 @@ +{% extends 'admin/base_with_header.html.twig' %} + +{% block content %} +

Healthcheck

+
+
+
+

Dates

+
+
+
PHP
+
{{ dates.php }}
+
MySQL Base_De_Donnees
+
{{ dates.mysql_bdd }}
+
MySQL Ting
+
{{ dates.mysql_ting }}
+
Différence MySQL et PHP
+
+ {{ dates.diff ? 'Les timezones sont différentes' : 'Pas de différence de timezones' }} +
+
+
+
+
+
+

Versions

+
+
+
PHP
+
{{ versions.php }}
+
Symfony
+
{{ versions.symfony }}
+
+
+
+
+ +{% endblock %} diff --git a/app/config/config.yml b/app/config/config.yml index 566d27698..ea586721e 100644 --- a/app/config/config.yml +++ b/app/config/config.yml @@ -287,6 +287,10 @@ parameters: forum_partenaire: nom: 'Sponsors/Partenaires' niveau: 'ROLE_ADMIN' + healthcheck: + nom: 'Healthcheck' + niveau: 'ROLE_ADMIN' + url: '/admin/healthcheck' connexion: nom: 'Connexion' masquee: true @@ -357,6 +361,11 @@ ting: port: "%database_port%" user: "%database_user%" password: "%database_password%" + + databases_options: + "%database_name%": + timezone: "%database_timezone%" + repositories: event: namespace : AppBundle\Event\Model\Repository diff --git a/app/config/routing/admin.yml b/app/config/routing/admin.yml index eb9c0b485..f91980ab3 100644 --- a/app/config/routing/admin.yml +++ b/app/config/routing/admin.yml @@ -108,3 +108,7 @@ admin_site: admin_github_user_routes: resource: "admin_github_user.yml" prefix: /event/github-user + +admin_healthcheck: + path: /healthcheck + defaults: {_controller: AppBundle\Controller\Admin\HealthcheckController} diff --git a/app/config/security.yml b/app/config/security.yml index faf7c0a9a..9224988cc 100644 --- a/app/config/security.yml +++ b/app/config/security.yml @@ -61,7 +61,7 @@ security: - { path: ^/admin/members/general_meeting_vote, roles: ROLE_ADMIN } - { path: ^/admin/site, roles: ROLE_ADMIN } - { path: ^/admin/planete, roles: ROLE_ADMIN } - - { path: ^/admin/(members/reporting|association/relances|talk|slackmembers/check), roles: ROLE_ADMIN} + - { path: ^/admin/(members/reporting|association/relances|talk|slackmembers/check|healthcheck), roles: ROLE_ADMIN} - { path: ^/member, roles: [ROLE_USER, ROLE_MEMBER_EXPIRED]} - { path: ^/admin/, roles: ROLE_MEMBER_EXPIRED } - { path: ^/blog, allow_if: "request.getClientIp() in ['217.70.189.71', '127.0.0.1', '192.168.42.1'] or request.server.get('ALLOW_BLOG_FROM_ALL') == 1 or request.headers.get('x-afup-blog-api-key') == '%blog_api_key%'" } diff --git a/docker/dockerfiles/mysql/my.cnf b/docker/dockerfiles/mysql/my.cnf index f12448d2d..add9a012b 100644 --- a/docker/dockerfiles/mysql/my.cnf +++ b/docker/dockerfiles/mysql/my.cnf @@ -3,3 +3,4 @@ skip-host-cache skip-name-resolve innodb_file_per_table=1 sql-mode="STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION" +default-time-zone = "+00:00" diff --git a/docker/dockerfiles/mysqltest/my.cnf b/docker/dockerfiles/mysqltest/my.cnf index f12448d2d..add9a012b 100644 --- a/docker/dockerfiles/mysqltest/my.cnf +++ b/docker/dockerfiles/mysqltest/my.cnf @@ -3,3 +3,4 @@ skip-host-cache skip-name-resolve innodb_file_per_table=1 sql-mode="STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION" +default-time-zone = "+00:00" diff --git a/sources/Afup/Utils/Base_De_Donnees.php b/sources/Afup/Utils/Base_De_Donnees.php index 5abbefe8f..329c878e4 100644 --- a/sources/Afup/Utils/Base_De_Donnees.php +++ b/sources/Afup/Utils/Base_De_Donnees.php @@ -42,6 +42,7 @@ public function getDbLink() if ($this->link === null) { $this->link = mysqli_connect($this->config['host'], $this->config['user'], $this->config['password'], null, (int) $this->config['port']) or die('Connexion à la base de données impossible'); mysqli_set_charset($this->link, "utf8mb4"); + mysqli_query($this->link, "SET time_zone = '" . getenv('DATABASE_TIMEZONE') . "'"); $this->selectionnerBase($this->config['database']); } return $this->link; diff --git a/sources/AppBundle/Controller/Admin/HealthcheckController.php b/sources/AppBundle/Controller/Admin/HealthcheckController.php new file mode 100644 index 000000000..a0d01e8d3 --- /dev/null +++ b/sources/AppBundle/Controller/Admin/HealthcheckController.php @@ -0,0 +1,51 @@ +ting = $ting; + } + + public function __invoke(): Response + { + $php = new DateTime(); + + $bdd = new _Site_Base_De_Donnees(); + $mysqlBdd = $bdd->obtenirUn('SELECT CURRENT_TIMESTAMP'); + $mysqlBdd = new DateTime($mysqlBdd); + + $repo = $this->ting->get(EventRepository::class); + $mysqlTing = $repo->getQuery('SELECT CURRENT_TIMESTAMP')->execute()['CURRENT_TIMESTAMP']; + $mysqlTing = new DateTime($mysqlTing); + + $diff = $php->getTimestamp() !== $mysqlBdd->getTimestamp() || $php->getTimestamp() !== $mysqlTing->getTimestamp(); + + return $this->render('admin/healthcheck.html.twig', [ + 'dates' => [ + 'php' => $php->format(\DateTime::ATOM), + 'mysql_bdd' => $mysqlBdd->format(\DateTime::ATOM), + 'mysql_ting' => $mysqlTing->format(\DateTime::ATOM), + 'diff' => $diff + ], + 'versions' => [ + 'php' => phpversion(), + 'symfony' => Kernel::VERSION + ] + ]); + } +} diff --git a/tests/behat/features/Admin/Divers/Healthcheck.feature b/tests/behat/features/Admin/Divers/Healthcheck.feature new file mode 100644 index 000000000..167c95ece --- /dev/null +++ b/tests/behat/features/Admin/Divers/Healthcheck.feature @@ -0,0 +1,15 @@ +Feature: Administration - Healthcheck + + @reloadDbWithTestData + Scenario: Un membre ne peut pas accéder aux vérifications du site + Given I am logged-in with the user "paul" and the password "paul" + And I am on "/admin/healthcheck" + Then the response status code should be 403 + + @reloadDbWithTestData + Scenario: Vérifications du site + Given I am logged in as admin and on the Administration + And I follow "Healthcheck" + Then I should see "Healthcheck" + And I should see "Pas de différence de timezones" + And I should not see "Les timezones sont différentes" From 75b8768f6f409179057c6d8752d7037bac0e2c15 Mon Sep 17 00:00:00 2001 From: Albin Kester <83301974+stakovicz@users.noreply.github.com> Date: Sat, 15 Feb 2025 09:25:12 +0100 Subject: [PATCH 04/13] Upgrade AlgoliaSearch (#1630) --- app/config/services.yml | 3 +- composer.json | 2 +- composer.lock | 245 ++++++++++-------- .../AppBundle/Command/IndexMeetupsCommand.php | 8 +- .../Controller/Website/HomeController.php | 18 +- .../AppBundle/Indexation/Meetups/Runner.php | 28 +- sources/AppBundle/Indexation/Talks/Runner.php | 22 +- 7 files changed, 164 insertions(+), 162 deletions(-) diff --git a/app/config/services.yml b/app/config/services.yml index c427f3610..7dee53644 100644 --- a/app/config/services.yml +++ b/app/config/services.yml @@ -322,7 +322,8 @@ services: AppBundle\Routing\LegacyRouter: autowire: true - AlgoliaSearch\Client: + Algolia\AlgoliaSearch\SearchClient: + factory: [ Algolia\AlgoliaSearch\SearchClient, create ] arguments: ["%algolia_app_id%", "%algolia_backend_api_key%"] AppBundle\Joindin\JoindinComments: diff --git a/composer.json b/composer.json index a8b30a001..fea7c4354 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,7 @@ "ext-json": "*", "ext-libxml": "*", "ext-openssl": "*", - "algolia/algoliasearch-client-php": "^1.12", + "algolia/algoliasearch-client-php": "^3.4", "beberlei/assert": "^2.9", "captioning/captioning": "^2.6", "ccmbenchmark/ting_bundle": "3.4.1", diff --git a/composer.lock b/composer.lock index 3ca321ac9..ce68a66b0 100644 --- a/composer.lock +++ b/composer.lock @@ -4,31 +4,43 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e837825c26c89e1148ab591c5c3db9da", + "content-hash": "6f80c056e0d1c12771ebc7a6a3582164", "packages": [ { "name": "algolia/algoliasearch-client-php", - "version": "1.28.1", + "version": "3.4.2", "source": { "type": "git", "url": "https://github.com/algolia/algoliasearch-client-php.git", - "reference": "43b0b0dc64e2d0f206d903ad3a4fb8b0a3660f81" + "reference": "7505959737f7944507be7ef476752ad761873c4a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/algolia/algoliasearch-client-php/zipball/43b0b0dc64e2d0f206d903ad3a4fb8b0a3660f81", - "reference": "43b0b0dc64e2d0f206d903ad3a4fb8b0a3660f81", + "url": "https://api.github.com/repos/algolia/algoliasearch-client-php/zipball/7505959737f7944507be7ef476752ad761873c4a", + "reference": "7505959737f7944507be7ef476752ad761873c4a", "shasum": "" }, "require": { "ext-curl": "*", + "ext-json": "*", "ext-mbstring": "*", - "php": "^5.3 || ^7.0" + "php": "^7.3 || ^8.0", + "psr/http-message": "^1.1 || ^2.0", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", - "satooshi/php-coveralls": "^1.0" + "friendsofphp/php-cs-fixer": "^2.0", + "fzaninotto/faker": "^1.8", + "phpunit/phpunit": "^8.0 || ^9.0", + "symfony/yaml": "^2.0 || ^4.0" + }, + "suggest": { + "guzzlehttp/guzzle": "If you prefer to use Guzzle HTTP client instead of the Http Client implementation provided by the package" }, + "bin": [ + "bin/algolia-doctor" + ], "type": "library", "extra": { "branch-alias": { @@ -36,8 +48,12 @@ } }, "autoload": { - "psr-0": { - "AlgoliaSearch": "src/" + "files": [ + "src/Http/Psr7/functions.php", + "src/functions.php" + ], + "psr-4": { + "Algolia\\AlgoliaSearch\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -48,23 +64,21 @@ { "name": "Algolia Team", "email": "contact@algolia.com" - }, - { - "name": "Jonathan H. Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Ryan T. Catlin", - "email": "ryan.catlin@gmail.com" } ], "description": "Algolia Search API Client for PHP", - "homepage": "https://github.com/algolia/algoliasearch-client-php", + "keywords": [ + "algolia", + "api", + "client", + "php", + "search" + ], "support": { "issues": "https://github.com/algolia/algoliasearch-client-php/issues", - "source": "https://github.com/algolia/algoliasearch-client-php/tree/1.28.1" + "source": "https://github.com/algolia/algoliasearch-client-php/tree/3.4.2" }, - "time": "2020-06-03T15:36:28+00:00" + "time": "2025-01-22T11:13:54+00:00" }, { "name": "aura/sqlquery", @@ -492,6 +506,77 @@ }, "time": "2017-03-23T21:52:55+00:00" }, + { + "name": "composer/pcre", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/67a32d7d6f9f560b726ab25a061b38ff3a80c560", + "reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.3", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/1.0.1" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-01-21T20:24:37+00:00" + }, { "name": "cuyz/valinor", "version": "0.17.1", @@ -3631,16 +3716,16 @@ }, { "name": "nesbot/carbon", - "version": "2.72.6", + "version": "2.73.0", "source": { "type": "git", "url": "https://github.com/CarbonPHP/carbon.git", - "reference": "1e9d50601e7035a4c61441a208cb5bed73e108c5" + "reference": "9228ce90e1035ff2f0db84b40ec2e023ed802075" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/1e9d50601e7035a4c61441a208cb5bed73e108c5", - "reference": "1e9d50601e7035a4c61441a208cb5bed73e108c5", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/9228ce90e1035ff2f0db84b40ec2e023ed802075", + "reference": "9228ce90e1035ff2f0db84b40ec2e023ed802075", "shasum": "" }, "require": { @@ -3734,7 +3819,7 @@ "type": "tidelift" } ], - "time": "2024-12-27T09:28:11+00:00" + "time": "2025-01-08T20:10:23+00:00" }, { "name": "nojimage/twitter-text-php", @@ -3932,19 +4017,20 @@ }, { "name": "phpoffice/phpspreadsheet", - "version": "1.29.8", + "version": "1.29.10", "source": { "type": "git", "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", - "reference": "089ffdfc04b5fcf25a3503d81a4e589f247e20e3" + "reference": "c80041b1628c4f18030407134fe88303661d4e4e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/089ffdfc04b5fcf25a3503d81a4e589f247e20e3", - "reference": "089ffdfc04b5fcf25a3503d81a4e589f247e20e3", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/c80041b1628c4f18030407134fe88303661d4e4e", + "reference": "c80041b1628c4f18030407134fe88303661d4e4e", "shasum": "" }, "require": { + "composer/pcre": "^1||^2||^3", "ext-ctype": "*", "ext-dom": "*", "ext-fileinfo": "*", @@ -4031,9 +4117,9 @@ ], "support": { "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", - "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.29.8" + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.29.10" }, - "time": "2025-01-12T03:16:27+00:00" + "time": "2025-02-08T02:56:14+00:00" }, { "name": "phpstan/phpdoc-parser", @@ -7091,77 +7177,6 @@ }, "time": "2022-03-30T09:27:43+00:00" }, - { - "name": "composer/pcre", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/composer/pcre.git", - "reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/67a32d7d6f9f560b726ab25a061b38ff3a80c560", - "reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.3", - "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Pcre\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "PCRE wrapping library that offers type-safe preg_* replacements.", - "keywords": [ - "PCRE", - "preg", - "regex", - "regular expression" - ], - "support": { - "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/1.0.1" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2022-01-21T20:24:37+00:00" - }, { "name": "composer/semver", "version": "3.4.3", @@ -7536,16 +7551,16 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.1", + "version": "2.1.5", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "cd6e973e04b4c2b94c86e8612b5a65f0da0e08e7" + "reference": "451b17f9665481ee502adc39be987cb71067ece2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/cd6e973e04b4c2b94c86e8612b5a65f0da0e08e7", - "reference": "cd6e973e04b4c2b94c86e8612b5a65f0da0e08e7", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/451b17f9665481ee502adc39be987cb71067ece2", + "reference": "451b17f9665481ee502adc39be987cb71067ece2", "shasum": "" }, "require": { @@ -7590,25 +7605,25 @@ "type": "github" } ], - "time": "2025-01-05T16:43:48+00:00" + "time": "2025-02-13T12:49:56+00:00" }, { "name": "rector/rector", - "version": "2.0.7", + "version": "2.0.9", "source": { "type": "git", "url": "https://github.com/rectorphp/rector.git", - "reference": "e70d681f6a0c361a63e6825897cd97746436f015" + "reference": "4393230e478c0006795770fe74c223b5c64ed68c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rectorphp/rector/zipball/e70d681f6a0c361a63e6825897cd97746436f015", - "reference": "e70d681f6a0c361a63e6825897cd97746436f015", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/4393230e478c0006795770fe74c223b5c64ed68c", + "reference": "4393230e478c0006795770fe74c223b5c64ed68c", "shasum": "" }, "require": { "php": "^7.4|^8.0", - "phpstan/phpstan": "^2.1.1" + "phpstan/phpstan": "^2.1.3" }, "conflict": { "rector/rector-doctrine": "*", @@ -7641,7 +7656,7 @@ ], "support": { "issues": "https://github.com/rectorphp/rector/issues", - "source": "https://github.com/rectorphp/rector/tree/2.0.7" + "source": "https://github.com/rectorphp/rector/tree/2.0.9" }, "funding": [ { @@ -7649,7 +7664,7 @@ "type": "github" } ], - "time": "2025-01-19T09:41:28+00:00" + "time": "2025-02-10T08:14:01+00:00" }, { "name": "smalot/pdfparser", diff --git a/sources/AppBundle/Command/IndexMeetupsCommand.php b/sources/AppBundle/Command/IndexMeetupsCommand.php index ddbb340aa..ff9b3bfb9 100644 --- a/sources/AppBundle/Command/IndexMeetupsCommand.php +++ b/sources/AppBundle/Command/IndexMeetupsCommand.php @@ -4,8 +4,8 @@ namespace AppBundle\Command; -use AlgoliaSearch\AlgoliaException; -use AlgoliaSearch\Client; +use Algolia\AlgoliaSearch\Exceptions\AlgoliaException; +use Algolia\AlgoliaSearch\SearchClient; use AppBundle\Event\Model\Repository\MeetupRepository; use AppBundle\Indexation\Meetups\Runner; use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; @@ -35,8 +35,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int $container = $this->getContainer(); $ting = $this->getContainer()->get('ting'); - /** @var Client $algoliaClient */ - $algoliaClient = $container->get(Client::class); + /** @var SearchClient $algoliaClient */ + $algoliaClient = $container->get(SearchClient::class); $meetupRepository = $ting->get(MeetupRepository::class); if ($input->getOption('run-scraping')) { diff --git a/sources/AppBundle/Controller/Website/HomeController.php b/sources/AppBundle/Controller/Website/HomeController.php index a41b73f0d..12a6f766c 100644 --- a/sources/AppBundle/Controller/Website/HomeController.php +++ b/sources/AppBundle/Controller/Website/HomeController.php @@ -7,8 +7,9 @@ use Afup\Site\Corporate\Articles; use Afup\Site\Corporate\Branche; use Afup\Site\Corporate\Feuille; -use AlgoliaSearch\AlgoliaException; -use AlgoliaSearch\Client; +use Algolia\AlgoliaSearch\Exceptions\AlgoliaException; +use Algolia\AlgoliaSearch\SearchClient; +use AppBundle\Event\Model\Meetup; use AppBundle\Event\Model\Repository\TalkRepository; use AppBundle\Event\Model\Talk; use AppBundle\Twig\ViewRenderer; @@ -26,14 +27,14 @@ class HomeController extends AbstractController private LoggerInterface $logger; private RepositoryFactory $repositoryFactory; private AdapterInterface $traceableAdapter; - private Client $client; + private SearchClient $client; private bool $homeAlgoliaEnabled; public function __construct(ViewRenderer $view, LoggerInterface $logger, RepositoryFactory $repositoryFactory, AdapterInterface $traceableAdapter, - Client $client, + SearchClient $client, bool $homeAlgoliaEnabled) { $this->view = $view; @@ -65,18 +66,15 @@ public function display(): Response ]); } - /** - * @return Talk - */ - protected function getTalkOfTheDay() + protected function getTalkOfTheDay(): Talk { return $this->repositoryFactory->get(TalkRepository::class)->getTalkOfTheDay(new \DateTime()); } /** - * @return array + * @return array */ - protected function getLatestMeetups() + protected function getLatestMeetups(): array { if (!$this->homeAlgoliaEnabled) { return []; diff --git a/sources/AppBundle/Indexation/Meetups/Runner.php b/sources/AppBundle/Indexation/Meetups/Runner.php index be342933e..cea14d4af 100644 --- a/sources/AppBundle/Indexation/Meetups/Runner.php +++ b/sources/AppBundle/Indexation/Meetups/Runner.php @@ -4,9 +4,8 @@ namespace AppBundle\Indexation\Meetups; -use AlgoliaSearch\AlgoliaException; -use AlgoliaSearch\Client; -use AlgoliaSearch\Index; +use Algolia\AlgoliaSearch\SearchClient; +use Algolia\AlgoliaSearch\SearchIndex; use AppBundle\Antennes\AntennesCollection; use AppBundle\Event\Model\Meetup; use AppBundle\Event\Model\Repository\MeetupRepository; @@ -14,23 +13,19 @@ class Runner { - protected Client $algoliaClient; + protected SearchClient $algoliaClient; protected MeetupRepository $meetupRepository; protected Transformer $transformer; - public function __construct(Client $algoliaClient, MeetupRepository $meetupRepository) + public function __construct(SearchClient $algoliaClient, MeetupRepository $meetupRepository) { $this->algoliaClient = $algoliaClient; $this->meetupRepository = $meetupRepository; $this->transformer = new Transformer(new AntennesCollection()); } - /** - * - * @throws AlgoliaException - */ public function run(): void { $index = $this->initIndex(); @@ -39,17 +34,15 @@ public function run(): void $meetups = $this->getTransformedMeetupsFromDatabase(); - $index->clearIndex(); - $index->addObjects($meetups, 'meetup_id'); + $index->clearObjects(); + $index->saveObjects($meetups, [ + 'objectIDKey' => 'meetup_id' + ]); echo "Indexation des meetups terminée avec succès !\n"; } - /** - * @return Index - * @throws AlgoliaException - */ - protected function initIndex() + protected function initIndex(): SearchIndex { $index = $this->algoliaClient->initIndex('afup_meetups'); @@ -80,10 +73,9 @@ private function getTransformedMeetupsFromDatabase(): array } /** - * @param CollectionInterface $meetupsCollection * @return array */ - public function transformMeetupsForIndexation($meetupsCollection): array + public function transformMeetupsForIndexation(CollectionInterface $meetupsCollection): array { $meetupsArray = []; /** @var Meetup $meetup */ diff --git a/sources/AppBundle/Indexation/Talks/Runner.php b/sources/AppBundle/Indexation/Talks/Runner.php index afff5d417..69262ce79 100644 --- a/sources/AppBundle/Indexation/Talks/Runner.php +++ b/sources/AppBundle/Indexation/Talks/Runner.php @@ -4,8 +4,8 @@ namespace AppBundle\Indexation\Talks; -use AlgoliaSearch\Client; -use AlgoliaSearch\Index; +use Algolia\AlgoliaSearch\SearchClient; +use Algolia\AlgoliaSearch\SearchIndex; use AppBundle\Event\Model\Planning; use AppBundle\Event\Model\Repository\EventRepository; use AppBundle\Event\Model\Repository\PlanningRepository; @@ -15,22 +15,19 @@ class Runner { - protected Client $algoliaClient; + protected SearchClient $algoliaClient; protected RepositoryFactory $ting; protected Transformer $transformer; - public function __construct(Client $algoliaClient, RepositoryFactory $ting) + public function __construct(SearchClient $algoliaClient, RepositoryFactory $ting) { $this->algoliaClient = $algoliaClient; $this->ting = $ting; $this->transformer = new Transformer(); } - /** - * - */ public function run(): void { $index = $this->initIndex(); @@ -45,14 +42,13 @@ public function run(): void $objects[] = $object; } - $index->clearIndex(); - $index->addObjects($objects, 'planning_id'); + $index->clearObjects(); + $index->saveObjects($objects, [ + 'objectIDKey' => 'planning_id' + ]); } - /** - * @return Index - */ - protected function initIndex() + protected function initIndex(): SearchIndex { $index = $this->algoliaClient->initIndex('afup_talks'); From 6923905d7cf1ad790eda030b895297d8ecff505b Mon Sep 17 00:00:00 2001 From: Nathan Boiron Date: Sun, 16 Feb 2025 19:32:43 +0100 Subject: [PATCH 05/13] Correction du type pour les IDs (#1633) --- htdocs/pages/administration/compta_journal.php | 8 ++++---- sources/Afup/Comptabilite/Comptabilite.php | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/htdocs/pages/administration/compta_journal.php b/htdocs/pages/administration/compta_journal.php index bdcee51b7..3222975c3 100755 --- a/htdocs/pages/administration/compta_journal.php +++ b/htdocs/pages/administration/compta_journal.php @@ -73,7 +73,7 @@ $formulaire = instancierFormulaire(); if ($action === 'modifier') { - $champsRecup = $compta->obtenir($_GET['id']); + $champsRecup = $compta->obtenir((int) $_GET['id']); $champs['idcompte'] = $champsRecup['idcompte']; $champs['date_saisie'] = $champsRecup['date_ecriture']; @@ -373,7 +373,7 @@ elseif ($action === 'modifier_colonne') { try { // Bad request? - if (!isset($_POST['val']) || !isset($_GET['column']) || !isset($_GET['id']) || !($line = $compta->obtenir($_GET['id']))) { + if (!isset($_POST['val']) || !isset($_GET['column']) || !isset($_GET['id']) || !($line = $compta->obtenir((int) $_GET['id']))) { throw new Exception("Please verify parameters", 400); } @@ -458,7 +458,7 @@ elseif ($action === 'upload_attachment') { try { // Bad request? - if (!isset($_GET['id']) || !($line = $compta->obtenir($_GET['id']))) { + if (!isset($_GET['id']) || !($line = $compta->obtenir((int) $_GET['id']))) { throw new Exception("Please verify parameters", 400); } @@ -554,7 +554,7 @@ elseif ($action === 'download_attachment') { try { // Bad request? - if (!isset($_GET['id']) || !($line = $compta->obtenir($_GET['id']))) { + if (!isset($_GET['id']) || !($line = $compta->obtenir((int) $_GET['id']))) { throw new Exception("Please verify parameters", 400); } diff --git a/sources/Afup/Comptabilite/Comptabilite.php b/sources/Afup/Comptabilite/Comptabilite.php index fcf50d6e5..05579c060 100755 --- a/sources/Afup/Comptabilite/Comptabilite.php +++ b/sources/Afup/Comptabilite/Comptabilite.php @@ -628,7 +628,7 @@ public function modifierConfig(string $table, string $id, string $champ, $valeur return $this->_bdd->executer($requete); } - public function obtenir(string $id) + public function obtenir(int $id) { $requete = 'SELECT'; $requete .= ' * '; From a4017e1dd4bb3024b6426ba50c9b6cb79a875984 Mon Sep 17 00:00:00 2001 From: Albin Kester <83301974+stakovicz@users.noreply.github.com> Date: Mon, 17 Feb 2025 08:08:38 +0100 Subject: [PATCH 06/13] =?UTF-8?q?Suppression=20des=20d=C3=A9pr=C3=A9ciatio?= =?UTF-8?q?ns=20(#1631)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../views/event/ticket/ticket.html.twig | 4 +-- .../views/site/secondary_menu.html.twig | 4 ++- composer.json | 2 +- composer.lock | 28 ++++++++++--------- .../Association/Form/CompanyPublicProfile.php | 1 + .../AppBundle/Event/Speaker/SpeakerPage.php | 4 +-- sources/AppBundle/Planete/FeedFormData.php | 2 +- .../Security/TestGithubAuthenticator.php | 4 +-- .../Form/HotelReservationType.php | 2 +- 9 files changed, 28 insertions(+), 23 deletions(-) diff --git a/app/Resources/views/event/ticket/ticket.html.twig b/app/Resources/views/event/ticket/ticket.html.twig index 2b3e2a98f..e22076ead 100644 --- a/app/Resources/views/event/ticket/ticket.html.twig +++ b/app/Resources/views/event/ticket/ticket.html.twig @@ -5,7 +5,7 @@ {% form_theme ticketForm _self %} {% block form_errors %} - {% spaceless %} + {% apply spaceless %} {% if errors|length > 0 %}
    {% for error in errors %} @@ -13,7 +13,7 @@ {% endfor %}
{% endif %} - {% endspaceless %} + {% endapply %} {% endblock form_errors %} {% block metas %} diff --git a/app/Resources/views/site/secondary_menu.html.twig b/app/Resources/views/site/secondary_menu.html.twig index 23e3e45e2..25932fcea 100644 --- a/app/Resources/views/site/secondary_menu.html.twig +++ b/app/Resources/views/site/secondary_menu.html.twig @@ -1,6 +1,8 @@
- {% for item in menu if item.is_active %} + {% for item in menu %} + {% if item.is_active %} {{ item.nom }} + {% endif %} {% endfor %}
diff --git a/composer.json b/composer.json index fea7c4354..d92f4e399 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ "doctrine/dbal": "^2.5", "ekino/newrelic-bundle": "^1.4", "erusev/parsedown": "^1.6", - "excelwebzone/recaptcha-bundle": "1.5.13", + "excelwebzone/recaptcha-bundle": "^1.5", "ezyang/htmlpurifier": "^4.10", "friendsofpear/pear_exception": "0.0.*", "guzzlehttp/guzzle": "^6.5", diff --git a/composer.lock b/composer.lock index ce68a66b0..7c7ba66d4 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6f80c056e0d1c12771ebc7a6a3582164", + "content-hash": "48fe53083063b2b6e6bc0ab4d0a6501d", "packages": [ { "name": "algolia/algoliasearch-client-php", @@ -1961,28 +1961,30 @@ }, { "name": "excelwebzone/recaptcha-bundle", - "version": "v1.5.13", + "version": "v1.5.42", "source": { "type": "git", "url": "https://github.com/excelwebzone/EWZRecaptchaBundle.git", - "reference": "0ab877583961efac961d651fd3bcc3bfcf3d543c" + "reference": "c1728fab6dc1a880e1b76298c5092f2ca25dac8f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/excelwebzone/EWZRecaptchaBundle/zipball/0ab877583961efac961d651fd3bcc3bfcf3d543c", - "reference": "0ab877583961efac961d651fd3bcc3bfcf3d543c", + "url": "https://api.github.com/repos/excelwebzone/EWZRecaptchaBundle/zipball/c1728fab6dc1a880e1b76298c5092f2ca25dac8f", + "reference": "c1728fab6dc1a880e1b76298c5092f2ca25dac8f", "shasum": "" }, "require": { "google/recaptcha": "^1.1", - "php": ">=5.6 || ^7.0", - "symfony/form": "^2.8 || ^3.0 || ^4.0", - "symfony/framework-bundle": "^2.8 || ^3.0 || ^4.0", - "symfony/security-bundle": "^2.8 || ^3.0 || ^4.0", - "symfony/validator": "^2.8 || ^3.0 || ^4.0" + "php": "^7.1 || ^8.0", + "symfony/form": "^2.8 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "symfony/framework-bundle": "^2.8 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "symfony/security-bundle": "^2.8 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "symfony/validator": "^2.8 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "symfony/yaml": "^2.8 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "twig/twig": "^1.40 || ^2.9 || ^3.0" }, "require-dev": { - "phpunit/phpunit": "^5 || ^6 || ^7" + "phpunit/phpunit": "^7 || ^8 || ^9.5" }, "type": "symfony-bundle", "extra": { @@ -2013,9 +2015,9 @@ ], "support": { "issues": "https://github.com/excelwebzone/EWZRecaptchaBundle/issues", - "source": "https://github.com/excelwebzone/EWZRecaptchaBundle/tree/master" + "source": "https://github.com/excelwebzone/EWZRecaptchaBundle/tree/v1.5.42" }, - "time": "2019-01-21T16:57:28+00:00" + "time": "2025-02-03T20:57:06+00:00" }, { "name": "ezyang/htmlpurifier", diff --git a/sources/AppBundle/Association/Form/CompanyPublicProfile.php b/sources/AppBundle/Association/Form/CompanyPublicProfile.php index f28bd48b1..38c1e71c9 100644 --- a/sources/AppBundle/Association/Form/CompanyPublicProfile.php +++ b/sources/AppBundle/Association/Form/CompanyPublicProfile.php @@ -142,6 +142,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void new Choice([ 'choices' => array_values($antennesInfos), 'multiple' => true, + 'strict' => true, ]), ] ] diff --git a/sources/AppBundle/Event/Speaker/SpeakerPage.php b/sources/AppBundle/Event/Speaker/SpeakerPage.php index 8c43cc753..32fd3bc95 100644 --- a/sources/AppBundle/Event/Speaker/SpeakerPage.php +++ b/sources/AppBundle/Event/Speaker/SpeakerPage.php @@ -85,7 +85,7 @@ public function handleRequest(Request $request, Event $event, Speaker $speaker) $shouldDisplaySpeakersDinerForm = $event->getSpeakersDinerEnabled() && $event->getDateEndSpeakersDinerInfosCollection() > $now; - if ($shouldDisplaySpeakersDinerForm && $speakersDinerType->isValid()) { + if ($shouldDisplaySpeakersDinerForm && $speakersDinerType->isSubmitted() && $speakersDinerType->isValid()) { $speakersDinerData = $speakersDinerType->getData(); $speaker->setWillAttendSpeakersDiner($speakersDinerData['will_attend'] === 1); $speaker->setHasSpecialDiet($speakersDinerData['has_special_diet'] === 1); @@ -111,7 +111,7 @@ public function handleRequest(Request $request, Event $event, Speaker $speaker) $shouldDisplayHotelReservationForm = $event->getAccomodationEnabled() && $event->getDateEndHotelInfosCollection() > $now; - if ($shouldDisplayHotelReservationForm && $hotelReservationType->isValid()) { + if ($shouldDisplayHotelReservationForm && $hotelReservationType->isSubmitted() && $hotelReservationType->isValid()) { $hotelReservationData = $hotelReservationType->getData(); $speaker->setHotelNightsArray($hotelReservationData['nights']); diff --git a/sources/AppBundle/Planete/FeedFormData.php b/sources/AppBundle/Planete/FeedFormData.php index cf7e3de83..ee4ad6cdc 100644 --- a/sources/AppBundle/Planete/FeedFormData.php +++ b/sources/AppBundle/Planete/FeedFormData.php @@ -32,7 +32,7 @@ class FeedFormData public $userId; /** * @var int - * @Assert\Choice({0, 1}) + * @Assert\Choice(choices={0, 1}, strict=true) */ public $status = Feed::STATUS_ACTIVE; } diff --git a/sources/AppBundle/Security/TestGithubAuthenticator.php b/sources/AppBundle/Security/TestGithubAuthenticator.php index da6b5e249..539153ab2 100644 --- a/sources/AppBundle/Security/TestGithubAuthenticator.php +++ b/sources/AppBundle/Security/TestGithubAuthenticator.php @@ -24,7 +24,7 @@ public function __construct(GithubUserRepository $githubUserRepository) $this->githubUserRepository = $githubUserRepository; } - public function getCredentials(Request $request) + public function getCredentials(Request $request): string { return $request->get('github_test_user'); } @@ -118,6 +118,6 @@ public function start(Request $request, AuthenticationException $authException = public function supports(Request $request): bool { - return true; + return (bool) $request->get('github_test_user'); } } diff --git a/sources/AppBundle/SpeakerInfos/Form/HotelReservationType.php b/sources/AppBundle/SpeakerInfos/Form/HotelReservationType.php index 1433eefe7..e05524982 100644 --- a/sources/AppBundle/SpeakerInfos/Form/HotelReservationType.php +++ b/sources/AppBundle/SpeakerInfos/Form/HotelReservationType.php @@ -67,7 +67,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'multiple' => true, 'choices' => $choices, 'constraints' => [ - new Choice(['choices' => array_values($choices), 'multiple' => true, 'min' => 1]), + new Choice(['choices' => array_values($choices), 'multiple' => true, 'min' => 1, 'strict' => true]), new Callback(['callback' => function ($values, ExecutionContextInterface $context): void { if (in_array(self::NIGHT_NONE, $values) && 1 !== count($values)) { From c8b88c976647d337145a5b12f39eee752dcad04d Mon Sep 17 00:00:00 2001 From: Albin Kester <83301974+stakovicz@users.noreply.github.com> Date: Mon, 17 Feb 2025 20:04:32 +0100 Subject: [PATCH 07/13] Upgrade Smarty 5.4 (#1632) Upgrade Smarty 5.4 --- composer.json | 2 +- composer.lock | 52 +++++++++++++------ .../administration/compta_balance.html | 6 +-- .../administration/compta_banque.html | 4 +- .../administration/compta_bilan.html | 10 ++-- .../administration/compta_journal.html | 14 ++--- .../administration/forum_facturation.html | 6 +-- .../administration/forum_inscriptions.html | 22 ++++---- .../administration/forum_sessions.html | 6 +-- .../administration/personnes_morales.html | 6 +-- .../administration/site_articles.html | 6 +-- sources/Afup/Bootstrap/Http.php | 14 ++--- .../AppBundle/Controller/LegacyController.php | 8 ++- 13 files changed, 91 insertions(+), 65 deletions(-) diff --git a/composer.json b/composer.json index d92f4e399..2c7eb2eb3 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,7 @@ "robmorgan/phinx": "^0.9.2", "sabre/vobject": "^4.1", "setasign/fpdf": "^1.8", - "smarty/smarty": "2.6.*", + "smarty/smarty": "^5.4", "symfony/monolog-bundle": "*", "symfony/phpunit-bridge": "^3.1", "symfony/symfony": "^3.4", diff --git a/composer.lock b/composer.lock index 7c7ba66d4..04b508a7d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "48fe53083063b2b6e6bc0ab4d0a6501d", + "content-hash": "944cf166c0d3508556fbd3190eff658b", "packages": [ { "name": "algolia/algoliasearch-client-php", @@ -5149,28 +5149,39 @@ }, { "name": "smarty/smarty", - "version": "v2.6.33", + "version": "v5.4.3", "source": { "type": "git", "url": "https://github.com/smarty-php/smarty.git", - "reference": "533fa2abe308dcc009c7bdab977d530b32408346" + "reference": "c6bff5795081ca5e60aabda59fb87daa511acd1e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/smarty-php/smarty/zipball/533fa2abe308dcc009c7bdab977d530b32408346", - "reference": "533fa2abe308dcc009c7bdab977d530b32408346", + "url": "https://api.github.com/repos/smarty-php/smarty/zipball/c6bff5795081ca5e60aabda59fb87daa511acd1e", + "reference": "c6bff5795081ca5e60aabda59fb87daa511acd1e", "shasum": "" }, "require": { - "php": ">=5.2" + "php": "^7.2 || ^8.0", + "symfony/polyfill-mbstring": "^1.27" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^7.5", + "smarty/smarty-lexer": "^4.0.2" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0.x-dev" + } + }, "autoload": { - "classmap": [ - "libs/Smarty.class.php", - "libs/Smarty_Compiler.class.php", - "libs/Config_File.class.php" - ] + "files": [ + "src/functions.php" + ], + "psr-4": { + "Smarty\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -5184,20 +5195,27 @@ { "name": "Uwe Tews", "email": "uwe.tews@googlemail.com" + }, + { + "name": "Rodney Rehm", + "email": "rodney.rehm@medialize.de" + }, + { + "name": "Simon Wisselink", + "homepage": "https://www.iwink.nl/" } ], "description": "Smarty - the compiling PHP template engine", - "homepage": "http://www.smarty.net", + "homepage": "https://smarty-php.github.io/smarty/", "keywords": [ "templating" ], "support": { - "forum": "http://www.smarty.net/forums/", - "irc": "irc://irc.freenode.org/smarty", - "issues": "http://code.google.com/p/smarty-php/issues/list", - "source": "https://github.com/smarty-php/smarty/tree/v2.6.33" + "forum": "https://github.com/smarty-php/smarty/discussions", + "issues": "https://github.com/smarty-php/smarty/issues", + "source": "https://github.com/smarty-php/smarty/tree/v5.4.3" }, - "time": "2021-10-23T12:46:52+00:00" + "time": "2024-12-23T00:38:44+00:00" }, { "name": "symfony/monolog-bundle", diff --git a/htdocs/templates/administration/compta_balance.html b/htdocs/templates/administration/compta_balance.html index 1112bc547..e014c3529 100644 --- a/htdocs/templates/administration/compta_balance.html +++ b/htdocs/templates/administration/compta_balance.html @@ -35,14 +35,14 @@

Balance comptable

{$ecriture.debit|floatval|number_format:2:',':' '} {$ecriture.credit|floatval|number_format:2:',':' '} - - Balance comptable {elseif $action == 'view'}