provider = 'instagram'; $this->oauth_version = '2.0'; $this->response_type = 'token'; $this->scope = 'basic'; $this->api_key = 'not-required-but-not-empty'; $this->api_secret = 'not-required-but-not-empty'; $this->token = $photonic_instagram_access_token; // Used in the Authentication page to see if a token is set $this->access_token = $photonic_instagram_access_token; // Used everywhere else. This is updated later based on the cached value in memory $this->field_list = 'id,caption,media_type,media_url,permalink,thumbnail_url,timestamp,username'; $this->link_lightbox_title = empty($photonic_instagram_disable_title_link); $this->doc_links = [ 'general' => 'https://aquoid.com/plugins/photonic/instagram/', ]; $this->authenticate($photonic_instagram_access_token); $this->cached_token = $this->get_cached_token(); if (!empty($this->cached_token)) { $this->access_token = $this->cached_token['oauth_token']; } } public static function get_instance() { if (self::$instance == null) { self::$instance = new Instagram(); } return self::$instance; } /** * Main function that fetches the images associated with the shortcode. * * @param array $attr * @param array $gallery_meta * @return string */ public function get_gallery_images($attr = [], &$gallery_meta = []) { global $photonic_instagram_main_size, $photonic_instagram_tile_size, $photonic_instagram_media, $photonic_thumbnail_style; $this->gallery_index++; $this->push_to_stack('Get Gallery Images'); $attr = array_merge( $this->common_parameters, [ // Common overrides ... 'caption' => 'title', 'thumb_size' => 75, 'main_size' => $photonic_instagram_main_size, 'tile_size' => $photonic_instagram_tile_size, // Instagram-specific ... 'count' => 25, 'layout' => (empty($photonic_thumbnail_style) || $photonic_thumbnail_style == 'square') ? 'random' : $photonic_thumbnail_style, 'media' => $photonic_instagram_media, 'embed_type' => 'embed', 'carousel_handling' => 'expand', ], $attr); if ($attr['tile_size'] == 'same') { $attr['tile_size'] = $attr['main_size']; } $attr = array_map('trim', $attr); extract($attr); if (empty($this->token) || empty($this->cached_token) || !$this->token_valid) { return $this->error(esc_html__("Instagram Access Token not valid. Please reauthenticate.", 'photonic')); } if (empty($user_id)) { $user_id = 'me'; } $base_url = 'https://graph.instagram.com/'; $display_what = 'media'; if (!empty($media_id)) {// Trumps all else. A single photo will be shown. $id_format = preg_match('/^[A-Z][A-Z0-9_-]+/i', $media_id) ? 'old' : 'new'; if ($id_format == 'old') { $query_url = 'http://api.instagram.com/oembed?url='.urlencode('http://instagram.com/p/'.$media_id.'/'); $attr['embed_type'] = 'embed'; } else { $query_url = $base_url.$media_id.'?fields='.$this->field_list; } $display_what = 'single-media'; } else if (!empty($carousel)) { $query_url = $base_url.$carousel.'/children?fields=id,media_type,media_url,permalink,thumbnail_url'; $display_what = 'carousel'; } else if (!empty($user_id)) { $query_url = $base_url.$user_id.'/media?fields='.$this->field_list; // Doesn't matter what the other values are. User's photos will be shown. } else { if (empty($view)) { return $this->error(sprintf(esc_html__('The %s parameter has to be defined.', 'photonic'), 'view')); } else { return $this->error(sprintf(esc_html__('Malformed shortcode. Either %1$s or %2$s or %3$s has to be defined.', 'photonic'), 'media_id', 'carousel', 'view')); } } if (isset($count)) { $query_url = add_query_arg(['limit' => $count], $query_url); } if (isset($after)) { $query_url = add_query_arg(['after' => $after], $query_url); } $ret = $this->make_call($query_url, $display_what, $attr); $this->pop_from_stack(); return $this->finalize_markup($ret, $attr).$this->get_stack_markup(); } protected function make_call($query_url, $display_what, &$shortcode_attr) { $this->push_to_stack("Make call $query_url"); $ret = ''; if (!empty($this->cached_token) && $this->token_valid) { $query = add_query_arg(['access_token' => $this->access_token], $query_url); } else { $this->pop_from_stack(); // Make call, error encountered return $this->error(esc_html__("Instagram Access Token not valid. Please reauthenticate.", 'photonic')); } $this->push_to_stack('Send request'); $response = wp_remote_request($query, [ 'sslverify' => PHOTONIC_SSL_VERIFY, ]); $this->pop_from_stack(); // Send request $this->push_to_stack('Process response'); if (!is_wp_error($response)) { if (isset($response['response']) && isset($response['response']['code'])) { if ($response['response']['code'] == 200) { $body = json_decode($response['body']); if (isset($body->paging) && isset($body->paging->next)&& isset($body->paging->cursors) && isset($body->paging->cursors->after)) { $shortcode_attr['after'] = $body->paging->cursors->after; if (empty($shortcode_attr['more'])) { $shortcode_attr['more'] = esc_html__('More', 'photonic'); } } else { if (isset($shortcode_attr['after'])) { unset($shortcode_attr['after']); } } if (isset($body->data) && $display_what != 'single-media') { $data = $body->data; $ret .= $this->process_media($data, $shortcode_attr); } else if ($display_what == 'single-media') { if (!empty($body->html)) { // Old-style id, can only operate in "embed" mode. Directly show the "embed" $ret .= $body->html; } else if (!empty($body->permalink)) { // New style id; need to re-execute the call with the permalink to embed, or display single-photo markup for "integrate" if ($shortcode_attr['embed_type'] == 'embed') { $query_url = 'http://api.instagram.com/oembed?url='.urlencode($body->permalink); $embed_response = wp_remote_request($query_url); if (!is_wp_error($embed_response) && isset($embed_response['response']) && isset($embed_response['response']['code']) && $embed_response['response']['code'] == 200) { $embed_response = json_decode($embed_response['body']); if (!empty($embed_response->html)) { $ret .= $embed_response->html; } else { $this->pop_from_stack(); // 'Process response' $this->pop_from_stack(); // 'Make call' return $this->error(esc_html__('No data returned. Unknown error', 'photonic')); } } else { $this->pop_from_stack(); // 'Process response' $this->pop_from_stack(); // 'Make call' return $this->error(esc_html__('No data returned. Unknown error', 'photonic')); } } else { $this->pop_from_stack(); // 'Process response' $this->pop_from_stack(); // 'Make call' return $this->generate_single_photo_markup([ 'src' => $body->media_url, 'href' => $body->permalink, 'title' => '', 'caption' => isset($body->caption) ? $body->caption : '', ] ); } } } else { $this->pop_from_stack(); // 'Process response' $this->pop_from_stack(); // 'Make call' return $this->error(esc_html__('No data returned. Unknown error', 'photonic')); } } else if (isset($response['body'])) { $body = json_decode($response['body']); if (isset($body->meta) && isset($body->meta->error_message)) { $this->pop_from_stack(); // 'Process response' $this->pop_from_stack(); // 'Make call' return $body->meta->error_message; } else { $this->pop_from_stack(); // 'Process response' $this->pop_from_stack(); // 'Make call' return $this->error(esc_html__('Unknown error', 'photonic')); } } else if (isset($response['response']['message'])) { $this->pop_from_stack(); // 'Process response' $this->pop_from_stack(); // 'Make call' return $this->error($response['response']['message']); } else { $this->pop_from_stack(); // 'Process response' $this->pop_from_stack(); // 'Make call' return $this->error(esc_html__('Unknown error', 'photonic')); } } } else { $this->pop_from_stack(); // 'Process response' $this->pop_from_stack(); // 'Make call' return $this->wp_error_message($response); } $this->pop_from_stack(); // 'Process response' $this->pop_from_stack(); // 'Make call' return $ret; } function process_media($data, $short_code) { global $photonic_instagram_photos_per_row_constraint, $photonic_instagram_photos_constrain_by_padding, $photonic_instagram_photos_constrain_by_count, $photonic_instagram_photo_title_display; $photo_objects = $this->build_level_1_objects($data, $short_code); $row_constraints = ['constraint-type' => $photonic_instagram_photos_per_row_constraint, 'padding' => $photonic_instagram_photos_constrain_by_padding, 'count' => $photonic_instagram_photos_constrain_by_count]; $ret = $this->layout_gallery($photo_objects, [ 'title_position' => $photonic_instagram_photo_title_display, 'row_constraints' => $row_constraints, 'parent' => 'stream', 'level_2_meta' => ['end' => 0, 'total' => empty($short_code['after']) ? 0 : $short_code['count']], ], $short_code, 1 ); return $ret; } function build_level_1_objects($data, $short_code, $module_parameters = [], $options = []) { $level_1_objects = []; $media = explode(',', $short_code['media']); $videos_ok = in_array('videos', $media) || in_array('all', $media); $photos_ok = in_array('photos', $media) || in_array('all', $media); foreach ($data as $photo) { if (isset($photo->media_type) && (((strtolower($photo->media_type) == 'image' || strtolower($photo->media_type) == 'carousel_album') && $photos_ok) || (strtolower($photo->media_type) == 'video' && $videos_ok)) && isset($photo->media_url)) { if (strtolower($photo->media_type) == 'carousel_album' && $short_code['carousel_handling'] == 'expand') { $query_url = 'https://graph.instagram.com/'.$photo->id.'/children?fields=id,media_type,media_url,permalink,thumbnail_url&access_token='.$this->access_token; $carousel_contents = $this->get_carousel_contents($query_url); $carousel_caption = empty($photo->caption) ? null : $photo->caption; foreach ($carousel_contents as $carousel_photo) { $this->process_single_item($carousel_photo, $level_1_objects, $photos_ok, $videos_ok, $carousel_photo->permalink, $carousel_photo->id, $carousel_caption); // Carousel photos have no caption, so use the post caption } } else { $this->process_single_item($photo, $level_1_objects, $photos_ok, $videos_ok, $photo->permalink, $photo->id); } } } return $level_1_objects; } function refresh_access_token($current_token, $save) { $token = []; $soon = $this->is_token_expiring_soon(30); if (is_null($soon)) { // No token exists. Do nothing, because the API secret is needed and authentication will need the back-end. // The call to is_token_expired will return true, prompting validation. } else { $photonic_authentication = get_option('photonic_authentication'); $instagram_token = $photonic_authentication['instagram']; if ($soon > 0) { if (!empty($current_token)) { $response = wp_remote_request('https://graph.instagram.com/refresh_access_token?grant_type=ig_refresh_token&access_token='.$current_token, [ 'sslverify' => PHOTONIC_SSL_VERIFY, ]); if (!is_wp_error($response)) { $token = $this->parse_token($response); if (!empty($token)) { $token['client_id'] = $instagram_token['client_id']; $user_response = wp_remote_request('https://graph.instagram.com/me?fields=id,username&access_token='.$token['oauth_token'], [ 'sslverify' => PHOTONIC_SSL_VERIFY, ]); if (!is_wp_error($user_response)) { $user_response = $user_response['body']; $user_response = json_decode($user_response); $token['user'] = $user_response->username; } } if ($save) { $this->save_token($token); } } } } else if ($soon < 0) { // No token exists. Do nothing, because the API secret is needed and authentication will need the back-end. // The call to is_token_expired will return true, prompting validation. } else { $token = $instagram_token; } } return $token; } function is_token_expired($token) { if (empty($token)) { return true; } if (!isset($token['oauth_token']) || !isset($token['oauth_token_created']) || !isset($token['oauth_token_expires'])) { return true; } $current = time(); if ($token['oauth_token_created'] + $token['oauth_token_expires'] < $current) { return true; } return false; } /** * @param $photo * @param array $level_1_objects * @param $photos_ok * @param $videos_ok * @param null $photo_link * @param null $photo_id * @param null $photo_caption */ private function process_single_item($photo, array &$level_1_objects, $photos_ok, $videos_ok, $photo_link = null, $photo_id = null, $photo_caption = null) { if (isset($photo->media_type) && (((strtolower($photo->media_type) == 'image' || strtolower($photo->media_type) == 'carousel_album') && $photos_ok) || (strtolower($photo->media_type) == 'video' && $videos_ok)) && isset($photo->media_url)) { $photo_object = []; $main_image = $photo->media_url; if (!empty($photo->thumbnail_url)) { $photo_object['thumbnail'] = $photo->thumbnail_url; $photo_object['tile_image'] = $photo->thumbnail_url; } else { $photo_object['thumbnail'] = $main_image; $photo_object['tile_image'] = $main_image; } $photo_object['main_image'] = $main_image; if (isset($photo->caption)) { $photo_object['title'] = esc_attr($photo->caption); } else if (!empty($photo_caption)) { $photo_object['title'] = esc_attr($photo_caption); } else { $photo_object['title'] = ''; } $photo_object['alt_title'] = $photo_object['title']; $photo_object['description'] = $photo_object['title']; $photo_object['main_page'] = $photo_link; $photo_object['id'] = $photo_id; if (strtolower($photo->media_type) == 'video') { $photo_object['video'] = $photo->media_url; $parse = wp_parse_url($photo_object['video']); $parse = explode('.', $parse['path']); $photo_object['mime'] = 'video/' . $parse[count($parse) - 1]; } $photo_object['provider'] = $this->provider; $level_1_objects[] = $photo_object; } } public function authentication_url() { // TODO: Implement authentication_url() method. } public function access_token_url() { // TODO: Implement access_token_url() method. } protected function set_token_validity($validity) { $this->token_valid = $validity; } /** * @param string $query_url * @return array|WP_Error */ private function get_carousel_contents($query_url) { $this->push_to_stack('Fetch carousel'); $response = wp_remote_request($query_url, [ 'sslverify' => PHOTONIC_SSL_VERIFY, ]); $carousel_contents = []; if (!is_wp_error($response)) { if (isset($response['response']) && isset($response['response']['code'])) { if ($response['response']['code'] == 200) { $body = json_decode($response['body']); if (isset($body->data)) { $carousel_contents = $body->data; } } } } $this->pop_from_stack(); // Fetch carousel return $carousel_contents; } }