1 <?php 
declare(strict_types
=1); 
   4  * This file is part of the Rapsys AirBundle package. 
   6  * (c) Raphaël Gertz <symfony@rapsys.eu> 
   8  * For the full copyright and license information, please view the LICENSE 
   9  * file that was distributed with this source code. 
  12 namespace Rapsys\AirBundle\Controller
; 
  14 use Symfony\Contracts\Cache\ItemInterface
; 
  15 use Symfony\Component\HttpFoundation\Request
; 
  16 use Symfony\Component\HttpFoundation\Response
; 
  17 use Symfony\Component\Routing\Generator\UrlGeneratorInterface
; 
  19 use Rapsys\UserBundle\Controller\UserController 
as BaseUserController
; 
  21 use Rapsys\AirBundle\Entity\Dance
; 
  22 use Rapsys\AirBundle\Entity\GoogleCalendar
; 
  23 use Rapsys\AirBundle\Entity\GoogleToken
; 
  24 use Rapsys\AirBundle\Entity\User
; 
  29 class UserController 
extends BaseUserController 
{ 
  31          * Set google client scopes 
  33         const googleScopes 
= [\Google\Service\Calendar
::CALENDAR_EVENTS
, \Google\Service\Calendar
::CALENDAR
, \Google\Service\Oauth2
::USERINFO_EMAIL
]; 
  38         public function edit(Request 
$request, string $hash, string $mail): Response 
{ 
  40                 if ($hash != $this->slugger
->hash($mail)) { 
  42                         throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash])); 
  46                 $mail = $this->slugger
->unshort($smail = $mail); 
  48                 //With existing subscriber 
  49                 if (empty($user = $this->doctrine
->getRepository($this->config
['class']['user'])->findOneByMail($mail))) { 
  51                         //XXX: prevent slugger reverse engineering by not displaying decoded mail 
  52                         throw $this->createNotFoundException($this->translator
->trans('Unable to find account %mail%', ['%mail%' => $smail])); 
  55                 //Prevent access when not admin, user is not guest and not currently logged user 
  56                 if (!$this->checker
->isGranted('ROLE_ADMIN') && $user != $this->security
->getUser() || !$this->checker
->isGranted('IS_AUTHENTICATED_FULLY')) { 
  58                         //XXX: prevent slugger reverse engineering by not displaying decoded mail 
  59                         throw $this->createAccessDeniedException($this->translator
->trans('Unable to access user: %mail%', ['%mail%' => $smail])); 
  62                 //Create the RegisterType form and give the proper parameters 
  63                 $edit = $this->factory
->create($this->config
['edit']['view']['edit'], $user, [ 
  64                         //Set action to register route name and context 
  65                         'action' => $this->generateUrl($this->config
['route']['edit']['name'], ['mail' => $smail, 'hash' => $hash]+
$this->config
['route']['edit']['context']), 
  67                         'civility_class' => $this->config
['class']['civility'], 
  68                         //Set civility default 
  69                         'civility_default' => $this->doctrine
->getRepository($this->config
['class']['civility'])->findOneByTitle($this->config
['default']['civility']), 
  71                         'country_class' => $this->config
['class']['country'], 
  73                         'country_default' => $this->doctrine
->getRepository($this->config
['class']['country'])->findOneByTitle($this->config
['default']['country']), 
  74                         //Set country favorites 
  75                         'country_favorites' => $this->doctrine
->getRepository($this->config
['class']['country'])->findByTitle($this->config
['default']['country_favorites']), 
  77                         'dance' => $this->checker
->isGranted('ROLE_ADMIN'), 
  79                         'dance_choices' => $danceChoices = $this->doctrine
->getRepository($this->config
['class']['dance'])->findChoicesAsArray(), 
  81                         #'dance_default' => /*$this->doctrine->getRepository($this->config['class']['dance'])->findOneByNameType($this->config['default']['dance'])*/null, 
  83                         'dance_favorites' => $this->doctrine
->getRepository($this->config
['class']['dance'])->findIdByNameTypeAsArray($this->config
['default']['dance_favorites']), 
  85                         'subscription' => $this->checker
->isGranted('ROLE_ADMIN'), 
  86                         //Set subscription choices 
  87                         'subscription_choices' => $subscriptionChoices = $this->doctrine
->getRepository($this->config
['class']['user'])->findChoicesAsArray(), 
  88                         //Set subscription default 
  89                         #'subscription_default' => /*$this->doctrine->getRepository($this->config['class']['user'])->findOneByPseudonym($this->config['default']['subscription'])*/null, 
  90                         //Set subscription favorites 
  91                         'subscription_favorites' => $this->doctrine
->getRepository($this->config
['class']['user'])->findIdByPseudonymAsArray($this->config
['default']['subscription_favorites']), 
  93                         'mail' => $this->checker
->isGranted('ROLE_ADMIN'), 
  95                         'pseudonym' => $this->checker
->isGranted('ROLE_GUEST'), 
 100                 ]+
$this->config
['edit']['field']); 
 103                 if ($this->checker
->isGranted('ROLE_ADMIN')) { 
 104                         //Create the ResetType form and give the proper parameters 
 105                         $reset = $this->factory
->create($this->config
['edit']['view']['reset'], $user, [ 
 106                                 //Set action to register route name and context 
 107                                 'action' => $this->generateUrl($this->config
['route']['edit']['name'], ['mail' => $smail, 'hash' => $hash]+
$this->config
['route']['edit']['context']), 
 115                         if ($request->isMethod('POST')) { 
 116                                 //Refill the fields in case the form is not valid. 
 117                                 $reset->handleRequest($request); 
 119                                 //With reset submitted and valid 
 120                                 if ($reset->isSubmitted() && $reset->isValid()) { 
 122                                         $data = $reset->getData(); 
 125                                         $data->setPassword($this->hasher
->hashPassword($data, $data->getPassword())); 
 127                                         //Queue user password save 
 128                                         $this->manager
->persist($data); 
 130                                         //Flush to get the ids 
 131                                         $this->manager
->flush(); 
 134                                         $this->addFlash('notice', $this->translator
->trans('Account %mail% password updated', ['%mail%' => $mail])); 
 136                                         //Redirect to cleanup the form 
 137                                         return $this->redirectToRoute($this->config
['route']['edit']['name'], ['mail' => $smail, 'hash' => $hash]+
$this->config
['route']['edit']['context']); 
 142                         $this->config
['edit']['view']['context']['reset'] = $reset->createView(); 
 144                         //Add google calendar array 
 145                         $this->config
['edit']['view']['context']['calendar'] = [ 
 148                                 //Uri to link account 
 152                                         'png' => '@RapsysAir/png/calendar.png', 
 153                                         'svg' => '@RapsysAir/svg/calendar.svg' 
 158                         $googleClient = new \Google\
Client( 
 160                                         'application_name' => $request->server
->get('RAPSYSAIR_GOOGLE_PROJECT'), 
 161                                         'client_id' => $request->server
->get('RAPSYSAIR_GOOGLE_CLIENT'), 
 162                                         'client_secret' => $request->server
->get('RAPSYSAIR_GOOGLE_SECRET'), 
 163                                         'redirect_uri' => $this->generateUrl('rapsysair_google_callback', [], UrlGeneratorInterface
::ABSOLUTE_URL
), 
 164                                         'scopes' => self
::googleScopes
, 
 165                                         'access_type' => 'offline', 
 166                                         'login_hint' => $user->getMail(), 
 167                                         //XXX: see https://stackoverflow.com/questions/10827920/not-receiving-google-oauth-refresh-token 
 168                                         #'approval_prompt' => 'force' 
 169                                         'prompt' => 'consent' 
 174                         if (!($googleTokens = $user->getGoogleTokens())->isEmpty()) { 
 175                                 //Iterate on each google token 
 176                                 //XXX: either we finish with a valid token set or a logic exception after token removal 
 177                                 foreach($googleTokens as $googleToken) { 
 178                                         //Clear client cache before changing access token 
 179                                         //TODO: set a per token cache ? 
 180                                         $googleClient->getCache()->clear(); 
 183                                         $googleClient->setAccessToken( 
 185                                                         'access_token' => $googleToken->getAccess(), 
 186                                                         'refresh_token' => $googleToken->getRefresh(), 
 187                                                         'created' => $googleToken->getCreated()->getTimestamp(), 
 188                                                         'expires_in' => $googleToken->getExpired()->getTimestamp() - (new \
DateTime('now'))->getTimestamp(), 
 193                                         if ($googleClient->isAccessTokenExpired()) { 
 195                                                 if (($refresh = $googleClient->getRefreshToken()) && ($token = $googleClient->fetchAccessTokenWithRefreshToken($refresh)) && empty($token['error'])) { 
 197                                                         $googleToken->setAccess($token['access_token']); 
 200                                                         $googleToken->setExpired(new \
DateTime('+'.$token['expires_in'].' second')); 
 203                                                         $googleToken->setRefresh($token['refresh_token']); 
 205                                                         //Queue google token save 
 206                                                         $this->manager
->persist($googleToken); 
 208                                                         //Flush to get the ids 
 209                                                         $this->manager
->flush(); 
 212                                                         //Add error in flash message 
 215                                                                 $this->translator
->trans( 
 216                                                                         empty($token['error'])?'Unable to refresh token':'Unable to refresh token: %error%', 
 217                                                                         empty($token['error'])?[]:['%error%' => str_replace('_', ' ', $token['error'])] 
 224                                                         //Iterate on each google token calendars 
 225                                                         foreach($googleToken->getGoogleCalendars() as $googleCalendar) { 
 227                                                                 $cmails[] = $googleCalendar->getMail(); 
 229                                                                 //Remove google token calendar 
 230                                                                 $this->manager
->remove($googleCalendar); 
 233                                                         //Log unlinked google token infos 
 234                                                         $this->logger
->emergency( 
 235                                                                 $this->translator
->trans( 
 236                                                                         'expired: mail=%mail% gmail=%gmail% cmails=%cmails% locale=%locale%', 
 238                                                                                 '%mail%' => $googleToken->getUser()->getMail(), 
 239                                                                                 '%gmail%' => $googleToken->getMail(), 
 240                                                                                 '%cmails' => implode(',', $cmails), 
 241                                                                                 '%locale%' => $request->getLocale() 
 246                                                         //Remove google token 
 247                                                         $this->manager
->remove($googleToken); 
 250                                                         $this->manager
->flush(); 
 252                                                         //TODO: warn user by mail ? 
 259                                         //XXX: TODO: remove DEBUG 
 260                                         #$this->cache->delete('user.edit.calendar.'.$this->slugger->short($googleToken->getMail())); 
 265                                                 $calendars = $this->cache
->get( 
 266                                                         //Set key to user.edit.$mail 
 267                                                         ($calendarKey = 'user.edit.calendar.'.($googleShortMail = $this->slugger
->short($googleMail = $googleToken->getMail()))), 
 268                                                         //Fetch mail calendar list 
 269                                                         function (ItemInterface 
$item) use ($googleClient): array { 
 271                                                                 $item->expiresAfter(3600); 
 273                                                                 //Get google calendar service 
 274                                                                 $service = new \Google\Service\
Calendar($googleClient); 
 285                                                                 //Iterate until next page token is null 
 287                                                                         //Get token calendar list 
 288                                                                         //XXX: require permission to read and write events 
 289                                                                         $calendarList = $service->calendarList
->listCalendarList(['pageToken' => $pageToken, 'minAccessRole' => 'writer', 'showHidden' => true]); 
 292                                                                         foreach($calendarList->getItems() as $calendarItem) { 
 293                                                                                 //With primary calendar 
 294                                                                                 if ($calendarItem->getPrimary()) { 
 295                                                                                         //Add primary calendar 
 296                                                                                         //XXX: use primary as key as described in google api documentation 
 297                                                                                         $calendars = ['primary' => $this->translator
->trans('Primary') /*$calendarItem->getSummary()*/] + 
$calendars; 
 298                                                                                 //With secondary calendar 
 300                                                                                         //Add secondary calendar 
 301                                                                                         //XXX: Append counter to make sure summary is unique for later array_flip call 
 302                                                                                         $calendars +
= [$calendarItem->getId() => $calendarItem->getSummary().' ('.++
$count.')']; 
 305                                                                 } while ($pageToken = $calendarList->getNextPageToken()); 
 312                                         } catch(\Google\Service\Exception 
$e) { 
 314                                                 //XXX: see https://cloud.google.com/apis/design/errors 
 315                                                 if ($e->getCode() == 401 || $e->getCode() == 403) { 
 316                                                         //Add error in flash message 
 319                                                                 $this->translator
->trans( 
 320                                                                         'Unable to list calendars: %error%', 
 321                                                                         ['%error%' => $e->getMessage()] 
 328                                                         //Iterate on each google token calendars 
 329                                                         foreach($googleToken->getGoogleCalendars() as $googleCalendar) { 
 331                                                                 $cmails[] = $googleCalendar->getMail(); 
 333                                                                 //Remove google token calendar 
 334                                                                 $this->manager
->remove($googleCalendar); 
 337                                                         //Log unlinked google token infos 
 338                                                         $this->logger
->emergency( 
 339                                                                 $this->translator
->trans( 
 340                                                                         'denied: mail=%mail% gmail=%gmail% cmails=%cmails% locale=%locale%', 
 342                                                                                 '%mail%' => $googleToken->getUser()->getMail(), 
 343                                                                                 '%gmail%' => $googleToken->getMail(), 
 344                                                                                 '%cmails' => implode(',', $cmails), 
 345                                                                                 '%locale%' => $request->getLocale() 
 350                                                         //Remove google token 
 351                                                         $this->manager
->remove($googleToken); 
 354                                                         $this->manager
->flush(); 
 356                                                         //TODO: warn user by mail ? 
 363                                                 throw new \
LogicException('Calendar list failed', 0, $e); 
 367                                         $formData = ['calendar' => []]; 
 369                                         //With google calendars 
 370                                         if (!($googleCalendars = $googleToken->getGoogleCalendars())->isEmpty()) { 
 371                                                 //Iterate on each google calendars 
 372                                                 foreach($googleCalendars as $googleCalendar) { 
 373                                                         //With existing google calendar 
 374                                                         if (isset($calendars[$googleCalendar->getMail()])) { 
 375                                                                 //Add google calendar to form data 
 376                                                                 $formData['calendar'][] = $googleCalendar->getMail(); 
 378                                                                 //Remove google calendar from database 
 379                                                                 $this->manager
->remove($googleCalendar); 
 381                                                                 //Flush to persist ids 
 382                                                                 $this->manager
->flush(); 
 387                                         //TODO: add feature for alerts (-30min/-1h) ? 
 388                                         //TODO: [Direct link to calendar ?][Direct link to calendar settings ?][Alerts][Remove] 
 390                                         //Create the CalendarType form and give the proper parameters 
 391                                         $form = $this->factory
->createNamed('calendar_'.$googleShortMail, 'Rapsys\AirBundle\Form\CalendarType', $formData, [ 
 392                                                 //Set action to register route name and context 
 393                                                 'action' => $this->generateUrl($this->config
['route']['edit']['name'], ['mail' => $smail, 'hash' => $hash]+
$this->config
['route']['edit']['context']), 
 394                                                 //Set calendar choices 
 395                                                 //XXX: unique calendar summary required by choice widget is guaranteed by appending ' (x)' to secondary calendars earlier 
 396                                                 'calendar_choices' => array_flip($calendars), 
 402                                         if ($request->isMethod('POST')) { 
 403                                                 //Refill the fields in case the form is not valid. 
 404                                                 $form->handleRequest($request); 
 406                                                 //With reset submitted and valid 
 407                                                 if ($form->isSubmitted() && $form->isValid()) { 
 409                                                         $data = $form->getData(); 
 412                                                         if (($clicked = $form->getClickedButton()->getName()) == 'refresh') { 
 413                                                                 //Remove calendar key 
 414                                                                 $this->cache
->delete($calendarKey); 
 417                                                                 $this->addFlash('notice', $this->translator
->trans('Account %mail% calendars updated', ['%mail%' => $googleMail])); 
 419                                                         } elseif ($clicked == 'add') { 
 420                                                                 //Get google calendar service 
 421                                                                 $service = new \Google\Service\
Calendar($googleClient); 
 425                                                                         //Instantiate calendar 
 426                                                                         $calendar = new \Google\Service\Calendar\
Calendar( 
 428                                                                                         'summary' => $this->translator
->trans($this->config
['context']['site']['title']), 
 429                                                                                         'timeZone' => date_default_timezone_get() 
 434                                                                         $service->calendars
->insert($calendar); 
 436                                                                 } catch(\Google\Service\Exception 
$e) { 
 438                                                                         throw new \
LogicException('Calendar insert failed', 0, $e); 
 441                                                                 //Remove calendar key 
 442                                                                 $this->cache
->delete($calendarKey); 
 445                                                                 $this->addFlash('notice', $this->translator
->trans('Account %mail% calendar added', ['%mail%' => $googleMail])); 
 447                                                         } elseif ($clicked == 'delete') { 
 448                                                                 //Get google calendar service 
 449                                                                 $service = new \Google\Service\
Calendar($googleClient); 
 454                                                                         $siteTitle = $this->translator
->trans($this->config
['context']['site']['title']); 
 456                                                                         //Iterate on calendars 
 457                                                                         foreach($calendars as $calendarId => $calendarSummary) { 
 458                                                                                 //With calendar matching site title 
 459                                                                                 if (substr($calendarSummary, 0, strlen($siteTitle)) == $siteTitle) { 
 460                                                                                         //Delete the calendar 
 461                                                                                         $service->calendars
->delete($calendarId); 
 465                                                                 } catch(\Google\Service\Exception 
$e) { 
 467                                                                         throw new \
LogicException('Calendar delete failed', 0, $e); 
 470                                                                 //Remove calendar key 
 471                                                                 $this->cache
->delete($calendarKey); 
 474                                                                 $this->addFlash('notice', $this->translator
->trans('Account %mail% calendars deleted', ['%mail%' => $googleMail])); 
 476                                                         } elseif ($clicked == 'unlink') { 
 477                                                                 //Iterate on each google calendars 
 478                                                                 foreach($googleCalendars as $googleCalendar) { 
 479                                                                         //Remove google calendar from database 
 480                                                                         $this->manager
->remove($googleCalendar); 
 483                                                                 //Remove google token from database 
 484                                                                 $this->manager
->remove($googleToken); 
 487                                                                 $this->manager
->flush(); 
 489                                                                 //Revoke access token 
 490                                                                 $googleClient->revokeToken($googleToken->getAccess()); 
 493                                                                 if ($refresh = $googleToken->getRefresh()) { 
 494                                                                         //Revoke refresh token 
 495                                                                         $googleClient->revokeToken($googleToken->getRefresh()); 
 498                                                                 //Remove calendar key 
 499                                                                 $this->cache
->delete($calendarKey); 
 502                                                                 $this->addFlash('notice', $this->translator
->trans('Account %mail% calendars unlinked', ['%mail%' => $googleMail])); 
 505                                                                 //Flipped calendar data 
 506                                                                 $dataCalendarFlip = array_flip($data['calendar']); 
 508                                                                 //Iterate on each google calendars 
 509                                                                 foreach($googleCalendars as $googleCalendar) { 
 510                                                                         //Without calendar in flipped data 
 511                                                                         if (!isset($dataCalendarFlip[$googleCalendarMail = $googleCalendar->getMail()])) { 
 512                                                                                 //Remove google calendar from database 
 513                                                                                 $this->manager
->remove($googleCalendar); 
 514                                                                         //With calendar in flipped data 
 516                                                                                 //Remove google calendar from calendar data 
 517                                                                                 unset($data['calendar'][$dataCalendarFlip[$googleCalendarMail]]); 
 521                                                                 //Iterate on remaining calendar data 
 522                                                                 foreach($data['calendar'] as $googleCalendarMail) { 
 523                                                                         //Create new google calendar 
 524                                                                         //XXX: remove trailing ' (x)' from summary 
 525                                                                         $googleCalendar = new GoogleCalendar($googleToken, $googleCalendarMail, preg_replace('/ \([0-9]\)$/', '', $calendars[$googleCalendarMail])); 
 527                                                                         //Queue google calendar save 
 528                                                                         $this->manager
->persist($googleCalendar); 
 531                                                                 //Flush to persist ids 
 532                                                                 $this->manager
->flush(); 
 535                                                                 $this->addFlash('notice', $this->translator
->trans('Account %mail% calendars updated', ['%mail%' => $googleMail])); 
 538                                                         //Redirect to cleanup the form 
 539                                                         return $this->redirectToRoute($this->config
['route']['edit']['name'], ['mail' => $smail, 'hash' => $hash]+
$this->config
['route']['edit']['context']); 
 544                                         $this->config
['edit']['view']['context']['calendar']['form'][$googleToken->getMail()] = $form->createView(); 
 548                         //Add google calendar auth url 
 549                         $this->config
['edit']['view']['context']['calendar']['link'] = $googleClient->createAuthUrl(); 
 553                 if ($request->isMethod('POST')) { 
 554                         //Refill the fields in case the form is not valid. 
 555                         $edit->handleRequest($request); 
 557                         //With edit submitted and valid 
 558                         if ($edit->isSubmitted() && $edit->isValid()) { 
 560                                 $data = $edit->getData(); 
 563                                 $this->manager
->persist($data); 
 565                                 //Try saving in database 
 567                                         //Flush to get the ids 
 568                                         $this->manager
->flush(); 
 571                                         //XXX: get mail from data as it may change 
 572                                         $this->addFlash('notice', $this->translator
->trans('Account %mail% updated', ['%mail%' => $mail = $data->getMail()])); 
 574                                         //Redirect to cleanup the form 
 575                                         return $this->redirectToRoute($this->config
['route']['edit']['name'], ['mail' => $smail = $this->slugger
->short($mail), 'hash' => $this->slugger
->hash($smail)]+
$this->config
['route']['edit']['context']); 
 576                                 //Catch double slug or mail 
 577                                 } catch (UniqueConstraintViolationException 
$e) { 
 578                                         //Add error message mail already exists 
 579                                         $this->addFlash('error', $this->translator
->trans('Account %mail% already exists', ['%mail%' => $data->getMail()])); 
 583                 //XXX: prefer a reset on login to force user unspam action 
 584                 } elseif (!$this->checker
->isGranted('ROLE_ADMIN')) { 
 586                         $this->addFlash('notice', $this->translator
->trans('To change your password login with your mail and any password then follow the procedure')); 
 590                 return $this->render( 
 592                         $this->config
['edit']['view']['name'], 
 594                         ['edit' => $edit->createView(), 'sent' => $request->query
->get('sent', 0)]+
$this->config
['edit']['view']['context'] 
 599          * Handle google callback 
 601          * @param Request $request The request 
 602          * @return Response The response 
 604         public function googleCallback(Request 
$request): Response 
{ 
 606                 if (empty($code = $request->query
->get('code', ''))) { 
 607                         throw new \
InvalidArgumentException('Query parameter code is empty'); 
 611                 if (empty($user = $this->security
->getUser())) { 
 612                         throw new \
LogicException('User is empty'); 
 616                 $googleClient = new \Google\
Client( 
 618                                 'application_name' => $request->server
->get('RAPSYSAIR_GOOGLE_PROJECT'), 
 619                                 'client_id' => $request->server
->get('RAPSYSAIR_GOOGLE_CLIENT'), 
 620                                 'client_secret' => $request->server
->get('RAPSYSAIR_GOOGLE_SECRET'), 
 621                                 'redirect_uri' => $this->generateUrl('rapsysair_google_callback', [], UrlGeneratorInterface
::ABSOLUTE_URL
), 
 622                                 'scopes' => self
::googleScopes
, 
 623                                 'access_type' => 'offline', 
 624                                 'login_hint' => $user->getMail(), 
 625                                 #'approval_prompt' => 'force' 
 626                                 'prompt' => 'consent' 
 630                 //Protect to extract failure 
 632                         //Authenticate with code 
 633                         if (!empty($token = $googleClient->authenticate($code))) { 
 635                                 if (!empty($token['error'])) { 
 636                                         throw new \
LogicException('Client authenticate failed: '.str_replace('_', ' ', $token['error'])); 
 637                                 //Without refresh token 
 638                                 } elseif (empty($token['refresh_token'])) { 
 639                                         throw new \
LogicException('Refresh token is empty'); 
 641                                 } elseif (empty($token['expires_in'])) { 
 642                                         throw new \
LogicException('Expires in is empty'); 
 644                                 } elseif (empty($token['scope'])) { 
 645                                         throw new \
LogicException('Scope in is empty'); 
 646                                 //Without valid scope 
 647                                 } elseif (array_intersect(self
::googleScopes
, explode(' ', $token['scope'])) != self
::googleScopes
) { 
 648                                         throw new \
LogicException('Scope in is not valid'); 
 652                                 $oauth2 = new \Google\Service\
Oauth2($googleClient); 
 654                                 //Protect user info get call 
 657                                         $userInfo = $oauth2->userinfo
->get(); 
 659                                 } catch(\Google\Service\Exception 
$e) { 
 661                                         throw new \
LogicException('Userinfo get failed', 0, $e); 
 664                                 //With existing token 
 666                                         //If available retrieve google token with matching mail 
 667                                         $googleToken = array_reduce( 
 668                                                 $user->getGoogleTokens()->getValues(), 
 669                                                 function ($c, $i) use ($userInfo) { 
 670                                                         if ($i->getMail() == $userInfo['email']) { 
 677                                         //XXX: TODO: should already be set and not change, remove ? 
 678                                         //XXX: TODO: store picture as well ? 
 679                                         $googleToken->setMail($userInfo['email']); 
 682                                         $googleToken->setAccess($token['access_token']); 
 685                                         $googleToken->setExpired(new \
DateTime('+'.$token['expires_in'].' second')); 
 688                                         $googleToken->setRefresh($token['refresh_token']); 
 691                                         //XXX: TODO: store picture as well ? 
 692                                         $googleToken = new GoogleToken($user, $userInfo['email'], $token['access_token'], new \
DateTime('+'.$token['expires_in'].' second'), $token['refresh_token']); 
 695                                 //Queue google token save 
 696                                 $this->manager
->persist($googleToken); 
 698                                 //Flush to get the ids 
 699                                 $this->manager
->flush(); 
 702                                 $this->addFlash('notice', $this->translator
->trans('Account %mail% google token updated', ['%mail%' => $user->getMail()])); 
 703                         //With failed authenticate 
 705                                 throw new \
LogicException('Client authenticate failed'); 
 708                 } catch(\Exception 
$e) { 
 710                         $this->addFlash('error', $this->translator
->trans('Account %mail% google token rejected: %error%', ['%mail%' => $user->getMail(), '%error%' => $e->getMessage()])); 
 714                 return $this->redirectToRoute('rapsysuser_edit', ['mail' => $short = $this->slugger
->short($user->getMail()), 'hash' => $this->slugger
->hash($short)]);