/** * @param Token $newToken * @param bool $sameToken * @return bool|string */ public function setToken(Token $newToken, $sameToken = false) { if ($newToken->getLocation()) { $this->em->persist($newToken); $this->em->flush(); return true; } // Check whether the token already exists in relation to a channel. $repository = $this->em->getRepository('CampaignChainSecurityAuthenticationClientOAuthBundle:Token'); $query = $repository->createQueryBuilder('token')->where('token.application = :application')->andWhere('token.accessToken = :accessToken')->andWhere('token.location IS NOT NULL')->setParameter('application', $newToken->getApplication())->setParameter('accessToken', $newToken->getAccessToken())->setMaxResults(1)->getQuery(); $oldToken = $query->getOneOrNullResult(); if (!$oldToken) { // So, there's no token related to a specific channel, but perhaps there is one // that has been persisted at a previous attempt to connect to the channel? // TODO: Implement what to do if token was persisted previously without channel relationship? $this->token = $newToken; $this->em->persist($this->token); $this->em->flush(); return self::STATUS_NEW_TOKEN; } // Has a scope been set? if (!$newToken->getScope() && !$sameToken) { return self::STATUS_NO_CHANGE; } //With LinkedIn if a user connects multiple times, he will get the same access token //And even though they are same, it has to be saved as a new token if ($sameToken) { $this->token = $newToken; $this->em->persist($this->token); $this->em->flush(); return self::STATUS_NEW_TOKEN; } $newScope = $newToken->getScope(); $newAccessToken = $newToken->getAccessToken(); $existingScope = $oldToken->getScope(); $existingAccessToken = $oldToken->getAccessToken(); // If the channel has the same access token and the same scope, // or no scope has been defined, then we're done. if ($existingScope === $newScope) { return self::STATUS_SAME_SCOPE; } // Is the scope different for the same profile? if ($existingAccessToken !== $newAccessToken) { // If the channel has a different scope and access token, // then create a new token entry for the existing profile. // This takes care of how Google handles scopes for its APIs. $this->token = $newToken; $status = self::STATUS_NEW_TOKEN; } else { // If the channel has the same access token, but a different scope, // then just update the scope for the token. // This takes care of how Facebook deals with scope changes. $this->token = $oldToken; $this->token->setScope($newScope); $status = self::STATUS_NEW_SCOPE; } $this->em->persist($this->token); $this->em->flush(); return $status; }
/** * @param $resourceOwner * @param $applicationInfo * @param bool $sameToken * * @throws \Exception */ public function authenticate($resourceOwner, $applicationInfo, $sameToken = false) { // Get application credentials $oauthApp = $this->container->get('campaignchain.security.authentication.client.oauth.application'); $application = $oauthApp->getApplication($resourceOwner); $bundleParams = $this->container->getParameter('campaignchain_security_authentication_client_oauth'); if (isset($_SERVER['HTTPS']) && (!empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != 'off')) { $hostScheme = 'https://'; } else { $hostScheme = 'http://'; } $config = array("base_url" => $hostScheme . $_SERVER['HTTP_HOST'] . $this->container->get('router')->generate('campaignchain_security_authentication_client_oauth_login'), "providers" => array($resourceOwner => array("enabled" => true, "keys" => array($applicationInfo['key_labels'][0] => $application->getKey(), $applicationInfo['secret_labels'][0] => $application->getSecret()))), "debug_mode" => $bundleParams['debug_mode'], "debug_file" => $bundleParams['debug_file']); $config['providers'][$resourceOwner] = array_merge($config['providers'][$resourceOwner], $applicationInfo['parameters']); if (isset($applicationInfo['wrapper'])) { $config['providers'][$resourceOwner]['wrapper']['class'] = $applicationInfo['wrapper']['class']; $config['providers'][$resourceOwner]['wrapper']['path'] = $this->container->get('kernel')->getRootDir() . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $applicationInfo['wrapper']['path']); } try { $hybridauth = new Hybrid_Auth($config); $resource = $hybridauth->authenticate($resourceOwner); $this->profile = $resource->getUserProfile(); $accessToken = $resource->getAccessToken(); $hybridauth->logoutAllProviders(); $token = new Token(); $token->setApplication($application); $token->setAccessToken($accessToken["access_token"]); $token->setTokenSecret($accessToken["access_token_secret"]); $token->setRefreshToken($accessToken["refresh_token"]); $token->setExpiresIn($accessToken["expires_in"]); $token->setExpiresAt($accessToken["expires_at"]); if (isset($resource->adapter->api->api_base_url)) { $token->setEndpoint($resource->adapter->api->api_base_url); } if (isset($applicationInfo['parameters']['scope'])) { $token->setScope($applicationInfo['parameters']['scope']); } $this->oauthToken = $this->container->get('campaignchain.security.authentication.client.oauth.token'); return $this->oauthToken->setToken($token, $sameToken); } catch (\Exception $e) { throw new ExternalApiException($e->getMessage(), $e->getCode(), $e); } }
/** * Return a connection based on the suplied Token * * @param Token $token * * @return LinkedInClientService */ public function getConnectionByToken(Token $token) { $application = $this->oauthApp->getApplication(self::RESOURCE_OWNER); $connection = $this->connect($application->getKey(), $application->getSecret(), $token->getAccessToken(), $token->getTokenSecret()); return new LinkedInClientService($connection); }
/** * @return Location[] */ public function getParsedLocationsFromLinkedIn() { $channel = $this->channelWizard->getChannel(); $profile = $this->channelWizard->get('profile'); $locations = []; $locationName = $profile->displayName; if (!empty($profile->username)) { $locationName .= ' (' . $profile->username . ')'; } // Get the location module for the user stream. $locationModuleUser = $this->locationService->getLocationModule('campaignchain/location-linkedin', 'campaignchain-linkedin-user'); // Create the location instance for the user stream. $locationUser = new Location(); $locationUser->setIdentifier($profile->identifier); $locationUser->setName($locationName); $locationUser->setLocationModule($locationModuleUser); if (!$profile->photoURL || strlen($profile->photoURL) == 0) { $locationUser->setImage($this->assetsHelper->getUrl('/bundles/campaignchainchannellinkedin/ghost_person.png')); } else { $locationUser->setImage($profile->photoURL); } $locationUser->setChannel($channel); $locationModuleUser->addLocation($locationUser); $locations[$profile->identifier] = $locationUser; $tokens = $this->channelWizard->get('tokens'); /** @var Token $userToken */ $userToken = array_values($tokens)[0]; $connection = $this->client->getConnectionByToken($userToken); $companies = $connection->getCompanies(); //there is only a user page if (empty($companies)) { return $locations; } // Get the location module for the page stream. $locationModulePage = $this->locationService->getLocationModule('campaignchain/location-linkedin', 'campaignchain-linkedin-page'); $wizardPages = []; foreach ($companies as $company) { $newToken = new Token(); $newToken->setAccessToken($userToken->getAccessToken()); $newToken->setApplication($userToken->getApplication()); $newToken->setTokenSecret($userToken->getTokenSecret()); $tokens[$company['id']] = $newToken; $this->channelWizard->set('tokens', $tokens); $companyData = $connection->getCompanyProfile($company['id']); $locationPage = new Location(); $locationPage->setChannel($channel); $locationPage->setName($companyData['name']); $locationPage->setIdentifier($companyData['id']); if (isset($companyData['squareLogoUrl'])) { $locationPage->setImage($companyData['squareLogoUrl']); } else { $locationPage->setImage($this->assetsHelper->getUrl('/bundles/campaignchainchannellinkedin/ghost_person.png')); } $locationPage->setLocationModule($locationModulePage); $locationModulePage->addLocation($locationPage); $locations[$companyData['id']] = $locationPage; $wizardPages[$companyData['id']] = $companyData; } $this->channelWizard->set('pagesData', $wizardPages); return $locations; }
public function addLocationAction(Request $request) { $wizard = $this->get('campaignchain.core.channel.wizard'); $channel = $wizard->getChannel(); $profile = $wizard->get('profile'); $locations = []; $locationName = $profile->displayName; $username = $profile->username; if (!empty($username)) { $locationName .= ' (' . $username . ')'; } // Get the location module for the user stream. $locationService = $this->get('campaignchain.core.location'); $locationModuleUser = $locationService->getLocationModule('campaignchain/location-facebook', 'campaignchain-facebook-user'); // Create the location instance for the user stream. $locationUser = new Location(); $locationUser->setChannel($channel); $locationUser->setName($locationName); $locationUser->setIdentifier($profile->identifier); $locationUser->setImage($profile->photoURL); $locationUser->setLocationModule($locationModuleUser); $locationModuleUser->addLocation($locationUser); $locations[$profile->identifier] = $locationUser; // Connect to Facebook to retrieve pages related to the user. $tokens = $wizard->get('tokens'); $client = $this->container->get('campaignchain.channel.facebook.rest.client'); $connection = $client->connect($tokens[$profile->identifier]->getAccessToken()); if ($connection) { // TODO: Check whether user has manage_pages permission with /me/permissions // check if the user owns Facebook pages $response = $connection->api('/me/accounts'); $pagesData = $response['data']; if (is_array($pagesData) && count($pagesData)) { // TODO: Should we check whether the Facebook page has actually been published (through is_published), because if not, then posting to it won't make sense? Same with can_post and perms from /me/accounts? // Get the location module for the page stream. $locationModulePage = $locationService->getLocationModule('campaignchain/location-facebook', 'campaignchain-facebook-page'); // User owns pages, so let's build the form and ask him whether to create channels for each of them // with the respective channel name foreach ($pagesData as $pageData) { // Save the token in the Wizard. $tokens = $wizard->get('tokens'); $newToken = new Token(); $newToken->setAccessToken($pageData['access_token']); $application = $tokens[$wizard->get('facebook_user_id')]->getApplication(); $newToken->setApplication($application); $tokens[$pageData['id']] = $newToken; $wizard->set('tokens', $tokens); // Get the page picture $pageConnection = $client->connect($pageData['access_token']); $pagePicture = $pageConnection->api('/' . $pageData['id'] . '/picture', 'GET', ['redirect' => false, 'type' => 'large']); $pageData['picture_url'] = $pagePicture['data']['url']; // Create the location instance for the page stream. $locationPage = new Location(); $locationPage->setChannel($channel); $locationPage->setName($pageData['name']); $locationPage->setIdentifier($pageData['id']); $locationPage->setImage($pageData['picture_url']); $locationPage->setLocationModule($locationModulePage); $locationModulePage->addLocation($locationPage); $locations[$pageData['id']] = $locationPage; $wizardPages[$pageData['id']] = $pageData; } $wizard->set('pagesData', $wizardPages); } } $data = []; $form = $this->createFormBuilder($data); foreach ($locations as $identifier => $location) { // Has the page already been added as a location? $repository = $this->getDoctrine()->getRepository('CampaignChainCoreBundle:Location'); $pageExists = $repository->findOneBy(['identifier' => $identifier, 'locationModule' => $location->getLocationModule()]); // Compose the checkbox form field. $form->add($identifier, CheckboxType::class, ['label' => '<img class="campaignchain-location-image-input-prepend" src="' . $location->getImage() . '"> ' . $location->getName(), 'required' => false, 'data' => true, 'mapped' => false, 'disabled' => $pageExists, 'attr' => ['align_with_widget' => true]]); // If a location has already been added before, remove it from this process. // TODO: Also assign existing locations to the new FB user. if ($pageExists) { unset($locations[$identifier]); } } $form = $form->getForm(); $form->handleRequest($request); if ($form->isValid()) { // Find out which locations should be added, i.e. which respective checkbox is active. foreach ($locations as $identifier => $location) { if (!$form->get($identifier)->getData()) { unset($locations[$identifier]); $wizard->removeLocation($identifier); } } // If there's at least one location to be added, then have the user configure it. if (is_array($locations) && count($locations)) { $wizard->setLocations($locations); return $this->redirectToRoute('campaignchain_channel_facebook_location_configure', ['step' => 0]); } else { $this->addFlash('warning', 'No new location has been added.'); return $this->redirectToRoute('campaignchain_core_location'); } } return $this->render('CampaignChainCoreBundle:Base:new.html.twig', ['page_title' => 'Add Facebook Locations', 'form' => $form->createView()]); }