/**
  * @param string $operationId
  * @return string
  * @throws \Exception
  */
 public function execute($operationId)
 {
     /** @var NewsItem $newsItem */
     $newsItem = $this->em->getRepository('CampaignChainOperationLinkedInBundle:NewsItem')->findOneByOperation($operationId);
     if (!$newsItem) {
         throw new \Exception('No news item found for an operation with ID: ' . $operationId);
     }
     //have images?
     $images = $this->em->getRepository('CampaignChainHookImageBundle:Image')->getImagesForOperation($newsItem->getOperation());
     // if the message does not contain a url, we need to skip the content block
     if (is_null($newsItem->getLinkUrl())) {
         // LinkedIn accepts an image link only if we also provided a submitted URL.
         if ($images) {
             throw new \Exception('To include an image, you must also provide a URL in the text message.');
             return self::STATUS_WARNING;
         }
         $content = ['comment' => $newsItem->getMessage(), 'visibility' => ['code' => 'anyone']];
     } else {
         /*
          * process urls and add tracking
          * important: both the urls in the message and submitted url field must be identical
          */
         $newsItem->setLinkUrl($this->ctaService->processCTAs($newsItem->getLinkUrl(), $newsItem->getOperation())->getContent());
         $newsItem->setMessage($this->ctaService->processCTAs($newsItem->getMessage(), $newsItem->getOperation())->getContent());
         $content = ['comment' => $newsItem->getMessage(), 'content' => ['title' => $newsItem->getLinkTitle(), 'description' => $newsItem->getLinkDescription(), 'submitted-url' => $newsItem->getLinkUrl()], 'visibility' => ['code' => 'anyone']];
         if ($images) {
             //Linkedin can handle only 1 image
             $content['content']['submitted-image-url'] = $this->cacheManager->getBrowserPath($images[0]->getPath(), "campaignchain_linkedin_news_item");
         }
     }
     $activity = $newsItem->getOperation()->getActivity();
     $locationModuleIdentifier = $activity->getLocation()->getLocationModule()->getIdentifier();
     $isCompanyPageShare = 'campaignchain-linkedin-page' == $locationModuleIdentifier;
     $connection = $this->client->getConnectionByActivity($activity);
     if ($isCompanyPageShare) {
         $response = $connection->shareOnCompanyPage($activity, $content);
     } else {
         $response = $connection->shareOnUserPage($content);
     }
     $newsItem->setUrl($response['updateUrl']);
     $newsItem->setUpdateKey($response['updateKey']);
     // Set Operation to closed.
     $newsItem->getOperation()->setStatus(Action::STATUS_CLOSED);
     $location = $newsItem->getOperation()->getLocations()[0];
     $location->setIdentifier($response['updateKey']);
     $location->setUrl($response['updateUrl']);
     $location->setName($newsItem->getOperation()->getName());
     $location->setStatus(Medium::STATUS_ACTIVE);
     // Schedule data collection for report
     $this->reportShareNewsItem->schedule($newsItem->getOperation());
     $this->em->flush();
     $this->message = 'The message "' . $newsItem->getMessage() . '" with the ID "' . $newsItem->getUpdateKey() . '" has been posted on LinkedIn. See it on LinkedIn: <a href="' . $newsItem->getUrl() . '">' . $newsItem->getUrl() . '</a>';
     return self::STATUS_OK;
 }
 public function execute($operationId)
 {
     /** @var Status $status */
     $status = $this->em->getRepository('CampaignChainOperationTwitterBundle:Status')->findOneByOperation($operationId);
     if (!$status) {
         throw new \Exception('No status message found for an operation with ID: ' . $operationId);
     }
     // Check whether the message can be posted in the Location.
     $isExecutable = $this->validator->isExecutableByLocation($status, $status->getOperation()->getStartDate());
     if ($isExecutable['status'] == false) {
         throw new JobException($isExecutable['message'], ErrorCode::OPERATION_NOT_EXECUTABLE_IN_LOCATION);
     }
     /*
      * If it is a campaign or parent campaign with an interval (e.g.
      * repeating campaign), we make sure that every URL will be shortened to
      * avoid a duplicate status message error.
      */
     $options = array();
     if ($status->getOperation()->getActivity()->getCampaign()->getInterval() || $status->getOperation()->getActivity()->getCampaign()->getParent() && $status->getOperation()->getActivity()->getCampaign()->getParent()->getInterval()) {
         $options['shorten_all_unique'] = true;
     }
     /*
      * Process URLs in message and save the new message text, now including
      * the replaced URLs with the Tracking ID attached for call to action tracking.
      */
     $status->setMessage($this->cta->processCTAs($status->getMessage(), $status->getOperation(), $options)->getContent());
     /** @var Client $connection */
     $connection = $this->client->connectByActivity($status->getOperation()->getActivity());
     $params['status'] = $status->getMessage();
     //have images?
     $images = $this->em->getRepository('CampaignChainHookImageBundle:Image')->getImagesForOperation($status->getOperation());
     $mediaIds = [];
     if ($images) {
         foreach ($images as $image) {
             $streamPath = 'gaufrette://images/' . $image->getPath();
             $imageRequest = $connection->post('https://upload.twitter.com/1.1/media/upload.json', null, ['media_data' => base64_encode(file_get_contents($streamPath))]);
             try {
                 $response = $imageRequest->send()->json();
                 $mediaIds[] = $response['media_id'];
             } catch (\Exception $e) {
             }
         }
         if ($mediaIds) {
             $params['media_ids'] = implode(',', $mediaIds);
         }
     }
     /*
      * @TODO
      *
      * If there are URLs in the tweet, they have been shortened. Thus, we'll
      * pass the expanded URLs as entities in the API call, so that Twitter
      * can display them when hovering the mouse on a short URL.
      */
     $request = $connection->post('statuses/update.json', null, $params);
     $response = $request->send()->json();
     // TODO
     // If status code is 403, this means that the same tweet with identical content already exists
     // This should be checked upon creation of tweet (same with FB!)
     // Set URL to published status message on Facebook
     $statusURL = 'https://twitter.com/' . $response['user']['screen_name'] . '/status/' . $response['id_str'];
     $status->setUrl($statusURL);
     $status->setIdStr($response['id_str']);
     // Set Operation to closed.
     $status->getOperation()->setStatus(Action::STATUS_CLOSED);
     $location = $status->getOperation()->getLocations()[0];
     $location->setIdentifier($response['id_str']);
     $location->setUrl($statusURL);
     $location->setName($status->getOperation()->getName());
     $location->setStatus(Medium::STATUS_ACTIVE);
     // Schedule data collection for report
     $this->report->schedule($status->getOperation());
     $this->em->flush();
     $this->message = 'The message "' . $response['text'] . '" with the ID "' . $response['id_str'] . '" has been posted on Twitter. See it on Twitter: <a href="' . $statusURL . '">' . $statusURL . '</a>';
     return self::STATUS_OK;
     //            }
     //            else {
     //                // Handle errors, if authentication did not work.
     //                // 1) Check if App is installed.
     //                // 2) check if access token is valid and retrieve new access token if necessary.
     //                // Log error, send email, prompt user, ask to check App Key and Secret or to authenticate again
     //            }
 }
 /**
  * @param  string $operationId
  * @return string
  * @throws \Exception
  */
 public function execute($operationId)
 {
     /** @var StatusBase $status */
     $status = $this->em->getRepository('CampaignChainOperationFacebookBundle:StatusBase')->findOneByOperation($operationId);
     if (!$status) {
         throw new \Exception('No Facebook status found for an operation with ID: ' . $operationId);
     }
     // Check whether the message can be posted in the Location.
     $isExecutable = $this->validator->isExecutableByLocation($status, $status->getOperation()->getStartDate());
     if ($isExecutable['status'] == false) {
         throw new JobException($isExecutable['message'], ErrorCode::OPERATION_NOT_EXECUTABLE_IN_LOCATION);
     }
     /*
      * If it is a campaign or parent campaign with an interval (e.g.
      * repeating campaign), we make sure that every URL will be shortened to
      * avoid a duplicate status message error.
      */
     $options = array();
     if ($status->getOperation()->getActivity()->getCampaign()->getInterval() || $status->getOperation()->getActivity()->getCampaign()->getParent() && $status->getOperation()->getActivity()->getCampaign()->getParent()->getInterval()) {
         $options['shorten_all_unique'] = true;
     }
     /*
      * Process URLs in message and save the new message text, now including
      * the replaced URLs with the Tracking ID attached for call to action
      * tracking.
      */
     $status->setMessage($this->cta->processCTAs($status->getMessage(), $status->getOperation(), $options)->getContent());
     /** @var \Facebook $connection */
     $connection = $this->client->connectByActivity($status->getOperation()->getActivity());
     if (!$connection) {
         throw new JobException('Cannot connect to Facebook REST API for Location "' . $status->getOperation()->getActivity()->getLocation()->getUrl() . '"', ErrorCode::CONNECTION_TO_REST_API_FAILED);
     }
     $paramsMsg = array();
     /*
      * If an image was attached, we'll first upload the photo to Facebook
      * and then use the Facebook object ID of the picture in the message.
      */
     $images = $this->em->getRepository('CampaignChainHookImageBundle:Image')->getImagesForOperation($status->getOperation());
     if ($images) {
         $paramsImg = array();
         $paramsImg['caption'] = $status->getMessage();
         // Avoid that feed shows "... added a new photo" entry automtically.
         $paramsImg['no_story'] = 1;
         //Facebook handles only 1 image
         $paramsImg['url'] = $this->cacheManager->getBrowserPath($images[0]->getPath(), "campaignchain_facebook_photo");
         try {
             $responseImg = $connection->api('/' . $status->getFacebookLocation()->getIdentifier() . '/photos', 'POST', $paramsImg);
             $paramsMsg['object_attachment'] = $responseImg['id'];
         } catch (\Exception $e) {
             throw new ExternalApiException($e->getMessage() . '. Parameters of REST API call: ' . json_encode($paramsImg), $e->getCode(), $e);
         }
     }
     if ($status instanceof UserStatus) {
         $privacy = array('value' => $status->getPrivacy());
         $paramsMsg['privacy'] = json_encode($privacy);
     }
     $paramsMsg['message'] = $status->getMessage();
     try {
         $responseMsg = $connection->api('/' . $status->getFacebookLocation()->getIdentifier() . '/feed', 'POST', $paramsMsg);
     } catch (\Exception $e) {
         throw new ExternalApiException($e->getMessage() . '. Parameters of REST API call: ' . json_encode($paramsMsg), $e->getCode(), $e);
     }
     $connection->destroySession();
     // Set URL to published status message on Facebook
     $statusURL = 'https://www.facebook.com/' . str_replace('_', '/posts/', $responseMsg['id']);
     $status->setUrl($statusURL);
     $status->setPostId($responseMsg['id']);
     // Set Operation to closed.
     $status->getOperation()->setStatus(Action::STATUS_CLOSED);
     $location = $status->getOperation()->getLocations()[0];
     $location->setIdentifier($responseMsg['id']);
     $location->setUrl($statusURL);
     $location->setName($status->getOperation()->getName());
     $location->setStatus(Medium::STATUS_ACTIVE);
     // Schedule data collection for report
     $this->report->schedule($status->getOperation());
     $this->em->flush();
     $this->message = 'The message "' . $paramsMsg['message'] . '" with the ID "' . $responseMsg['id'] . '" has been posted on Facebook';
     if ($status instanceof UserStatus) {
         $this->message .= ' with privacy setting "' . $privacy['value'] . '"';
     }
     $this->message .= '. See it on Facebook: <a href="' . $statusURL . '">' . $statusURL . '</a>';
     return self::STATUS_OK;
 }