/** * 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; }