/**
  * Uses the Brightcove oAuth API to retrieve and store an access key for use with requests. The token is stored as a transient
  * with an expiration time matching that which is returned from Brightcove. The call to the API is only performed if that transient
  * is invalid or expired. Return a WP_Error object for use in WordPress in the case of failure.
  *
  * @since  1.0.0
  *
  * @see    BC_Utility::get_cache_item()
  * @see    set_transient()
  * @see    BC_Utility::delete_cache_item()
  * @see    wp_remote_post()
  *
  * @param bool $force_new_token whether or not to obtain a new OAuth token
  * @param bool $retry           true to retry on failure or false
  *
  * @return string|WP_Error
  */
 public function _request_access_token($force_new_token = false, $retry = true)
 {
     $transient_name = $this->transient_name;
     $token = $force_new_token ? false : BC_Utility::get_cache_item($transient_name);
     if (!$token) {
         $endpoint = esc_url_raw(self::ENDPOINT_BASE . '/access_token?grant_type=client_credentials');
         $request = wp_remote_post($endpoint, $this->_http_headers);
         if ('400' == wp_remote_retrieve_response_code($request)) {
             // Just in case
             BC_Utility::delete_cache_item($transient_name);
             $oauth_error = new WP_Error('oauth_access_token_failure', sprintf(__('There is a problem with your Brightcove %1$s or %2$s', 'brightcove'), '<code>client_id</code>', '<code>client_secret</code>'));
             BC_Logging::log(sprintf('BC OAUTH ERROR: %s', $oauth_error->get_error_message()));
             return $oauth_error;
         }
         $body = wp_remote_retrieve_body($request);
         $data = json_decode($body);
         if (isset($data->access_token)) {
             $token = $data->access_token;
             BC_Utility::set_cache_item($transient_name, 'oauth', $token, $data->expires_in);
         } else {
             if (!$retry) {
                 return new WP_Error('oauth_access_token_response_failure', sprintf(esc_html__('oAuth API did not return us an access token', 'brightcove')));
             }
             return $this->_request_access_token($force_new_token, false);
         }
     }
     return $token;
 }
 /**
  * Sends API requests to remote server
  *
  * Sends the request to the remote server using the appropriate method and
  * logs any errors in the event of failures.
  *
  * @param string  $url             the endpoint to connect to
  * @param string  $method          the http method to use
  * @param array   $data            array of further data to send to the server
  * @param boolean $force_new_token whether or not to force obtaining a new oAuth token
  *
  *
  * @return mixed the return data from the call of false if a failure occurred
  */
 protected function send_request($url, $method = 'GET', $data = array(), $force_new_token = false)
 {
     $method = strtoupper(sanitize_text_field($method));
     $allowed_methods = array('DELETE', 'GET', 'PATCH', 'POST', 'PUT', 'JSON_DELETE', 'JSON_POST');
     //only allow methods required by the brightcove APIs
     if (!in_array($method, $allowed_methods)) {
         return false;
     }
     $url = esc_url_raw($url);
     $transient_key = false;
     if ($method === "GET") {
         $hash = substr(BC_Utility::get_hash_for_object(array("url" => $url, "data" => $data)), 0, 20);
         $transient_key = "_bc_request_{$hash}";
         $cached_request = BC_Utility::get_cache_item($transient_key);
         if (false !== $cached_request) {
             return $cached_request;
         }
     }
     $auth_header = $this->get_authorization_header($force_new_token);
     if (is_wp_error($auth_header)) {
         return $auth_header;
     }
     add_filter('http_request_timeout', array($this, 'increase_http_timeout'));
     $headers = array('Authorization' => $auth_header);
     // All JSON_ methods are used to indicate that application/json is the content type
     if (false !== strpos($method, 'JSON')) {
         $headers['Content-type'] = 'application/json';
         $method = str_replace('JSON_', '', $method);
     } else {
         $headers['Content-type'] = 'application/x-www-form-urlencoded';
     }
     $args = array('headers' => $headers);
     switch ($method) {
         case 'GET':
             $request = $this->cached_get($url, $args);
             break;
         case 'POST':
             $args['body'] = wp_json_encode($data);
             $request = wp_remote_post($url, $args);
             break;
         default:
             $args['method'] = $method;
             $args['body'] = wp_json_encode($data);
             if (!empty($data)) {
                 $args['body'] = json_encode($data);
             }
             $request = wp_remote_request($url, $args);
             break;
     }
     if (401 === wp_remote_retrieve_response_code($request)) {
         if ("Unauthorized" === $request['response']['message']) {
             // Token may have expired, so before we give up, let's retry
             // the request with a fresh OAuth token.
             if (!$force_new_token) {
                 return $this->send_request($url, $method, $data, true);
             } else {
                 $this->errors[] = array('url' => $url, 'error' => new WP_Error('unauthorized-oauth', __('API says permission denied, check your client ID and client secret', 'brightcove')));
                 return false;
             }
         }
     }
     //log errors for further processing or return the body
     if (is_wp_error($request)) {
         $this->errors[] = array('url' => $url, 'error' => $request->get_error_message());
         BC_Logging::log(sprintf('WP_ERROR: %s', $request->get_error_message()));
         return false;
     }
     $body = json_decode(wp_remote_retrieve_body($request), true);
     $successful_response_codes = array(200, 201, 202, 204);
     if (!in_array(wp_remote_retrieve_response_code($request), $successful_response_codes)) {
         $message = esc_html__('An unspecified error has occurred.', 'brightcove');
         if (isset($body[0]) && isset($body[0]['error_code'])) {
             $message = $body[0]['error_code'];
         } elseif (isset($body['message'])) {
             $message = $body['message'];
         }
         $this->errors[] = array('url' => $url, 'error' => new WP_Error($request['response']['message'], $message));
         BC_Logging::log(sprintf('BC API ERROR: %s', $message));
         return false;
     }
     if ('204' == wp_remote_retrieve_response_code($request)) {
         return true;
     }
     if ($transient_key && $body && (!defined('WP_DEBUG') || false === WP_DEBUG)) {
         // Store body for 60s to prevent hammering the BC APIs.
         BC_Utility::set_cache_item($transient_key, 'api-request', $body, 60);
     }
     return $body;
 }
 /**
  * Fetches a list of media objects
  *
  * Fetches a list of media objects from the Brightcove api.
  *
  * @since 1.0
  *
  * @param string $type           The type of object to fetch.
  * @param int    $posts_per_page The number of posts to fetch.
  * @param int    $page           The current page (for paged queries).
  * @param string $query_string   Extra query parameters to use for listing.
  * @param string $sort_order     The field to sort by.
  *
  * @return array An array of media items.
  */
 public function fetch_all($type, $posts_per_page = 100, $page = 1, $query_string = '', $sort_order = 'updated_at')
 {
     global $bc_accounts;
     $request_identifier = "{$type}-{$posts_per_page}-{$query_string}-{$sort_order}";
     $transient_key = substr('_brightcove_req_all_' . BC_Utility::get_hash_for_object($request_identifier), 0, 45);
     $results = BC_Utility::get_cache_item($transient_key);
     $results = is_array($results) ? $results : array();
     $initial_page = 1;
     $accounts = $bc_accounts->get_sanitized_all_accounts();
     $account_ids = array();
     foreach ($accounts as $account) {
         $account_ids[] = $account['account_id'];
     }
     $account_ids = array_unique($account_ids);
     while (count($results) <= $page * $posts_per_page) {
         if (0 === count($account_ids)) {
             // No more vids to fetch.
             break;
         }
         foreach ($account_ids as $key => $account_id) {
             $bc_accounts->set_current_account_by_id($account_id);
             $account_results = $this->cms_api->video_list($posts_per_page, $posts_per_page * ($initial_page - 1), $query_string, sanitize_text_field($sort_order));
             // Not enough account results returned, so we assume there are no more results to fetch.
             if (count($account_results) < $posts_per_page) {
                 unset($account_ids[$key]);
             }
             if (is_array($account_results) && count($account_results) > 0) {
                 $results = array_merge($results, $account_results);
             } else {
                 unset($account_ids[$key]);
             }
         }
         $initial_page++;
     }
     BC_Utility::set_cache_item($transient_key, 'video_list', $results, 600);
     // High cache time due to high expense of fetching the data.
     $results = array_slice($results, $posts_per_page * ($page - 1), $posts_per_page);
     $bc_accounts->restore_default_account();
     return $results;
 }
 /**
  * Return a set of the most recent videos for the specified account.
  *
  * @param string $account_id
  * @param int $count
  *
  * @global BC_Accounts $bc_accounts
  *
  * @return array
  */
 protected function fetch_videos($account_id, $count = 10)
 {
     global $bc_accounts;
     $transient_key = substr('_brightcove_req_heartbeat_' . $account_id, 0, 45);
     $results = BC_Utility::get_cache_item($transient_key);
     $results = is_array($results) ? $results : array();
     if (empty($results)) {
         // Set up the account from which we're fetching data
         $account = $bc_accounts->set_current_account_by_id($account_id);
         if (false === $account) {
             // Account was invalid, fail
             // Restore our global, default account
             $bc_accounts->restore_default_account();
             return array();
         }
         // Get a list of videos
         $results = $this->cms_api->video_list($count, 0, '', 'updated_at');
         // Get a list of available custom fields
         $fields = $this->cms_api->video_fields();
         // Loop through results to remap items
         foreach ($results as &$result) {
             // Map the custom_fields array to a collection of objects with description, display name, id, etc
             $result['custom'] = $fields['custom_fields'];
             foreach ($result['custom_fields'] as $id => $value) {
                 // Extract the change tracking item explicitly
                 if ($id == '_change_history') {
                     $result['history'] = $value;
                     continue;
                 }
                 foreach ($result['custom'] as &$field) {
                     if ($field['id'] === $id) {
                         $field['value'] = $value;
                         break;
                     }
                 }
             }
             // Massage the text tracks
             $result['captions'] = array();
             foreach ($result['text_tracks'] as $caption) {
                 $result['captions'][] = array('source' => $caption['src'], 'language' => $caption['srclang'], 'label' => $caption['label']);
             }
         }
         $bc_accounts->restore_default_account();
         if (!empty($results)) {
             BC_Utility::set_cache_item($transient_key, 'video_list', $results, 600);
             // High cache time due to high expense of fetching the data.
         }
     }
     return $results;
 }