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->soon_limit = 30;
$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'];
}
}
/**
* Main function that fetches the images associated with the shortcode.
*
* @param array $attr
* @return array
*/
public function get_gallery_images($attr = []): array {
global $photonic_instagram_media, $photonic_thumbnail_style, $photonic_instagram_carousel_caption_position;
$this->push_to_stack('Get Gallery Images');
$attr = array_merge(
$this->common_parameters,
[
// Common overrides ...
'caption' => 'title',
// Instagram-specific ...
'count' => 25,
'layout' => (empty($photonic_thumbnail_style) || 'square' === $photonic_thumbnail_style) ? 'random' : $photonic_thumbnail_style,
'media' => $photonic_instagram_media,
'embed_type' => 'embed',
'carousel_handling' => 'expand',
'carousel_caption' => $photonic_instagram_carousel_caption_position
],
$attr
);
$attr = array_map('trim', $attr);
if (empty($this->token) || empty($this->cached_token) || !$this->token_valid) {
$components = [new Error(esc_html__("Instagram Access Token not valid. Please reauthenticate.", 'photonic'))];
return $components;
}
if (empty($attr['user_id'])) {
$user_id = 'me';
}
else {
$user_id = $attr['user_id'];
}
$base_url = 'https://graph.instagram.com/';
$display_what = 'media';
if (!empty($attr['media_id'])) {// Trumps all else. A single photo will be shown.
$id_format = preg_match('/^[A-Z][A-Z0-9_-]+/i', $attr['media_id']) ? 'old' : 'new';
if ('old' === $id_format) {
$components = [new Error(sprintf(esc_html__('The %1$s used, %2$s has been deprecated by Instagram. Please regenerate the gallery with a new id.', 'photonic'), "media_id", esc_attr("" . $attr['media_id'] . "")))];
return $components;
}
else {
$query_url = $base_url . $attr['media_id'] . '?fields=' . $this->field_list;
}
$display_what = 'single-media';
}
elseif (!empty($attr['carousel'])) {
$query_url = $base_url . $attr['carousel'] . '?fields=' . $this->field_list;
$display_what = 'carousel';
}
elseif (!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($attr['view'])) {
$components = [new Error(sprintf(esc_html__('The %s parameter has to be defined.', 'photonic'), 'view'))];
return $components;
}
else {
$components = [
new Error(sprintf(esc_html__('Malformed shortcode. Either %1$s or %2$s or %3$s has to be defined.', 'photonic'), 'media_id', 'carousel', 'view'))
];
return $components;
}
}
if (isset($attr['count'])) {
$query_url = add_query_arg(['limit' => $attr['count']], $query_url);
}
if (isset($attr['after'])) {
$query_url = add_query_arg(['after' => $attr['after']], $query_url);
}
$components = $this->make_call($query_url, $display_what, $attr);
$this->pop_from_stack();
if (!empty($this->stack_trace[$this->gallery_index])) {
$components[] = $this->stack_trace[$this->gallery_index];
}
return $components;
}
protected function make_call($query_url, $display_what, array &$shortcode_attr) {
$this->push_to_stack("Make call $query_url");
$components = [];
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
$components = [new Error(esc_html__("Instagram Access Token not valid. Please reauthenticate.", 'photonic'))];
return $components;
}
$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 (200 === $response['response']['code']) {
$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;
}
else {
if (isset($shortcode_attr['after'])) {
unset($shortcode_attr['after']);
}
}
if (isset($body->data) && 'media' === $display_what) {
$data = $body->data;
$components[] = $this->process_media($data, $shortcode_attr);
}
elseif ('single-media' === $display_what || 'carousel' === $display_what) {
// Special handling for cases where the user was passing a media_id that was really a carousel.
// Prior to Instagram disabling oEmbed, this would be displayed as an inline slideshow.
// Now we will make an additional call to fetch the carousel
if ('CAROUSEL_ALBUM' === $body->media_type) {
$carousel_query_url = 'https://graph.instagram.com/' . $body->id . '/children?fields=id,media_type,media_url,permalink,thumbnail_url&access_token=' . $this->access_token;
$carousel_contents = $this->get_carousel_contents($carousel_query_url);
if (!empty($carousel_contents)) {
if ('above' === $shortcode_attr['carousel_caption'] && !empty($body->caption)) {
$components[] = "
$soon_limit defines how many days is "soon", and a refresh is triggered if the current date
* is in the "soon" range. E.g. If you have a soon limit of 30 days, and your token expires in 15 days when you load the page, this method will
* return true.
*
* For cases where the token does not exist yet, the method returns null.
*
* @param $soon_limit int Number of days to check the expiry limit for.
* @return int|null If there is no token, return null. Otherwise, if there are < $soon_limit days left, return 1, if token is expired return -1, and if there is time return 0.
*/
public function is_token_expiring_soon($soon_limit) {
$transient_token = get_transient('photonic_' . $this->provider . '_token');
$photonic_authentication = get_option('photonic_authentication');
if (false === $transient_token &&
(empty($photonic_authentication) || empty($photonic_authentication[$this->provider]) ||
empty($photonic_authentication[$this->provider]['oauth_token']) || empty($photonic_authentication[$this->provider]['oauth_token_created']) || empty($photonic_authentication[$this->provider]['oauth_token_expires']))) {
return null; // There is no token!
}
$token = (false === $transient_token) ? $photonic_authentication[$this->provider] : $transient_token;
$token_expiry = $token['oauth_token_created'] + $token['oauth_token_expires'];
$current = time();
$test_expiry = $current + $soon_limit * 24 * 60 * 60;
$time_left = $token_expiry - $test_expiry;
if ($current >= $token_expiry) {
return -1; // already expired
}
elseif ($time_left <= 0) {
return 1; // Expiring soon
}
else {
return 0; // There is still time
}
}
/**
* @param $photo
* @param $photos_ok
* @param $videos_ok
* @param null $photo_link
* @param null $photo_id
* @param null $photo_caption
*
* @return Photo
*/
private function process_single_item($photo, $photos_ok, $videos_ok, $photo_link = null, $photo_id = null, $photo_caption = null) {
if (isset($photo->media_type) && ((('image' === strtolower($photo->media_type) || 'carousel_album' === strtolower($photo->media_type)) && $photos_ok) || ('video' === strtolower($photo->media_type) && $videos_ok)) && isset($photo->media_url)) {
$photonic_photo = new Photo();
$main_image = esc_url($photo->media_url);
if (!empty($photo->thumbnail_url)) {
$photonic_photo->thumbnail = esc_url($photo->thumbnail_url);
$photonic_photo->tile_image = esc_url($photo->thumbnail_url);
}
else {
$photonic_photo->thumbnail = $main_image;
$photonic_photo->tile_image = $main_image;
}
$photonic_photo->main_image = $main_image;
if (isset($photo->caption)) {
$photonic_photo->title = wp_kses_post($photo->caption);
}
elseif (!empty($photo_caption)) {
$photonic_photo->title = wp_kses_post($photo_caption);
}
else {
$photonic_photo->title = '';
}
$photonic_photo->alt_title = $photonic_photo->title;
$photonic_photo->description = $photonic_photo->title;
$photonic_photo->main_page = $photo_link;
$photonic_photo->id = $photo_id;
if ('video' === strtolower($photo->media_type)) {
$photonic_photo->video = esc_url($photo->media_url);
$photonic_photo->main_image = esc_url($photo->thumbnail_url);
$parse = wp_parse_url($photonic_photo->video);
$parse = explode('.', $parse['path']);
$photonic_photo->mime = 'video/' . $parse[count($parse) - 1];
}
return $photonic_photo;
}
return null;
}
public function authentication_URL() {
// Not needed; placeholder for abstract class method stub
}
public function access_token_URL() {
// Not needed; placeholder for abstract class method stub
}
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 (200 === $response['response']['code']) {
$body = json_decode($response['body']);
if (isset($body->data)) {
$carousel_contents = $body->data;
}
}
}
}
$this->pop_from_stack(); // Fetch carousel
return $carousel_contents;
}
}