X-Git-Url: https://git.rapsys.eu/airbundle/blobdiff_plain/e7de7e74b5caea1e25f4f4dbfa7cdef81c28f3a7..4f1511980043fca288c364b794e540af1c54f95e:/Command/CalendarCommand.php diff --git a/Command/CalendarCommand.php b/Command/CalendarCommand.php new file mode 100644 index 0000000..25c45ca --- /dev/null +++ b/Command/CalendarCommand.php @@ -0,0 +1,492 @@ +config = $container->getParameter($this->getAlias()); + + //Retrieve locale + $this->locale = $container->getParameter('kernel.default_locale'); + + //Store doctrine + $this->doctrine = $doctrine; + + //Store router + $this->router = $router; + + //Get router context + $context = $this->router->getContext(); + + //Set host + $context->setHost('airlibre.eu'); + + //Set scheme + $context->setScheme('https'); + + //Set the translator + $this->translator = $translator; + } + + ///Configure attribute command + protected function configure() { + //Configure the class + $this + //Set name + ->setName('rapsysair:calendar') + //Set description shown with bin/console list + ->setDescription('Synchronize sessions in calendar') + //Set description shown with bin/console --help airlibre:attribute + ->setHelp('This command synchronize sessions in google calendar'); + } + + ///Process the attribution + protected function execute(InputInterface $input, OutputInterface $output) { + //Compute period + $period = new \DatePeriod( + //Start from last week + new \DateTime('-1 week'), + //Iterate on each day + new \DateInterval('P1D'), + //End with next 2 week + new \DateTime('+2 week') + ); + + //Retrieve events to update + $sessions = $this->doctrine->getRepository(Session::class)->fetchAllByDatePeriod($period, $this->locale); + + //Markdown converted instance + $markdown = new DefaultMarkdown; + + //Retrieve cache object + //XXX: by default stored in /tmp/symfony-cache/@/W/3/6SEhFfeIW4UMDlAII+Dg + //XXX: stored in %kernel.project_dir%/var/cache/airlibre/0/P/IA20X0K4dkMd9-+Ohp9Q + $cache = new FilesystemAdapter($this->config['cache']['namespace'], $this->config['cache']['lifetime'], $this->config['cache']['directory']); + + //Retrieve calendars + $cacheCalendars = $cache->getItem('calendars'); + + //Without calendars + if (!$cacheCalendars->isHit()) { + //Return failure + return self::FAILURE; + } + + //Retrieve calendars + $calendars = $cacheCalendars->get(); + + //XXX: calendars content + #var_export($calendars); + + //Check expired token + foreach($calendars as $clientId => $client) { + //Get google client + $googleClient = new \Google\Client(['application_name' => $client['project'], 'client_id' => $clientId, 'client_secret' => $client['secret'], 'redirect_uri' => $client['redirect']]); + + //Iterate on each tokens + foreach($client['tokens'] as $tokenId => $token) { + //Set token + $googleClient->setAccessToken( + [ + 'access_token' => $tokenId, + 'refresh_token' => $token['refresh'], + 'expires_in' => $token['expire'], + 'scope' => $token['scope'], + 'token_type' => $token['type'], + 'created' => $token['created'] + ] + ); + + //With expired token + if ($exp = $googleClient->isAccessTokenExpired()) { + //Refresh token + if ($googleClient->getRefreshToken()) { + //Retrieve refreshed token + $googleToken = $googleClient->fetchAccessTokenWithRefreshToken($googleClient->getRefreshToken()); + + //Add refreshed token + $calendars[$clientId]['tokens'][$googleToken['access_token']] = [ + 'calendar' => $token['calendar'], + 'prefix' => $token['prefix'], + 'refresh' => $googleToken['refresh_token'], + 'expire' => $googleToken['expires_in'], + 'scope' => $googleToken['scope'], + 'type' => $googleToken['token_type'], + 'created' => $googleToken['created'] + ]; + + //Remove old token + unset($calendars[$clientId]['tokens'][$tokenId]); + } else { + //Drop token + unset($calendars[$clientId]['tokens'][$tokenId]); + + //Without tokens + if (empty($calendars[$clientId]['tokens'])) { + //Drop client + unset($calendars[$clientId]); + } + + //Drop token and report + echo 'Token '.$tokenId.' for calendar '.$token['calendar'].' has expired and is not refreshable'."\n"; + + //Return failure + //XXX: we want that mail and stop here + return self::FAILURE; + } + } + } + } + + //Save calendars + $cacheCalendars->set($calendars); + + //Save calendar + $cache->save($cacheCalendars); + + //Iterate on each calendar client + foreach($calendars as $clientId => $client) { + //Get google client + $googleClient = new \Google\Client(['application_name' => $client['project'], 'client_id' => $clientId, 'client_secret' => $client['secret'], 'redirect_uri' => $client['redirect']]); + + //Iterate on each tokens + foreach($client['tokens'] as $tokenId => $token) { + //Set token + $googleClient->setAccessToken( + [ + 'access_token' => $tokenId, + 'refresh_token' => $token['refresh'], + 'expires_in' => $token['expire'], + 'scope' => $token['scope'], + 'token_type' => $token['type'], + 'created' => $token['created'] + ] + ); + + //With expired token + if ($exp = $googleClient->isAccessTokenExpired()) { + //Last chance to skip this run + continue; + } + + //Get google calendar + $googleCalendar = new \Google\Service\Calendar($googleClient); + + //Retrieve calendar + try { + $calendar = $googleCalendar->calendars->get($token['calendar']); + //Catch exception + } catch(\Google\Service\Exception $e) { + //Display exception + //TODO: handle codes here https://developers.google.com/calendar/api/guides/errors + echo 'Exception '.$e->getCode().':'.$e->getMessage().' in '.$e->getFile().' +'.$e->getLine()."\n"; + echo $e->getTraceAsString()."\n"; + + //Return failure + return self::FAILURE; + } + + //Init events + $events = []; + + //Set filters + $filters = [ + //XXX: show even deleted event to be able to update them + 'showDeleted' => true, + //TODO: fetch events one day before and one day after to avoid triggering double insert duplicate key 409 errors :=) on google + 'timeMin' => $period->getStartDate()->format(\DateTime::ISO8601), + 'timeMax' => $period->getEndDate()->format(\DateTime::ISO8601) + /*, 'iCalUID' => 'airlibre/?????'*//*'orderBy' => 'startTime', */ + ]; + + //Retrieve event collection + $googleEvents = $googleCalendar->events->listEvents($token['calendar'], $filters); + + //Iterate until reached end + while (true) { + //Iterate on each event + foreach ($googleEvents->getItems() as $event) { + //Store event by id + if (preg_match('/^'.$token['prefix'].'([0-9]+)$/', $id = $event->getId(), $matches)) { + $events[$matches[1]] = $event; + //XXX: 3rd party events with id not matching prefix are skipped + #} else { + # echo 'Skipping '.$event->getId().':'.$event->getSummary()."\n";*/ + } + } + + //Get page token + $pageToken = $googleEvents->getNextPageToken(); + + //Handle next page + if ($pageToken) { + //Replace collection with next one + $googleEvents = $service->events->listEvents($token['calendar'], $filters+['pageToken' => $pageToken]); + } else { + break; + } + } + + //Iterate on each session to sync + foreach($sessions as $sessionId => $session) { + //Init shared properties + //TODO: validate for constraints here ??? https://developers.google.com/calendar/api/guides/extended-properties + $shared = [ + 'gps' => $session['l_latitude'].','.$session['l_longitude'] + ]; + + //Init source + $source = [ + 'title' => $this->translator->trans('Session %id% by %pseudonym%', ['%id%' => $sessionId, '%pseudonym%' => $session['au_pseudonym']]).' '.$this->translator->trans('at '.$session['l_title']), + 'url' => $this->router->generate('rapsys_air_session_view', ['id' => $sessionId], UrlGeneratorInterface::ABSOLUTE_URL) + ]; + + //Init description + #$description = '
'.$session['p_class'].'
'.$session['p_contact'].'
'.$session['p_donate'].'
'.$session['p_link'].'
'.$session['p_profile'].'