diff --git a/packages/ChillZimbraBundle/src/Calendar/Connector/ZimbraConnector.php b/packages/ChillZimbraBundle/src/Calendar/Connector/ZimbraConnector.php new file mode 100644 index 000000000..8652253dc --- /dev/null +++ b/packages/ChillZimbraBundle/src/Calendar/Connector/ZimbraConnector.php @@ -0,0 +1,93 @@ +hasRemoteId()) { + $calItemId = ($this->createEvent)($calendar); + $this->logger->info(self::LOG_PREFIX.'Calendar synced with Zimbra', ['calendar_id' => $calendar->getId(), 'action' => $action, 'calItemId' => $calItemId]); + + $calendar->setRemoteId($calItemId); + + return; + } + + throw new \RuntimeException('Update of calendar: to be implemented'); + } + + public function syncCalendarRange(CalendarRange $calendarRange): void + { + if (!$calendarRange->hasRemoteId()) { + $calItemId = ($this->createEvent)($calendarRange); + $this->logger->info(self::LOG_PREFIX.'Calendar range synced with Zimbra', ['calendar_range_id' => $calendarRange->getId(), 'calItemId' => $calItemId]); + + $calendarRange->setRemoteId($calItemId); + return; + } + + throw new \RuntimeException('Update of calendar: to be implemented'); + } + + public function syncInvite(Invite $invite): void + { + // TODO: Implement syncInvite() method. + } +} diff --git a/packages/ChillZimbraBundle/src/Calendar/Connector/ZimbraConnector/CreateEvent.php b/packages/ChillZimbraBundle/src/Calendar/Connector/ZimbraConnector/CreateEvent.php new file mode 100644 index 000000000..956b1b052 --- /dev/null +++ b/packages/ChillZimbraBundle/src/Calendar/Connector/ZimbraConnector/CreateEvent.php @@ -0,0 +1,64 @@ +getMainUser()->getEmail(); + $organizerLang = $calendar->getMainUser()->getLocale(); + } else { + $organizerEmail = $calendar->getUser()->getEmail(); + $organizerLang = $calendar->getUser()->getLocale(); + } + + if (null === $organizerEmail) { + throw new CalendarWithoutMainUserException(); + } + + $api = $this->soapClientBuilder->getApiForAccount($organizerEmail); + + $comp = $this->createEvent->createZimbraInviteComponentFromCalendar($calendar); + + $inv = new InvitationInfo(); + $inv->setInviteComponent($comp); + + $mp = new MimePartInfo(); + $mp->addMimePart(new MimePartInfo('text/plain', $this->translator->trans('zimbra.event_created_by_chill', locale: $organizerLang))); + + $msg = new Msg(); + $msg->setSubject($this->translator->trans('zimbra.event_created_trough_soap', locale: $organizerLang)) + ->setFolderId('10') + ->setInvite($inv) + ->setMimePart($mp); + + $response = $api->createAppointment($msg, echo: true); + + return $response->getCalItemId(); + } +} diff --git a/packages/ChillZimbraBundle/src/Calendar/Connector/ZimbraConnector/CreateZimbraComponent.php b/packages/ChillZimbraBundle/src/Calendar/Connector/ZimbraConnector/CreateZimbraComponent.php new file mode 100644 index 000000000..74b9c4b42 --- /dev/null +++ b/packages/ChillZimbraBundle/src/Calendar/Connector/ZimbraConnector/CreateZimbraComponent.php @@ -0,0 +1,102 @@ +getPersons()->map(fn (Person $p) => $this->personRender->renderString($p, []))->toArray() + ); + + } else { + $subject = $this->translator->trans('remote_calendar.calendar_range_title'); + } + + $comp = new InviteComponent(); + $comp->setName($subject) + ->setFreeBusy(FreeBusyStatus::BUSY) + ->setStatus(InviteStatus::CONFIRMED) + ->setCalClass(InviteClass::PUB) + ->setTransparency(Transparency::OPAQUE) + ->setIsAllDay(false) + ->setIsDraft(false) + ->setDtStart($this->dateConverter->phpToZimbraDateTime($calendar->getStartDate())) + ->setDtEnd($this->dateConverter->phpToZimbraDateTime($calendar->getEndDate())); + + if ($calendar->hasLocation()) { + $comp + ->setLocation($this->createLocationString($calendar->getLocation())); + } + + return $comp; + } + + private function createLocationString(Location $location): string + { + $str = ''; + + if ('' !== ((string) $location->getName())) { + $str .= $location->getName(); + $str .= ', '; + } + + if ($location->hasAddress()) { + if ('' !== $str) { + $str .= ', '; + } + + $str .= $this->addressRender->renderString($location->getAddress(), []); + } + + return $str; + } +} diff --git a/packages/ChillZimbraBundle/src/Calendar/Connector/ZimbraConnector/DateConverter.php b/packages/ChillZimbraBundle/src/Calendar/Connector/ZimbraConnector/DateConverter.php new file mode 100644 index 000000000..1fdea5040 --- /dev/null +++ b/packages/ChillZimbraBundle/src/Calendar/Connector/ZimbraConnector/DateConverter.php @@ -0,0 +1,37 @@ +format(self::FORMAT_DATE_TIME), $date->getTimezone()->getName()); + } +} diff --git a/packages/ChillZimbraBundle/src/Calendar/Connector/ZimbraConnector/SoapClientBuilder.php b/packages/ChillZimbraBundle/src/Calendar/Connector/ZimbraConnector/SoapClientBuilder.php new file mode 100644 index 000000000..e66c45e85 --- /dev/null +++ b/packages/ChillZimbraBundle/src/Calendar/Connector/ZimbraConnector/SoapClientBuilder.php @@ -0,0 +1,78 @@ +parameterBag->get('chill_calendar.remote_calendar_dsn'); + $url = parse_url($dsn); + + $this->username = urldecode($url['user']); + $this->password = urldecode($url['pass']); + if ('zimbra+http' === $url['scheme']) { + $scheme = 'http://'; + $port = $url['port'] ?? 80; + } elseif ('zimbra+https' === $url['scheme']) { + $scheme = 'https://'; + $port = $url['port'] ?? 443; + } else { + throw new \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException('Unsupported remote calendar scheme: '.$url['scheme']); + } + + $this->url = $scheme.$url['host'].':'.$port; + } + + private function buildApi(): MailApi + { + $baseClient = $this->client->withOptions([ + 'base_uri' => $location = $this->url.'/service/soap', + 'verify_host' => false, + 'verify_peer' => false, + ]); + $psr18Client = new Psr18Client($baseClient); + $api = new MailApi(); + $client = ClientFactory::create($location, $psr18Client); + $api->setClient($client); + + return $api; + } + + public function getApiForAccount(string $accountName): MailApi + { + $api = $this->buildApi(); + $response = $api->authByAccountName($this->username, $this->password); + + $token = $response->getAuthToken(); + + $apiBy = $this->buildApi(); + $apiBy->setAuthToken($token); + $apiBy->setTargetAccount(new AccountInfo(AccountBy::NAME, $accountName)); + + return $apiBy; + } +} diff --git a/packages/ChillZimbraBundle/src/ChillZimbraBundle.php b/packages/ChillZimbraBundle/src/ChillZimbraBundle.php index 0e2690117..b49e7e8af 100644 --- a/packages/ChillZimbraBundle/src/ChillZimbraBundle.php +++ b/packages/ChillZimbraBundle/src/ChillZimbraBundle.php @@ -1,10 +1,16 @@ load('services.yaml'); } - } diff --git a/packages/ChillZimbraBundle/src/Exception/CalendarWithoutMainUserException.php b/packages/ChillZimbraBundle/src/Exception/CalendarWithoutMainUserException.php new file mode 100644 index 000000000..01314a62b --- /dev/null +++ b/packages/ChillZimbraBundle/src/Exception/CalendarWithoutMainUserException.php @@ -0,0 +1,14 @@ +location; + } + public function setLocation(?Location $location): self { $this->location = $location;