PluginProbe
Photonic Gallery & Lightbox for Flickr, SmugMug & Others / 2.41
Photonic Gallery & Lightbox for Flickr, SmugMug & Others v2.41
3.36 3.35 3.34 3.33 2.19 2.20 2.21 2.22 2.23 2.24 2.25 2.26 2.27 2.28 2.29 2.30 2.31 2.32 2.33 2.34 2.40 2.41 2.42 2.43 2.44 All 141 releases
photonic / Modules / Google_Photos.php

Google_Photos.php in Photonic Gallery & Lightbox for Flickr, SmugMug & Others 2.41, at Modules/Google_Photos.php

776 lines 26.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Photonic_Plugin\Modules;
3
4 use Photonic_Plugin\Core\Photonic;
5
6 require_once('OAuth2.php');
7 require_once('Level_One_Module.php');
8
9 /**
10 * Fetches photos from a user's Google Photos account.
11 * Lacks support for dual title / description fields, doesn't provide download URLs, and video support is ambiguous.
12 */
13
14 class Google_Photos extends OAuth2 implements Level_One_Module {
15 var $error_date_format;
16 private static $instance = null;
17
18 protected function __construct() {
19 parent::__construct();
20 global $photonic_google_client_id, $photonic_google_client_secret, $photonic_google_refresh_token;
21
22 // if (!empty($photonic_google_use_own_keys) || (!empty($photonic_google_client_id) && !empty($photonic_google_client_secret))) {
23 if (!empty($photonic_google_client_id) && !empty($photonic_google_client_secret)) {
24 $this->client_id = trim($photonic_google_client_id);
25 $this->client_secret = trim($photonic_google_client_secret);
26 }
27 /* else if (empty($photonic_google_use_own_keys)) {
28 $this->client_id = '';
29 $this->client_secret = '';
30 }*/
31
32 $this->provider = 'google';
33 $this->oauth_version = '2.0';
34 $this->response_type = 'code';
35 $this->scope = 'https://www.googleapis.com/auth/photoslibrary.readonly';
36 $this->link_lightbox_title = false; //empty($photonic_google_disable_title_link);
37
38 // Documentation
39 $this->doc_links = [
40 'general' => 'https://aquoid.com/plugins/photonic/google-photos/',
41 'photos' => 'https://aquoid.com/plugins/photonic/google-photos/photos/',
42 'albums' => 'https://aquoid.com/plugins/photonic/google-photos/albums/',
43 ];
44
45 $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');
46 $this->oauth_done = false;
47 $this->authenticate($photonic_google_refresh_token);
48 }
49
50 public static function get_instance() {
51 if (self::$instance == null) {
52 self::$instance = new Google_Photos();
53 }
54 return self::$instance;
55 }
56
57 /**
58 * Main function that fetches the images associated with the shortcode.
59 *
60 * @param array $attr
61 * @param array $gallery_meta
62 * @return string
63 */
64 public function get_gallery_images($attr = [], &$gallery_meta = []) {
65 global $photonic_google_refresh_token, $photonic_google_media, $photonic_google_title_caption;
66 $this->gallery_index++;
67 $this->push_to_stack('Get Gallery Images');
68 $attr = array_merge(
69 $this->common_parameters,
70 [
71 'caption' => $photonic_google_title_caption,
72 'thumb_size' => '150',
73 'main_size' => '1600',
74 'tile_size' => '1600',
75 'crop_thumb' => 'crop',
76
77 // Google ...
78 'count' => 100,
79 'media' => $photonic_google_media,
80 'video_size' => 'dv',
81 'date_filters' => '',
82 'content_filters' => '',
83 'access' => 'all',
84 ],
85 $attr);
86 $attr = array_map('trim', $attr);
87
88 $attr['overlay_size'] = empty($attr['overlay_size']) ? $attr['thumb_size'] : $attr['overlay_size'];
89 $attr['overlay_video_size'] = empty($attr['overlay_video_size']) ? $attr['video_size'] : $attr['overlay_video_size'];
90 $attr['overlay_crop'] = empty($attr['overlay_crop']) ? $attr['crop_thumb'] : $attr['overlay_crop'];
91
92 if (empty($this->client_id)) {
93 $this->pop_from_stack();
94 return $this->error(esc_html__('Google Photos Client ID not defined.', 'photonic').Photonic::doc_link($this->doc_links['general']));
95 }
96 if (empty($this->client_secret)) {
97 $this->pop_from_stack();
98 return $this->error(esc_html__('Google Photos Client Secret not defined.', 'photonic').Photonic::doc_link($this->doc_links['general']));
99 }
100 if (empty($photonic_google_refresh_token)) {
101 $this->pop_from_stack();
102 return $this->error(sprintf(esc_html__('Google Photos Refresh Token not defined. Please authenticate from %s.', 'photonic'), '<em>Photonic &rarr; Authentication</em>').Photonic::doc_link($this->doc_links['general']));
103 }
104
105 if (empty($attr['view'])) {
106 $this->pop_from_stack();
107 return $this->error(sprintf(esc_html__('The %s parameter is mandatory for the shortcode.', 'photonic'), '<code>view</code>'));
108 }
109
110 $query_urls = [];
111 if ($attr['view'] == 'albums') {
112 $additional = [];
113 if (!empty($attr['count'])) {
114 $additional['pageSize'] = intval($attr['count']) > 50 ? 50 : intval($attr['count']);
115 }
116
117 if (!empty($attr['next_token'])) {
118 $additional['pageToken'] = $attr['next_token'];
119 }
120
121 $access = $this->access_all_or_shared($attr);
122 if ($access['shared']) {
123 $query_urls['https://photoslibrary.googleapis.com/v1/sharedAlbums'] = ['GET' => $additional];
124 }
125 if ($access['all']) {
126 $query_urls['https://photoslibrary.googleapis.com/v1/albums'] = ['GET' => $additional];
127 }
128 }
129 else if ($attr['view'] == 'photos') {
130 $additional = [];
131 if (!empty($attr['album_id'])) {
132 $additional['albumId'] = $attr['album_id'];
133 }
134 else {
135 $filters = [];
136
137 $date_parameter = [];
138 $range_parameter = [];
139 if (!empty($attr['date_filters'])) {
140 /*
141 * Structure of $attr['date_filters']: comma-separated list of dates or date ranges.
142 * Each date is represented by Y/M/D, where 0 <= Y <= 9999, 0 <= M <= 12, 0 <= D < 31
143 * Each range is represented as Y/M/D-Y/M/D
144 */
145 $date_filters = explode(',', trim($attr['date_filters']));
146 foreach($date_filters as $date_filter) {
147 $dates = explode('-', trim($date_filter));
148 if (count($dates) > 2) {
149 $dates = array_slice($dates, 0, 2);
150 }
151 $range = [];
152 foreach ($dates as $idx => $date) {
153 $date_parts = explode('/', trim($date));
154 if (count($date_parts) != 3) {
155 $this->pop_from_stack();
156 return $this->error(sprintf($this->error_date_format, $date));
157 }
158
159 if (!is_numeric($date_parts[0]) || $date_parts[0] > 9999 || $date_parts[0] < 0 ||
160 !is_numeric($date_parts[1]) || $date_parts[1] > 12 || $date_parts[1] < 0 ||
161 !is_numeric($date_parts[2]) || $date_parts[2] > 31 || $date_parts[2] < 0) {
162 $this->pop_from_stack();
163 return $this->error(sprintf($this->error_date_format, $date));
164 }
165
166 $date_object = [
167 'year' => intval($date_parts[0]),
168 'month' => intval($date_parts[1]),
169 'day' => intval($date_parts[2]),
170 ];
171
172 if (count($dates) == 1) {
173 $date_parameter[] = $date_object;
174 }
175 else if ($idx == 0) {
176 $range['startDate'] = $date_object;
177 }
178 else {
179 $range['endDate'] = $date_object;
180 }
181 }
182 if (!empty($range)) {
183 $range_parameter[] = $range;
184 }
185 }
186
187 $date_filter_parameter = [];
188 if (!empty($date_parameter)) {
189 $date_filter_parameter['dates'] = $date_parameter;
190 }
191 if (!empty($range_parameter)) {
192 $date_filter_parameter['ranges'] = $range_parameter;
193 }
194 if (!empty($date_filter_parameter)) {
195 $filters['dateFilter'] = $date_filter_parameter;
196 }
197 }
198
199 if (!empty($attr['content_filters'])) {
200 $valid_filters = [
201 'NONE' => 'Default content category. This category is ignored if any other category is also listed.',
202 'LANDSCAPES' => 'Media items containing landscapes.',
203 'RECEIPTS' => 'Media items containing receipts.',
204 'CITYSCAPES' =>'Media items containing cityscapes.',
205 'LANDMARKS' => 'Media items containing landmarks.',
206 'SELFIES' => 'Media items that are selfies.',
207 'PEOPLE' => 'Media items containing people.',
208 'PETS' => 'Media items containing pets.',
209 'WEDDINGS' => 'Media items from weddings.',
210 'BIRTHDAYS' => 'Media items from birthdays.',
211 'DOCUMENTS' => 'Media items containing documents.',
212 'TRAVEL' => 'Media items taken during travel.',
213 'ANIMALS' => 'Media items containing animals.',
214 'FOOD' => 'Media items containing food.',
215 'SPORT' => 'Media items from sporting events.',
216 'NIGHT' => 'Media items taken at night.',
217 'PERFORMANCES' => 'Media items from performances.',
218 'WHITEBOARDS' => 'Media items containing whiteboards.',
219 'SCREENSHOTS' => 'Media items that are screenshots.',
220 'UTILITY' => 'Media items that are considered to be utility. These include, but are not limited to documents, screenshots, whiteboards etc.',
221 ];
222
223 /*
224 * Structure of content_filters: C1,C2,-C3,C4,-C5.
225 * The filters are specified as a comma-separated list.
226 * A "-" before the filter's name indicates that the filter should be excluded rather than included.
227 */
228 $content_filters = explode(',', $attr['content_filters']);
229 $include = $exclude = [];
230 foreach ($content_filters as $content_filter) {
231 $content_filter = strtoupper($content_filter);
232 if (stripos($content_filter, '-') == 0 && array_key_exists(substr($content_filter, 1), $valid_filters)) {
233 $exclude[] = substr($content_filter, 1);
234 }
235 else if (array_key_exists($content_filter, $valid_filters)){
236 $include[] = $content_filter;
237 }
238 }
239
240 $content_filter_parameter = [];
241 if (!empty($include)) {
242 $content_filter_parameter['includedContentCategories'] = $include;
243 }
244 if (!empty($exclude)) {
245 $content_filter_parameter['excludedContentCategories'] = $exclude;
246 }
247 if (!empty($content_filter_parameter)) {
248 $filters['contentFilter'] = $content_filter_parameter;
249 }
250 }
251
252 $media_filters = explode(',', $attr['media']);
253 $media_filter_parameter = [];
254 if (in_array('all', $media_filters)) {
255 $media_filter_parameter[] = 'ALL_MEDIA';
256 }
257 else if (in_array('photos', $media_filters)) {
258 $media_filter_parameter[] = 'PHOTO';
259 }
260 else if (in_array('videos', $media_filters)) {
261 $media_filter_parameter[] = 'VIDEO';
262 }
263
264 if (!empty($media_filter_parameter)) {
265 $filters['mediaTypeFilter'] = ['mediaTypes' => $media_filter_parameter];
266 }
267
268 if (!empty($filters)) {
269 $additional['filters'] = $filters;
270 }
271 }
272
273 if (!empty($attr['count']) || !empty($attr['photo_count'])) {
274 $additional['pageSize'] = !empty($attr['photo_count']) ? $attr['photo_count'] : $attr['count'];
275 $additional['pageSize'] = intval($additional['pageSize']) > 100 ? 100 : intval($additional['pageSize']);
276 }
277 if (!empty($attr['next_token'])) {
278 $additional['pageToken'] = $attr['next_token'];
279 }
280
281 $query_urls['https://photoslibrary.googleapis.com/v1/mediaItems:search'] = ['POST' => $additional];
282 }
283
284 $out = $this->make_call($query_urls, $attr);
285 $out = $this->finalize_markup($out, $attr);
286 $this->pop_from_stack();
287
288 return $out.$this->get_stack_markup();
289 }
290
291 /**
292 * Makes calls to Google with the shortcode parameters.
293 *
294 * @param $query_urls - The URLs to be queried
295 * @param $attr - The shortcode attributes
296 * @return string
297 */
298 private function make_call($query_urls, $attr) {
299 global $photonic_google_refresh_token;
300 $this->push_to_stack('Making calls');
301
302 $incremented = false;
303 $out = '';
304 $access = $this->access_all_or_shared($attr);
305
306 $all_ids = [];
307 foreach ($query_urls as $query_url => $method_and_args) {
308 $this->push_to_stack("Query $query_url");
309 if ($access['all'] && $query_url == 'https://photoslibrary.googleapis.com/v1/sharedAlbums') {
310 $defer = true;
311 }
312 else {
313 $defer = false;
314 }
315
316 if (!empty($photonic_google_refresh_token) && !empty($this->access_token)) {
317 $query_url = add_query_arg('access_token', $this->access_token, $query_url);
318 }
319
320 foreach ($method_and_args as $method => $args) {
321 $this->push_to_stack('Sending request');
322 if (empty($args['filters'])) {
323 $call_args = [];
324 $call_args['method'] = $method;
325 $call_args['body'] = $args;
326 $call_args['sslverify'] = PHOTONIC_SSL_VERIFY;
327 $response = wp_remote_request($query_url, $call_args);
328 }
329 else {
330 $headers = [];
331 $headers[] = 'Accept: application/json';
332 $headers[] = 'Content-Type: application/json';
333
334 // This doesn't work for some reason while using 'filters'. Google always responds with an invalid JSON format error.
335 // Various options have been tried, including sending 'body' without a json_encode, enclosing the json_encode in '[]' etc.
336 // Falling back on cURL for cases where $args have 'filters'
337 /* $response = wp_remote_request($query_url, [
338 'method' => 'POST',
339 'headers' => $headers,
340 'httpversion' => '1.0',
341 'body' => json_encode($args),
342 ]);
343 */
344
345 $ch = curl_init($query_url);
346 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, PHOTONIC_SSL_VERIFY);
347 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // If not set, this prints the output
348 curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
349 curl_setopt($ch, CURLOPT_HEADER, false);
350 curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($args));
351
352 $curl_response = curl_exec($ch);
353 curl_close($ch);
354 // The following mimics a structure that can be handled by is_wp_error and wp_remote_retrieve_body
355 $response = [];
356 $response['body'] = $curl_response;
357 }
358 $this->pop_from_stack();
359
360 if (!is_wp_error($response)) {
361 $this->push_to_stack('Processing Response');
362 $body = wp_remote_retrieve_body($response);
363
364 if (!$incremented) {
365 $incremented = true;
366 }
367
368 $output = $this->process_response($body, $attr, $defer, $all_ids);
369 if ($defer && is_array($output)) {
370 foreach ($output as $object) {
371 $all_ids[] = $object['id_1'];
372 }
373 }
374 else {
375 $out .= $output;
376 }
377 $this->pop_from_stack();
378 }
379 else {
380 $this->pop_from_stack(); // "Query $query_url"
381 $this->pop_from_stack(); // 'Making calls'
382 return $this->error($response->get_error_message());
383 }
384 }
385 $this->pop_from_stack();
386 }
387
388 $this->pop_from_stack();
389 return $out;
390 }
391
392 /**
393 * @param $body
394 * @param $short_code
395 * @param bool|false $deferred
396 * @param array $remove
397 * @return mixed
398 */
399 private function process_response($body, $short_code, $deferred = false, $remove = []) {
400 global $photonic_google_photo_title_display, $photonic_google_photos_per_row_constraint, $photonic_google_photos_constrain_by_padding,
401 $photonic_google_photos_constrain_by_count, $photonic_google_photos_pop_per_row_constraint, $photonic_google_photos_pop_constrain_by_padding,
402 $photonic_google_photos_pop_constrain_by_count, $photonic_google_photo_pop_title_display, $photonic_google_hide_album_photo_count_display;
403 $out = '';
404 if (!empty($body)) {
405 $body = json_decode($body);
406 $row_constraints = ['constraint-type' => $photonic_google_photos_per_row_constraint, 'padding' => $photonic_google_photos_constrain_by_padding, 'count' => $photonic_google_photos_constrain_by_count];
407 $display = $short_code['display'];
408 if (isset($body->albums) || isset($body->sharedAlbums)) {
409 $albums = isset($body->albums) ? $body->albums : $body->sharedAlbums;
410
411 $pagination = [];
412 if (!empty($body->nextPageToken)) {
413 $pagination = [
414 'total' => 10,
415 'start' => 0,
416 'end' => 1,
417 'per-page' => $short_code['count'],
418 'next-token' => $body->nextPageToken,
419 ];
420 }
421
422 $albums = $this->build_level_2_objects($albums, $short_code, $remove, $pagination);
423 if ($deferred) {
424 return $albums;
425 }
426
427 $out .= $this->layout_gallery($albums,
428 [
429 'row_constraints' => $row_constraints,
430 'type' => 'albums',
431 'singular_type' => 'album',
432 'title_position' => $photonic_google_photo_title_display,
433 'level_1_count_display' => !empty($photonic_google_hide_album_photo_count_display),
434 'pagination' => $pagination,
435 ],
436 $short_code,
437 2
438 );
439 }
440 else if (isset($body->mediaItems)){
441 if ($display == 'in-page') {
442 $title_position = $photonic_google_photo_title_display;
443 }
444 else {
445 $row_constraints = ['constraint-type' => $photonic_google_photos_pop_per_row_constraint, 'padding' => $photonic_google_photos_pop_constrain_by_padding, 'count' => $photonic_google_photos_pop_constrain_by_count];
446 $title_position = $photonic_google_photo_pop_title_display;
447 }
448
449 $level_2_meta = [];
450 if (!empty($body->nextPageToken)) {
451 $level_2_meta = [
452 'total' => 10,
453 'start' => 0,
454 'end' => 1,
455 'per-page' => $short_code['count'],
456 'next-token' => $body->nextPageToken,
457 ];
458 }
459 $photos = $body->mediaItems;
460 $photos = $this->build_level_1_objects($photos, $short_code);
461
462 $out .= $this->layout_gallery($photos,
463 [
464 'title_position' => $title_position,
465 'row_constraints' => $row_constraints,
466 'parent' => 'album',
467 'level_2_meta' => $level_2_meta,
468 ],
469 $short_code,
470 1);
471 }
472 else if (isset($body->error)) {
473 $out .= esc_html__('Failed to get data. Error:', 'photonic')."<br/><code>\n";
474 $out .= $body->error->message;
475 $out .= "</code><br/>\n";
476 return $this->error($out);
477 }
478 }
479 else {
480 $out .= esc_html__('Failed to get data. Error:', 'photonic')."<br/><code>\n";
481 $out .= $body;
482 $out .= "</code><br/>\n";
483 return $this->error($out);
484 }
485
486 return $out;
487 }
488
489 function build_level_1_objects($photos, $short_code, $module_parameters = [], $options = []) {
490 $objects = [];
491 foreach ($photos as $photo) {
492 $object = [];
493 $is_video = false;
494 // if (empty($photo->mimeType) || stripos($photo->mimeType, 'image') !== 0) {
495 if (!empty($photo->mediaMetadata) && !empty($photo->mediaMetadata->video)) {
496 $is_video = true;
497 }
498
499 $media = explode(',', $short_code['media']);
500 $videos_ok = in_array('videos', $media) || in_array('all', $media);
501 $photos_ok = in_array('photos', $media) || in_array('all', $media);
502 if (($is_video && !$videos_ok) || (!$is_video && !$photos_ok)) {
503 continue;
504 }
505
506 $object['id'] = $photo->id;
507
508 $object['thumbnail'] = $photo->baseUrl . "=w{$short_code['thumb_size']}-h{$short_code['thumb_size']}" . ($short_code['crop_thumb'] == 'crop' ? '-c' : '');
509 $object['tile_image'] = $photo->baseUrl . "=w{$short_code['tile_size']}-h{$short_code['tile_size']}";
510 $object['main_image'] = $photo->baseUrl . "=w{$short_code['main_size']}-h{$short_code['main_size']}";
511
512 if ($is_video) {
513 $object['video'] = $photo->baseUrl."={$short_code['video_size']}";
514 $object['mime'] = empty($photo->mimeType) ? 'video/mp4' : $photo->mimeType;
515 }
516 else {
517 $object['download'] = $object['main_image'].'-d';
518 }
519
520 if (!isset($photo->productUrl)) {
521 $object['main_page'] = $object['main_image'];
522 }
523 else {
524 $object['main_page'] = $photo->productUrl;
525 }
526
527 if (!empty($photo->description)) {
528 $object['title'] = $photo->description;
529 }
530 else {
531 $object['title'] = '';
532 }
533 $object['alt_title'] = $object['title'];
534 $object['description'] = $object['title'];
535
536 $objects[] = $object;
537 }
538 return $objects;
539 }
540
541 /**
542 * @param $albums
543 * @param $short_code
544 * @param $remove
545 * @param $pagination
546 * @return array
547 */
548 private function build_level_2_objects($albums, $short_code, $remove, &$pagination) {
549 $filter = $short_code['filter'];
550 $filters = empty($filter) ? [] : explode(',', $filter);
551 $processed = [];
552
553 $objects = [];
554 foreach ($albums as $album) {
555 if (!empty($filters) && ((!in_array($album->id, $filters) && strtolower($short_code['filter_type']) !== 'exclude') ||
556 (in_array($album->id, $filters) && strtolower($short_code['filter_type']) === 'exclude'))) {
557 continue;
558 }
559
560 if (in_array($album->id, $remove)) {
561 continue;
562 }
563
564 $object = $this->process_album($album, $short_code);
565 if (!empty($object)) {
566 $objects[] = $object;
567 $processed[] = $album->id;
568 }
569 }
570
571 global $photonic_google_chain_queries;
572 if (!empty($pagination['next-token']) && strtolower($short_code['filter_type']) !== 'exclude' && !empty($filters) && count($processed) < count($filters) && !empty($photonic_google_chain_queries)) {
573 $additional = [];
574 if (!empty($short_code['count'])) {
575 $additional['pageSize'] = $short_code['count'];
576 }
577
578 if (!empty($pagination['next-token'])) {
579 $additional['pageToken'] = $pagination['next-token'];
580 }
581
582 $access = $this->access_all_or_shared($short_code);
583 if ($access['shared']) {
584 $query_url = 'https://photoslibrary.googleapis.com/v1/sharedAlbums';
585 }
586 if ($access['all']) {
587 $query_url = 'https://photoslibrary.googleapis.com/v1/albums';
588 }
589
590 if (!empty($query_url)) {
591 global $photonic_google_refresh_token;
592 if (!empty($photonic_google_refresh_token) && !empty($this->access_token)) {
593 $query_url = add_query_arg('access_token', $this->access_token, $query_url);
594 }
595
596 $call_args = [];
597 $call_args['method'] = 'GET';
598 $call_args['body'] = $additional;
599 $call_args['sslverify'] = PHOTONIC_SSL_VERIFY;
600 $response = wp_remote_request($query_url, $call_args);
601 if (!is_wp_error($response)) {
602 $body = wp_remote_retrieve_body($response);
603 $body = json_decode($body);
604 $inner_albums = isset($body->albums) ? $body->albums : $body->sharedAlbums;
605 if (!empty($body->nextPageToken)) {
606 $pagination['next-token'] = $body->nextPageToken;
607 }
608 else {
609 $pagination = [];
610 }
611
612 $remaining = array_diff($filters, $processed);
613 $remaining = implode(',', $remaining);
614 $inner_code = $short_code;
615 $inner_code['filter'] = $remaining;
616 $inner = $this->build_level_2_objects($inner_albums, $inner_code, $remove, $pagination);
617 $objects = array_merge($objects, $inner);
618 }
619 }
620 }
621
622 return $objects;
623 }
624
625 /**
626 * @param $album
627 * @param $short_code
628 * @return array
629 */
630 private function process_album($album, $short_code) {
631 if (empty($album->coverPhotoBaseUrl)) {
632 return null;
633 }
634 $object = [];
635
636 $object['id_1'] = "{$album->id}";
637
638 $object['thumbnail'] = $album->coverPhotoBaseUrl . "=w{$short_code['thumb_size']}-h{$short_code['thumb_size']}" . ($short_code['crop_thumb'] == 'crop' ? '-c' : '');
639 $object['tile_image'] = $album->coverPhotoBaseUrl . "=w{$short_code['tile_size']}-h{$short_code['tile_size']}";
640 $object['main_page'] = '';
641
642 $object['title'] = esc_attr($album->title);
643 $object['counter'] = empty($album->totalMediaItems) ? $album->mediaItemsCount : $album->totalMediaItems;
644 $object['data_attributes'] = [
645 'thumb-size' => $short_code['thumb_size'],
646 'photo-count' => empty($short_code['photo_count']) ? $short_code['count'] : $short_code['photo_count'],
647 'photo-more' => empty($short_code['photo_more']) ? '' : $short_code['photo_more'],
648 'overlay-size' => $short_code['overlay_size'],
649 'overlay-crop' => $short_code['overlay_crop'],
650 'overlay-video-size' => $short_code['overlay_video_size'],
651 ];
652 return $object;
653 }
654
655 private function access_all_or_shared($short_code) {
656 $all_or_shared = [];
657 $access = explode(',', $short_code['access']);
658 if (empty($access) || (in_array('shared', $access) && in_array('not-shared', $access)) || in_array('all', $access)) {
659 $all_or_shared['all'] = true;
660 $all_or_shared['shared'] = false;
661 }
662 else if (count($access) == 1 && in_array('not-shared', $access)) {
663 $all_or_shared['all'] = true;
664 $all_or_shared['shared'] = true;
665 }
666 else if (count($access) == 1 && in_array('shared', $access)) {
667 $all_or_shared['all'] = false;
668 $all_or_shared['shared'] = true;
669 }
670 return $all_or_shared;
671 }
672
673 public function authentication_url() {
674 return 'https://accounts.google.com/o/oauth2/auth';
675 }
676
677 public function access_token_url() {
678 return 'https://accounts.google.com/o/oauth2/token';
679 }
680
681 protected function set_token_validity($validity) {
682 $this->refresh_token_valid = $validity;
683 }
684
685 function execute_helper($args) {
686 $query_url = 'https://photoslibrary.googleapis.com/v1/albums';
687 $parameters = [
688 'access_token' => $this->access_token,
689 'pageSize' => 50,
690 ];
691 if (!empty($args['nextPageToken'])) {
692 $parameters['pageToken'] = sanitize_text_field($args['nextPageToken']);
693 }
694
695 $call_args = [];
696 $call_args['method'] = 'GET';
697 $call_args['body'] = $parameters;
698 $call_args['sslverify'] = PHOTONIC_SSL_VERIFY;
699 $response = wp_remote_request($query_url, $call_args);
700
701 if (!is_wp_error($response)) {
702 if (isset($response['response']) && isset($response['response']['code'])) {
703 if ($response['response']['code'] == 200) {
704 $body = json_decode(wp_remote_retrieve_body($response));
705 if (isset($body->albums) && !empty($body->albums) && is_array($body->albums)) {
706 $albums = $body->albums;
707
708 $ret = "<table>\n";
709 $ret .= "\t<tr>\n";
710 $ret .= "\t\t<th>Album Title</th>\n";
711 $ret .= "\t\t<th>Thumbnail</th>\n";
712 $ret .= "\t\t<th>Album ID</th>\n";
713 $ret .= "\t\t<th>Media Count</th>\n";
714 $ret .= "\t</tr>\n";
715
716 foreach ($albums as $album) {
717 $ret .= "\t<tr>\n";
718 $ret .= "\t\t<td>{$album->title}</td>\n";
719 $ret .= "\t\t<td><img alt='thumbnail' src='{$album->coverPhotoBaseUrl}=w75-h75-c' /></td>\n";
720 $ret .= "\t\t<td>{$album->id}</td>\n";
721 $ret .= "\t\t<td>{$album->mediaItemsCount}</td>\n";
722 $ret .= "\t</tr>\n";
723 }
724
725 if (!empty($body->nextPageToken)) {
726 $ret .= "\t<tr>\n";
727 $ret .= "\t\t<td colspan='4'>\n";
728 $ret .= '<input type="button" value="'.esc_attr__('Load More', 'photonic').'" name="photonic-google-album-more" class="photonic-helper-more" data-photonic-token="'.$body->nextPageToken.'" data-photonic-provider="google"/>';
729 $ret .= "\t\t</td>\n";
730 $ret .= "\t</tr>\n";
731 }
732
733 $ret .= "</table>\n";
734
735 return '<div class="photonic-helper">'.$ret.'</div>';
736 }
737 else {
738 return '<div class="photonic-helper">'.esc_html__('No albums found', 'photonic').'</div>';
739 }
740 }
741 else {
742 Photonic::log($response['response']);
743 return '<div class="photonic-helper">'.sprintf(esc_html__('No data returned. Error code %s', 'photonic'), $response['response']['code']).'</div>';
744 }
745 }
746 else {
747 Photonic::log($response);
748 return '<div class="photonic-helper">'.esc_html__('No data returned. Empty response, or empty error code.', 'photonic').'</div>';
749 }
750 }
751 else {
752 return '<div class="photonic-helper">'.$response->get_error_message().'</div>';
753 }
754 }
755
756 function refresh_access_token($refresh_token, $save) {
757 $token = [];
758 $response = Photonic::http($this->access_token_url(), 'POST', [
759 'client_id' => $this->client_id,
760 'client_secret' => $this->client_secret,
761 'refresh_token' => $refresh_token,
762 'grant_type' => 'refresh_token'
763 ]);
764
765 if (!is_wp_error($response)) {
766 $token = $this->parse_token($response);
767 if (!empty($token)) {
768 $token['client_id'] = $this->client_id;
769 }
770 if ($save) {
771 $this->save_token($token);
772 }
773 }
774 return $token;
775 }
776 }