eol_date = gmdate("Ymd", gmmktime(0, 0, 0, 3, 30, 2025));
$this->current_date = gmdate("Ymd");
// if (!empty($photonic_google_use_own_keys) || (!empty($photonic_google_client_id) && !empty($photonic_google_client_secret))) {
if (!empty($photonic_google_client_id) && !empty($photonic_google_client_secret)) {
$this->client_id = trim($photonic_google_client_id);
$this->client_secret = trim($photonic_google_client_secret);
}
/*
*
elseif (empty($photonic_google_use_own_keys)) {
$this->client_id = '';
$this->client_secret = '';
}
*/
$this->provider = 'google';
$this->oauth_version = '2.0';
$this->response_type = 'code';
$this->scope = 'https://www.googleapis.com/auth/photoslibrary.readonly';
$this->link_lightbox_title = false; // empty($photonic_google_disable_title_link);
// Documentation
$this->doc_links = [
'general' => 'https://aquoid.com/plugins/photonic/google-photos/',
'photos' => 'https://aquoid.com/plugins/photonic/google-photos/photos/',
'albums' => 'https://aquoid.com/plugins/photonic/google-photos/albums/',
];
$this->error_date_format = esc_html__('Dates must be entered in the format Y/M/D where Y is from 0 to 9999, M is from 0 to 12 and D is from 0 to 31. You entered %s.', 'photonic');
$this->oauth_done = false;
if ($this->current_date < $this->eol_date) {
$this->authenticate($photonic_google_refresh_token);
}
}
/**
* Main function that fetches the images associated with the shortcode.
*
* @param array $attr
* @return array
*/
public function get_gallery_images($attr = []): array {
// This block kills the rest of the execution without deleting the rest of the code.
if ('google' === $attr['type']) {
return [new Comment(esc_html__('Google Photos is no longer supported due to an API deprecation by Google.', 'photonic'))];
}
global $photonic_google_refresh_token, $photonic_google_media, $photonic_google_title_caption;
$out = [];
if ($this->current_date < $this->eol_date) {
$this->push_to_stack('Get Gallery Images');
$attr = array_merge(
$this->common_parameters,
[
'caption' => $photonic_google_title_caption,
'thumb_size' => '150',
'main_size' => '1600',
'tile_size' => '1600',
'crop_thumb' => 'crop',
// Google ...
'count' => 100,
'media' => $photonic_google_media,
'video_size' => 'dv',
'date_filters' => '',
'content_filters' => '',
'access' => 'all',
],
$attr
);
$attr = array_map('trim', $attr);
$attr['overlay_size'] = empty($attr['overlay_size']) ? $attr['thumb_size'] : $attr['overlay_size'];
$attr['overlay_video_size'] = empty($attr['overlay_video_size']) ? $attr['video_size'] : $attr['overlay_video_size'];
$attr['overlay_crop'] = empty($attr['overlay_crop']) ? $attr['crop_thumb'] : $attr['overlay_crop'];
if (empty($this->client_id)) {
$this->pop_from_stack();
return [new Error(esc_html__('Google Photos Client ID not defined.', 'photonic') . Photonic::doc_link($this->doc_links['general']))];
}
if (empty($this->client_secret)) {
$this->pop_from_stack();
return [new Error(esc_html__('Google Photos Client Secret not defined.', 'photonic') . Photonic::doc_link($this->doc_links['general']))];
}
if (empty($photonic_google_refresh_token)) {
$this->pop_from_stack();
return [new Error(sprintf(esc_html__('Google Photos Refresh Token not defined. Please authenticate from %s.', 'photonic'), 'Photonic → Authentication') . Photonic::doc_link($this->doc_links['general']))];
}
if (!$this->refresh_token_valid) {
$this->pop_from_stack();
$error = sprintf(esc_html__('Google Photos Refresh Token invalid. Please authenticate from %s.', 'photonic'), 'Photonic → Authentication');
if (!empty($this->auth_error)) {
$error .= '
' . sprintf(esc_html__('Error encountered during authentication: %s', 'photonic'), '
' . $this->auth_error . ''); } return [new Error($error . Photonic::doc_link($this->doc_links['general']))]; } if (empty($attr['view'])) { $this->pop_from_stack(); return [new Error(sprintf(esc_html__('The %s parameter is mandatory for the shortcode.', 'photonic'), '
view'))];
}
$query_urls = [];
if ('albums' === $attr['view']) {
$additional = [];
if (!empty($attr['count'])) {
$additional['pageSize'] = min(intval($attr['count']), 50);
}
if (!empty($attr['next_token'])) {
$additional['pageToken'] = $attr['next_token'];
}
$access = $this->access_all_or_shared($attr);
if ($access['shared']) {
$query_urls['https://photoslibrary.googleapis.com/v1/sharedAlbums'] = ['GET' => $additional];
}
if ($access['self']) {
$query_urls['https://photoslibrary.googleapis.com/v1/albums'] = ['GET' => $additional];
}
}
elseif ('photos' === $attr['view'] || 'shared-photos' === $attr['view']) {
$additional = [];
if (!empty($attr['album_id'])) {
$additional['albumId'] = $attr['album_id'];
}
else {
$filters = [];
$date_parameter = [];
$range_parameter = [];
if (!empty($attr['date_filters'])) {
/*
* Structure of $attr['date_filters']: comma-separated list of dates or date ranges.
* Each date is represented by Y/M/D, where 0 <= Y <= 9999, 0 <= M <= 12, 0 <= D < 31
* Each range is represented as Y/M/D-Y/M/D
*/
$date_filters = explode(',', trim($attr['date_filters']));
foreach ($date_filters as $date_filter) {
$dates = explode('-', trim($date_filter));
if (count($dates) > 2) {
$dates = array_slice($dates, 0, 2);
}
$range = [];
foreach ($dates as $idx => $date) {
$date_parts = explode('/', trim($date));
if (count($date_parts) !== 3) {
$this->pop_from_stack();
return [new Error(sprintf($this->error_date_format, $date))];
}
if (!is_numeric($date_parts[0]) || $date_parts[0] > 9999 || $date_parts[0] < 0 ||
!is_numeric($date_parts[1]) || $date_parts[1] > 12 || $date_parts[1] < 0 ||
!is_numeric($date_parts[2]) || $date_parts[2] > 31 || $date_parts[2] < 0) {
$this->pop_from_stack();
return [new Error(sprintf($this->error_date_format, $date))];
}
$date_object = [
'year' => intval($date_parts[0]),
'month' => intval($date_parts[1]),
'day' => intval($date_parts[2]),
];
if (count($dates) === 1) {
$date_parameter[] = $date_object;
}
elseif (0 === $idx) {
$range['startDate'] = $date_object;
}
else {
$range['endDate'] = $date_object;
}
}
if (!empty($range)) {
$range_parameter[] = $range;
}
}
$date_filter_parameter = [];
if (!empty($date_parameter)) {
$date_filter_parameter['dates'] = $date_parameter;
}
if (!empty($range_parameter)) {
$date_filter_parameter['ranges'] = $range_parameter;
}
if (!empty($date_filter_parameter)) {
$filters['dateFilter'] = $date_filter_parameter;
}
}
if (!empty($attr['content_filters'])) {
$valid_filters = [
'NONE' => 'Default content category. This category is ignored if any other category is also listed.',
'LANDSCAPES' => 'Media items containing landscapes.',
'RECEIPTS' => 'Media items containing receipts.',
'CITYSCAPES' => 'Media items containing cityscapes.',
'LANDMARKS' => 'Media items containing landmarks.',
'SELFIES' => 'Media items that are selfies.',
'PEOPLE' => 'Media items containing people.',
'PETS' => 'Media items containing pets.',
'WEDDINGS' => 'Media items from weddings.',
'BIRTHDAYS' => 'Media items from birthdays.',
'DOCUMENTS' => 'Media items containing documents.',
'TRAVEL' => 'Media items taken during travel.',
'ANIMALS' => 'Media items containing animals.',
'FOOD' => 'Media items containing food.',
'SPORT' => 'Media items from sporting events.',
'NIGHT' => 'Media items taken at night.',
'PERFORMANCES' => 'Media items from performances.',
'WHITEBOARDS' => 'Media items containing whiteboards.',
'SCREENSHOTS' => 'Media items that are screenshots.',
'UTILITY' => 'Media items that are considered to be utility. These include, but are not limited to documents, screenshots, whiteboards etc.',
];
/*
* Structure of content_filters: C1,C2,-C3,C4,-C5.
* The filters are specified as a comma-separated list.
* A "-" before the filter's name indicates that the filter should be excluded rather than included.
*/
$content_filters = explode(',', $attr['content_filters']);
$include = [];
$exclude = [];
foreach ($content_filters as $content_filter) {
$content_filter = strtoupper($content_filter);
if (stripos($content_filter, '-') === 0 && array_key_exists(substr($content_filter, 1), $valid_filters)) {
$exclude[] = substr($content_filter, 1);
}
elseif (array_key_exists($content_filter, $valid_filters)) {
$include[] = $content_filter;
}
}
$content_filter_parameter = [];
if (!empty($include)) {
$content_filter_parameter['includedContentCategories'] = $include;
}
if (!empty($exclude)) {
$content_filter_parameter['excludedContentCategories'] = $exclude;
}
if (!empty($content_filter_parameter)) {
$filters['contentFilter'] = $content_filter_parameter;
}
}
$media_filters = explode(',', $attr['media']);
$media_filter_parameter = [];
if (in_array('all', $media_filters, true)) {
$media_filter_parameter[] = 'ALL_MEDIA';
}
elseif (in_array('photos', $media_filters, true)) {
$media_filter_parameter[] = 'PHOTO';
}
elseif (in_array('videos', $media_filters, true)) {
$media_filter_parameter[] = 'VIDEO';
}
if (!empty($media_filter_parameter)) {
$filters['mediaTypeFilter'] = ['mediaTypes' => $media_filter_parameter];
}
if (!empty($filters)) {
$additional['filters'] = $filters;
}
}
if (!empty($attr['count']) || !empty($attr['photo_count'])) {
$additional['pageSize'] = !empty($attr['photo_count']) ? $attr['photo_count'] : $attr['count'];
$additional['pageSize'] = min(intval($additional['pageSize']), 100);
}
if (!empty($attr['next_token'])) {
$additional['pageToken'] = $attr['next_token'];
}
$query_urls['https://photoslibrary.googleapis.com/v1/mediaItems:search'] = ['POST' => $additional];
}
$out = $this->make_call($query_urls, $attr);
$this->pop_from_stack();
if (!empty($this->stack_trace[$this->gallery_index])) {
$out[] = $this->stack_trace[$this->gallery_index];
}
}
return $out;
}
/**
* Makes calls to Google with the shortcode parameters.
*
* @param $query_urls - The URLs to be queried
* @param $attr - The shortcode attributes
* @return array
*/
private function make_call($query_urls, $attr): array {
global $photonic_google_refresh_token;
$this->push_to_stack('Making calls');
$incremented = false;
$components = [];
$access = $this->access_all_or_shared($attr);
$all_ids = [];
foreach ($query_urls as $query_url => $method_and_args) {
$this->push_to_stack("Query $query_url");
if ($access['self'] && 'https://photoslibrary.googleapis.com/v1/sharedAlbums' === $query_url) {
$defer = true;
}
else {
$defer = false;
}
if (!empty($photonic_google_refresh_token) && !empty($this->access_token)) {
$query_url = add_query_arg('access_token', $this->access_token, $query_url);
}
foreach ($method_and_args as $method => $args) {
$this->push_to_stack('Sending request');
if (empty($args['filters'])) {
$call_args = [];
$call_args['method'] = $method;
$call_args['body'] = $args;
$call_args['sslverify'] = PHOTONIC_SSL_VERIFY;
$response = wp_remote_request($query_url, $call_args);
}
else {
$headers = [];
$headers['Accept'] = 'application/json';
$headers['Content-Type'] = 'application/json';
$response = wp_remote_request(
$query_url,
[
'method' => 'POST',
'headers' => $headers,
'httpversion' => '1.0',
'body' => wp_json_encode($args),
]
);
}
$this->pop_from_stack();
if (!is_wp_error($response)) {
$this->push_to_stack('Processing Response');
$body = wp_remote_retrieve_body($response);
if (!$incremented) {
$incremented = true;
}
$output = $this->process_response($body, $attr, $defer, $all_ids);
if (!is_null($output)) {
if ($defer && is_array($output)) {
foreach ($output as $object) {
$all_ids[] = $object->id;
}
}
else {
$components[] = $output;
}
}
$this->pop_from_stack();
}
else {
$this->pop_from_stack(); // "Query $query_url"
$this->pop_from_stack(); // 'Making calls'
return [new Error($response->get_error_message())];
}
}
$this->pop_from_stack();
}
$this->pop_from_stack();
return $components;
}
/**
* @param $body
* @param $short_code
* @param bool $deferred
* @param array $remove
* @return mixed
*/
private function process_response($body, $short_code, bool $deferred = false, array $remove = []) {
global $photonic_google_photo_title_display, $photonic_google_photos_per_row_constraint, $photonic_gallery_template_page,
$photonic_google_photos_constrain_by_count, $photonic_google_photo_pop_title_display, $photonic_google_hide_album_photo_count_display;
if (!empty($body)) {
$body = json_decode($body);
$row_constraints = ['constraint-type' => $photonic_google_photos_per_row_constraint, 'count' => $photonic_google_photos_constrain_by_count];
$display = $short_code['display'];
if (isset($body->albums) || isset($body->sharedAlbums)) {
$albums = $body->albums ?? $body->sharedAlbums;
$pagination = $this->get_pagination($body, $short_code);
$dummy_options = [];
$albums = $this->build_level_2_objects($albums, $short_code, $remove, $dummy_options, $pagination);
global $photonic_google_photos_layout_engine;
$layout_engine = $short_code['layout_engine'] ?? $photonic_google_photos_layout_engine;
if ('css' === $layout_engine) {
$this->update_thumbnail_information($albums, $short_code);
}
if ($deferred) {
return $albums;
}
$album_list = new Album_List($short_code);
$album_list->albums = $albums;
$album_list->row_constraints = $row_constraints;
$album_list->type = 'albums';
$album_list->singular_type = 'album';
$album_list->title_position = $photonic_google_photo_title_display;
$album_list->level_1_count_display = !empty($photonic_google_hide_album_photo_count_display);
$album_list->pagination = $pagination;
$album_list->album_opens_gallery = ('page' === $short_code['popup'] && !empty($photonic_gallery_template_page) && is_string(get_post_status($photonic_gallery_template_page)));
return $album_list;
}
elseif (isset($body->mediaItems)) {
if ('local' === $display) {
$title_position = $photonic_google_photo_title_display;
}
else {
$row_constraints = ['constraint-type' => 'padding'];
$title_position = $photonic_google_photo_pop_title_display;
}
$pagination = $this->get_pagination($body, $short_code);
$photos = $body->mediaItems;
$photos = $this->build_level_1_objects($photos, $short_code);
$photo_list = new Photo_List($short_code);
$photo_list->photos = $photos;
$photo_list->title_position = $title_position;
$photo_list->row_constraints = $row_constraints;
$photo_list->parent = 'album';
$photo_list->pagination = $pagination;
return $photo_list;
}
elseif (isset($body->error)) {
$err = esc_html__('Failed to get data. Error:', 'photonic') . "\n";
$err .= $body->error->message;
$err .= "\n";
$err .= $body;
$err .= "| Album Title | \n"; $ret .= "\t\tThumbnail | \n"; $ret .= "\t\tAlbum ID | \n"; $ret .= "\t\tMedia Count | \n"; $ret .= "\t
|---|---|---|---|
| " . wp_specialchars_decode(stripslashes(wp_filter_nohtml_kses($album->title)), ENT_QUOTES) . " | \n"; $ret .= "\t\t" . wp_filter_nohtml_kses($album->id) . " | \n"; $ret .= "\t\t" . wp_filter_nohtml_kses($album->mediaItemsCount) . " | \n"; $ret .= "\t|
| \n"; $ret .= ''; $ret .= "\t\t | \n"; $ret .= "\t|||