PluginProbe
Photonic Gallery & Lightbox for Flickr, SmugMug & Others / 3.33
Photonic Gallery & Lightbox for Flickr, SmugMug & Others v3.33
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 / Platforms / SmugMug.php

SmugMug.php in Photonic Gallery & Lightbox for Flickr, SmugMug & Others 3.33, at Platforms/SmugMug.php

929 lines 33.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Photonic_Plugin\Platforms;
4
5 use Photonic_Plugin\Components\Album_List;
6 use Photonic_Plugin\Components\Collection;
7 use Photonic_Plugin\Components\Error;
8 use Photonic_Plugin\Components\Header;
9 use Photonic_Plugin\Components\Pagination;
10 use Photonic_Plugin\Components\Photo_List;
11 use Photonic_Plugin\Core\Photonic;
12 use Photonic_Plugin\Components\Album;
13 use Photonic_Plugin\Components\Photo;
14 use WP_Error;
15
16 require_once 'OAuth1.php';
17 require_once 'Level_One_Module.php';
18 require_once 'Level_Two_Module.php';
19 require_once 'Pageable.php';
20 require_once PHOTONIC_PATH . '/Components/Collection.php';
21
22 class SmugMug extends OAuth1 implements Level_One_Module, Level_Two_Module, Pageable {
23 private $highlights_processed;
24 private static $instance = null;
25
26 protected function __construct() {
27 parent::__construct();
28 global $photonic_smug_api_key, $photonic_smug_api_secret, $photonic_smug_access_token, $photonic_smug_disable_title_link, $photonic_smug_token_secret, $photonic_smug_show_buy_link;
29
30 $this->api_key = !empty($photonic_smug_api_key) ? trim($photonic_smug_api_key) : '86MZ8N8TqJf5x2fQ4FRWXRtJ3C6Jm7XV';
31 $this->api_secret = trim($photonic_smug_api_secret);
32 $this->token = trim($photonic_smug_access_token);
33 $this->token_secret = trim($photonic_smug_token_secret);
34 $this->provider = 'smug';
35 $this->link_lightbox_title = empty($photonic_smug_disable_title_link);
36 $this->oauth_version = '1.0a';
37 $this->base_url = 'https://api.smugmug.com/api/v2/';
38 $this->show_buy_link = !empty($photonic_smug_show_buy_link);
39
40 $this->doc_links = [
41 'general' => 'https://aquoid.com/plugins/photonic/smugmug/',
42 'photos' => 'https://aquoid.com/plugins/photonic/smugmug/smugmug-photos/',
43 'albums' => 'https://aquoid.com/plugins/photonic/smugmug/smugmug-albums/',
44 'tree' => 'https://aquoid.com/plugins/photonic/smugmug/smugmug-tree/',
45 'folders' => 'https://aquoid.com/plugins/photonic/smugmug/folders/',
46 ];
47
48 $this->set_oauth_done();
49 $this->highlights_processed = [];
50 }
51
52 /**
53 * The main gallery builder for SmugMug. SmugMug takes the following parameters:
54 * - nick_name = The nickname of the user. This is mandatory for SmugMug.
55 * - view = tree | albums | album | images. If left blank, a value of 'tree' is assumed.
56 * - columns = The number of columns to show the output in
57 * - album or album_key = The album slug, which is the AlbumKey. This is needed if view='album' or 'images'. Prior to version 1.57 album_id was required
58 * - empty = true | false. If true, empty albums and categories are returned in the response, otherwise they are ignored.
59 * - columns = The number of columns to return the output in. Optional.
60 *
61 * @param array $attr
62 * @return array
63 */
64 public function get_gallery_images($attr = []): array {
65 global $photonic_smug_title_caption, $photonic_smug_album_sort_order;
66 global $photonic_smug_thumb_size, $photonic_smug_main_size, $photonic_smug_tile_size, $photonic_smug_video_size, $photonic_smug_media, $photonic_smug_default_user;
67 $this->push_to_stack('Get Gallery Images');
68
69 if (empty($this->api_key)) {
70 $this->pop_from_stack();
71 return [new Error(esc_html__('SmugMug API Key not defined.', 'photonic') . Photonic::doc_link($this->doc_links['general']))];
72 }
73
74 $attr = array_merge(
75 $this->common_parameters,
76 [
77 'caption' => $photonic_smug_title_caption,
78 'thumb_size' => $photonic_smug_thumb_size,
79 'main_size' => $photonic_smug_main_size,
80 'tile_size' => $photonic_smug_tile_size,
81 'video_size' => $photonic_smug_video_size,
82
83 'empty' => 'false',
84 'view' => 'tree',
85 'nick_name' => $photonic_smug_default_user,
86 'start' => 1,
87 'count' => 500,
88 'photo_count' => 500,
89 'media' => $photonic_smug_media,
90 'album_sort_order' => $photonic_smug_album_sort_order,
91 ],
92 $attr
93 );
94
95 /*
96 * if ($attr['main_size'] == 'custom') {
97 if (!empty($attr['custom_main_size'])) {
98 $attr['main_size'] = $attr['custom_main_size'];
99 }
100 else {
101 $attr['main_size'] = 'Largest';
102 }
103 }
104 if ($attr['thumb_size'] == 'custom') {
105 if (!empty($attr['custom_thumb_size'])) {
106 $attr['thumb_size'] = $attr['custom_thumb_size'];
107 }
108 else {
109 $attr['thumb_size'] = 'Tiny';
110 }
111 }
112 if ($attr['tile_size'] == 'custom' && !empty($attr['custom_tile_size'])) {
113 if (!empty($attr['custom_tile_size'])) {
114 $attr['tile_size'] = $attr['custom_tile_size'];
115 }
116 else {
117 $attr['tile_size'] = 'same';
118 }
119 }
120 */
121
122 $attr = array_map('trim', $attr);
123
124 $args = [
125 'APIKey' => $this->api_key,
126 '_accept' => 'application/json'
127 ];
128
129 $chained_calls = [];
130
131 $args['_expandmethod'] = 'inline';
132 $args['_verbosity'] = '1';
133
134 if ('tree' === $attr['view'] || 'albums' === $attr['view']) {
135 if (empty($attr['nick_name'])) {
136 $this->pop_from_stack();
137 return [new Error(sprintf(esc_html__('The %1$s attribute is required for the %2$s and %3$s views.', 'photonic'), '<code>nick_name</code>', '<code>tree</code>', '<code>albums</code>'))];
138 }
139 }
140
141 switch ($attr['view']) {
142 case 'albums':
143 if (!empty($attr['site_password'])) {
144 $chained_calls[] = $this->base_url . 'user/' . $attr['nick_name'] . '!unlock';
145 $args['Password'] = $attr['site_password'];
146 }
147
148 $chained_calls[] = $this->base_url . 'user/' . $attr['nick_name'] . '!albums';
149 $args['_expand'] = 'HighlightImage.ImageSizes,HighlightImage.ImageSizeDetails,HighlightImage.LargestImage';
150 $args['Order'] = $attr['album_sort_order'];
151
152 if (500 === absint($attr['count'])) {
153 $attr['count'] = 100;
154 $attr['photo_count'] = 500;
155 }
156
157 break;
158
159 case 'album':
160 case 'images':
161 $album_field = '';
162 $is_album = true;
163 if (!empty($attr['album_key'])) {
164 $album_field = $attr['album_key'];
165 }
166 elseif (!empty($attr['album'])) {
167 if (stripos($attr['album'], '_') === false) {
168 $album_field = $attr['album'];
169 }
170 else {
171 $album_field = substr($attr['album'], stripos($attr['album'], '_') + 1);
172 }
173 }
174 else {
175 $is_album = false;
176 }
177
178 if (!empty($attr['password']) || !empty($attr['site_password'])) {
179 $args['Password'] = !empty($attr['password']) ? $attr['password'] : $attr['site_password'];
180 $chained_calls[] = $this->base_url . 'album/' . $album_field . '!unlock';
181 }
182
183 if (!empty($attr['text'])) {
184 $args['Text'] = $attr['text'];
185 }
186 if (!empty($attr['keywords'])) {
187 $keywords = explode(',', $attr['keywords']);
188 $keywords = wp_json_encode($keywords);
189 $args['Keywords'] = $keywords;
190 }
191 if (!empty($attr['sort_method'])) {
192 $args['SortMethod'] = $attr['sort_method'];
193 }
194 if (!empty($attr['sort_order'])) {
195 $args['SortDirection'] = $attr['sort_order'];
196 }
197
198 /*
199 Weird issue: If _expand is called with a comma-separated list, and is passed in $args[], it doesn't expand everything.
200 So, for comma-separated values, _expand is appended to the URL.
201 However, if we do this, and OAuth is being used, the signature generation gets messed up, as the signature is generated with the "?_expand=...",
202 which doesn't match the signature for when we make a call with $signed_args, because SmugMug internally parses out the URL and puts _expand=...
203 somewhere in the middle. So, to fix, for authenticated calls we are separating out the _expand parameter and making two calls. Additionally we are
204 moving the parameter from the URL to the array in the generate_signature method.
205
206 Update, v1.59 - Using _expand and combining both the expansions prevents the usage of the <code>start</code> and <code>count</code>
207 attributes from the album node. So, splitting the call anyway, to introduce the album!images node...
208 */
209 if ($is_album) {
210 $chained_calls[] = $this->base_url . 'album/' . $album_field . '?_expand=HighlightImage.ImageSizes,HighlightImage.ImageSizeDetails,HighlightImage.LargestImage';
211 // if ('images' === $attr['view']) { // Fix for https://wordpress.org/support/topic/smugmug-filter-on-keywords-not-working-for-me/
212 if (!empty($args['Keywords']) || !empty($args['Text'])) { // Fix for https://wordpress.org/support/topic/smugmug-filter-on-keywords-not-working-for-me/
213 $args['Scope'] = 'https://api.smugmug.com/api/v2/album/' . $album_field;
214 }
215 else {
216 $chained_calls[] = $this->base_url . 'album/' . $album_field . '!images?_expand=ImageSizes,ImageSizeDetails,LargestImage';
217 }
218 }
219 elseif (!empty($attr['nick_name']) || !empty($attr['folder'])) {
220 if (!empty($attr['folder'])) {
221 $args['Scope'] = 'https://api.smugmug.com/api/v2/node/' . $attr['folder'];
222 }
223 else {
224 $args['Scope'] = 'https://api.smugmug.com/api/v2/user/' . $attr['nick_name'];
225 }
226 }
227
228 if (!empty($args['Scope'])) {
229 // Only search if you have view='images'
230 $chained_calls[] = 'https://api.smugmug.com/api/v2/image!search?_expand=ImageSizes,ImageSizeDetails,LargestImage';
231 }
232
233 break;
234
235 case 'folder':
236 if (!empty($attr['folder'])) {
237 $this->prepare_chained_calls_for_folder($chained_calls, $attr['folder'], $attr['count']);
238 }
239 break;
240
241 case 'tree':
242 default:
243 $this->push_to_stack('Initial user tree');
244 $initial_call = $this->base_url . 'user/' . $attr['nick_name'];
245 $initial_response = Photonic::http($initial_call, 'GET', $args);
246
247 if (!empty($attr['site_password'])) {
248 $chained_calls[] = $this->base_url . 'user/' . $attr['nick_name'] . '!unlock';
249 $args['Password'] = $attr['site_password'];
250 }
251
252 if (is_array($initial_response['response']) && isset($initial_response['response']['code']) && 200 === $initial_response['response']['code']) {
253 $body = $initial_response['body'];
254 $body = json_decode($body);
255 $body = $body->Response;
256 $node = $body->User->Uris->Node->Uri;
257 $pieces = explode('/', $node);
258 $node = array_pop($pieces);
259 $this->prepare_chained_calls_for_folder($chained_calls, $node, $attr['count']);
260 }
261 $this->pop_from_stack();
262 break;
263 }
264
265 if (!empty($attr['nick_name'])) {
266 $args['NickName'] = $attr['nick_name'];
267 }
268
269 if (!empty($attr['start'])) {
270 $args['start'] = $attr['start'];
271 }
272
273 if (!empty($attr['count'])) {
274 $args['count'] = $attr['count'];
275 }
276 $attr['photo_count'] = empty($attr['photo_count']) ? $attr['count'] : $attr['photo_count'];
277
278 $attr['overlay_size'] = empty($attr['overlay_size']) ? $attr['thumb_size'] : $attr['overlay_size'];
279 $attr['overlay_video_size'] = empty($attr['overlay_video_size']) ? $attr['video_size'] : $attr['overlay_video_size'];
280
281 $header_display = $this->get_header_display($attr);
282 $attr['header_display'] = $header_display;
283
284 $out = $this->make_chained_calls($chained_calls, $args, $attr);
285 $this->pop_from_stack();
286
287 if (!empty($this->stack_trace[$this->gallery_index])) {
288 $out[] = $this->stack_trace[$this->gallery_index];
289 }
290 return $out;
291 }
292
293 /**
294 * Runs a sequence of web-service calls to get information. Most often a single web-service call with the "Extras" parameter suffices for SmugMug.
295 * But there are some scenarios, e.g. clicking on an album to get a popup of all images in that album, where you need to chain the calls for the header.
296 *
297 * @param $chained_calls
298 * @param $smug_args
299 * @param $short_code
300 * @return array
301 */
302 private function make_chained_calls($chained_calls, $smug_args, $short_code): array {
303 $this->push_to_stack('Make chained calls');
304 $header_done = false;
305
306 if (is_array($chained_calls) && count($chained_calls) > 0) {
307 $components = [];
308
309 $cookies = [];
310 $header = null;
311
312 foreach ($chained_calls as $chained_call) {
313 $this->push_to_stack("Chained call: $chained_call");
314 if (false !== stripos($chained_call, '!unlock')) {
315 $response = Photonic::http($chained_call, 'POST', $smug_args);
316 if (!is_wp_error($response)) {
317 if (is_array($response['response']) && isset($response['response']['code']) && 200 === $response['response']['code']) {
318 $cookies = $response['cookies'];
319 foreach ($response['cookies'] as $cookie) {
320 $cookies[$cookie->name] = $cookie->value;
321 }
322 }
323 }
324 else {
325 $this->pop_from_stack(); // 'Chained call'
326 $this->pop_from_stack(); // 'Make chained calls'
327 return [$this->wp_error_message($response)];
328 }
329 }
330
331 // The following is NOT an "else" because we are modifying the headers with the cookies, if required
332 if (false === stripos($chained_call, '!unlock')) {
333 $this->push_to_stack('Making call');
334 if ($this->oauth_done && empty($cookies)) {
335 $signed_args = $this->sign_call($chained_call, 'GET', $smug_args);
336 $response = Photonic::http($chained_call, 'GET', $signed_args, null, 90, false, [], $cookies);
337 }
338 else {
339 $response = Photonic::http($chained_call, 'GET', $smug_args, null, 90, false, [], $cookies);
340 }
341
342 $this->pop_from_stack();
343
344 if (!is_wp_error($response)) {
345 $body = $response['body'];
346 $body = json_decode($body);
347
348 if (isset($body->Code) && 200 === $body->Code) {
349 $body = $body->Response;
350 if (false !== stripos($chained_call, '!albums')) {
351 $albums = $body->Album;
352 if (is_array($albums) && count($albums) > 0) {
353 $filter = [];
354 if (!empty($short_code['filter'])) {
355 $filter = explode(',', $short_code['filter']);
356 }
357 $components[] = $this->process_albums($albums, '', $filter, $short_code, $body->Pages);
358 }
359 }
360 elseif ((false !== stripos($chained_call, '/album/') || (false !== stripos($chained_call, '/image!search') && false !== stripos($chained_call, rawurlencode('/api/v2/album/')))) && false === stripos($chained_call, '!unlock')) {
361 $password_check_passed = true;
362 if (isset($body->Album)) {
363 $album = $body->Album;
364 $password_check_passed = ('Password' === $album->SecurityType && isset($album->Uris->AlbumImages)) || 'Password' !== $album->SecurityType;
365
366 global $photonic_smug_disable_title_link, $photonic_smug_hide_album_thumbnail, $photonic_smug_hide_album_title, $photonic_smug_hide_album_photo_count;
367 $hidden = [
368 'thumbnail' => !empty($photonic_smug_hide_album_thumbnail),
369 'title' => !empty($photonic_smug_hide_album_title),
370 'counter' => !empty($photonic_smug_hide_album_photo_count),
371 ];
372 $counters = ['photos' => $album->ImageCount ?? 0];
373
374 $display = $short_code['display'] ?? 'local';
375
376 if (!$header_done) {
377 $header = new Header();
378 $header->title = wp_kses_post($album->Name);
379 $header->page_url = $album->WebUri;
380 $header->description = wp_kses_post($album->Description ?? '');
381
382 if (isset($album->Uris->HighlightImage->Image)) {
383 $header->thumb_url = $album->Uris->HighlightImage->Image->ThumbnailUrl;
384 }
385 elseif (isset($album->Uris->AlbumImages->AlbumImage) && is_array($album->Uris->AlbumImages->AlbumImage)) {
386 $rand = wp_rand(0, $album->ImageCount - 1);
387 $header->thumb_url = $album->Uris->AlbumImages->AlbumImage[$rand]->ThumbnailUrl;
388 }
389
390 $header->header_for = 'album';
391 $header->hidden_elements = $this->get_hidden_headers($short_code['header_display'], $hidden);
392 $header->counters = $counters;
393 $header->enable_link = empty($photonic_smug_disable_title_link);
394 $header->display_location = $display;
395
396 $header_done = true;
397 }
398 }
399 if ($password_check_passed) {
400 $comp = $this->process_images($response, $short_code, $header);
401 $components = array_merge($components, $comp);
402 }
403 else {
404 $components[] = $this->password_protected;
405 }
406 }
407 elseif (false !== stripos($chained_call, '/user/')) {
408 $root_node = $body->User->Uris->Node->Node;
409 $components[] = $this->get_collection($root_node, $short_code);
410 }
411 elseif (false !== stripos($chained_call, '/node/')) {
412 $components[] = $this->get_collection($body->Node, $short_code);
413 }
414 elseif (false !== stripos($chained_call, '/image!search')) {
415 $comp = $this->process_images($response, $short_code, $header);
416 $components = array_merge($components, $comp);
417 }
418 }
419 }
420 else {
421 $this->pop_from_stack(); // 'Chained call'
422 $this->pop_from_stack(); // 'Make chained calls'
423 return [$this->wp_error_message($response)];
424 }
425 }
426 $this->pop_from_stack();
427 }
428 $this->pop_from_stack();
429 return $components;
430 }
431 $this->pop_from_stack();
432 return [];
433 }
434
435 public function get_config_object($count = 500, $levels = 5): array {
436 $base = [
437 'expand' => [
438 'HighlightImage' => [
439 'expand' => [
440 'ImageSizes' => [],
441 'ImageSizeDetails' => [],
442 'LargestImage' => [],
443 ],
444 ],
445 'NodeCoverImage' => [
446 'expand' => [
447 'ImageSizes' => [],
448 'ImageSizeDetails' => [],
449 'LargestImage' => [],
450 ],
451 ],
452 'ChildNodes' => [
453 'args' => [
454 'count' => $count
455 ],
456 ],
457 ],
458 ];
459
460 $level = 1;
461 $combined = $base;
462 while ($level < absint($levels)) {
463 $combined['expand']['ChildNodes']['expand'] = $combined['expand'];
464 $level++;
465 }
466 return $combined;
467 }
468
469 private function prepare_chained_calls_for_folder(&$chained_calls, $folder_node, $count) {
470 $config = $this->get_config_object($count);
471 $config_str = wp_json_encode($config);
472
473 if (!empty($folder_node)) {
474 $chained_calls[] = $this->base_url . 'node/' . $folder_node . '?_config=' . $config_str;
475 }
476 }
477
478 /**
479 * @param $node
480 * @param $short_code
481 * @param string $indent
482 * @param int $level
483 * @return Collection
484 */
485 private function get_collection($node, $short_code, $indent = '', $level = 0): Collection {
486 $collection = new Collection();
487 $collection->indent = $indent;
488
489 $albums = [];
490 $folders = [];
491
492 if (is_array($node)) {
493 foreach ($node as $child) {
494 if ('Album' === $child->Type) {
495 if (!in_array($child->NodeID . '-' . $this->gallery_index, $this->highlights_processed, true)) {
496 $this->highlights_processed[] = $child->NodeID . '-' . $this->gallery_index;
497 $albums[] = $child;
498 }
499 }
500 elseif ('Folder' === $child->Type) {
501 $folders[] = $child;
502 }
503 }
504 }
505 elseif ('Folder' === $node->Type) {
506 if (isset($node->Uris->ChildNodes->Node)) {
507 $child_nodes = $node->Uris->ChildNodes->Node;
508 foreach ($child_nodes as $child) {
509 if ('Album' === $child->Type) {
510 if (!in_array($child->NodeID . '-' . $this->gallery_index, $this->highlights_processed, true)) {
511 $this->highlights_processed[] = $child->NodeID . '-' . $this->gallery_index;
512 }
513 else {
514 continue;
515 }
516
517 if (isset($child->Uris->Album->Album)) {
518 $albums[] = $child->Uris->Album->Album; // Tree?
519 }
520 else {
521 $albums[] = $child; // Node?
522 }
523 }
524 elseif ('Folder' === $child->Type) {
525 $folders[] = $child;
526 }
527 }
528 }
529 }
530
531 if (count($albums) > 0 || count($folders) > 0) {
532 if (!is_array($node)) {
533 $hidden = ['thumbnail' => true, 'title' => false, 'counter' => false];
534
535 $header = new Header();
536 $header->title = wp_kses_post($node->Name);
537 $header->description = wp_kses_post($node->Description ?? '');
538 $header->header_for = 'folder';
539 $header->hidden_elements = $this->get_hidden_headers($short_code['header_display'], $hidden);
540 $header->enable_link = false;
541
542 $collection->header = $header;
543 }
544 }
545
546 if (count($albums) > 0) {
547 $collection->album_list = $this->process_albums($albums, $indent . "\t\t", [], $short_code, null);
548 }
549
550 if (count($folders) > 0) {
551 foreach ($folders as $folder) {
552 $collection->collections[] = $this->get_collection($folder, $short_code, $indent . "\t\t", $level + 1);
553 }
554 }
555
556 return $collection;
557 }
558
559 /**
560 * Parse an array of album objects returned by the SmugMug API, then return an appropriate response.
561 *
562 * @param $albums
563 * @param string $indent
564 * @param array $album_filter
565 * @param $short_code
566 * @param $pages
567 * @return Album_List
568 */
569 private function process_albums($albums, $indent, $album_filter, $short_code, $pages): Album_List {
570 global $photonic_smug_albums_album_per_row_constraint, $photonic_smug_albums_album_constrain_by_count, $photonic_smug_albums_album_title_display, $photonic_smug_hide_albums_album_photos_count_display, $photonic_gallery_template_page;
571 $objects = $this->build_level_2_objects($albums, $short_code, $album_filter);
572
573 $pagination = $this->get_pagination($pages);
574 $row_constraints = ['constraint-type' => $photonic_smug_albums_album_per_row_constraint, 'count' => $photonic_smug_albums_album_constrain_by_count];
575
576 $album_list = new Album_List($short_code);
577 $album_list->albums = $objects;
578 $album_list->title_position = $photonic_smug_albums_album_title_display;
579 $album_list->row_constraints = $row_constraints;
580 $album_list->type = 'albums';
581 $album_list->singular_type = 'album';
582 $album_list->level_1_count_display = $photonic_smug_hide_albums_album_photos_count_display;
583 $album_list->pagination = $pagination;
584 $album_list->indent = $indent;
585 $album_list->album_opens_gallery = ('page' === $short_code['popup'] && !empty($photonic_gallery_template_page) && is_string(get_post_status($photonic_gallery_template_page)));
586
587 return $album_list;
588 }
589
590 /**
591 * Takes a response, then parses out the images from that response and returns a set of thumbnails for it. This method handles
592 * local / template / lightbox images as well as images in a modal panel.
593 *
594 * @param $response
595 * @param array $short_code
596 * @param $header
597 * @return array
598 */
599 private function process_images($response, $short_code, $header): array {
600 global $photonic_smug_photos_per_row_constraint, $photonic_smug_photos_constrain_by_count,
601 $photonic_smug_photo_title_display, $photonic_smug_photo_pop_title_display;
602 $body = $response['body'];
603 $body = json_decode($body);
604
605 $components = [];
606 if ('Ok' === $body->Message) {
607 $body = $body->Response;
608 if (isset($body->Album)) {
609 $album = $body->Album;
610 $images = $album->Uris->AlbumImages;
611 }
612 elseif (isset($body->Image)) {
613 $images = $body->Image;
614 }
615 else {
616 $images = $body;
617 }
618
619 $photo_objects = [];
620 if (isset($images->AlbumImage)) {
621 $photo_objects = $this->build_level_1_objects($images->AlbumImage, $short_code);
622 }
623 if (is_array($images)) {
624 $photo_objects = $this->build_level_1_objects($images, $short_code);
625 }
626
627 $pages = isset($images->Pages) ? $images->Pages : (is_array($images) && isset($body->Pages) ? $body->Pages : null);
628 $pagination = $this->get_pagination($pages);
629
630 if (!empty($photo_objects)) {
631 if (!empty($header)) {
632 $components[] = $header;
633 }
634
635 if (isset($short_code['display']) && 'modal' === $short_code['display']) {
636 $row_constraints = ['constraint-type' => 'padding'];
637 $title_position = $photonic_smug_photo_pop_title_display;
638 }
639 else {
640 $row_constraints = ['constraint-type' => $photonic_smug_photos_per_row_constraint, 'count' => $photonic_smug_photos_constrain_by_count];
641 $title_position = $photonic_smug_photo_title_display;
642 }
643
644 $photo_list = new Photo_List($short_code);
645 $photo_list->photos = $photo_objects;
646
647 $photo_list->title_position = $title_position;
648 $photo_list->row_constraints = $row_constraints;
649 $photo_list->parent = 'album';
650 $photo_list->pagination = $pagination;
651
652 $components[] = $photo_list;
653 }
654 }
655 return $components;
656 }
657
658 private function construct_custom_size($custom, $sizes): string {
659 if (isset($sizes->LargeImageUrl)) {
660 $custom = str_replace('ImageUrl', '', $custom);
661 $large = $sizes->LargeImageUrl;
662 $split = explode('/', $large);
663 $split[count($split) - 2] = $custom;
664 $image_parts = explode('.', $split[count($split) - 1]);
665 $image_name_parts = explode('-', $image_parts[count($image_parts) - 2]);
666 $image_name_parts[count($image_name_parts) - 1] = $custom;
667 $image_parts[count($image_parts) - 2] = implode('-', $image_name_parts);
668 $split[count($split) - 1] = implode('.', $image_parts);
669 return implode('/', $split);
670 }
671 return '';
672 }
673
674 public function build_level_1_objects($response, array $short_code, $module_parameters = [], $options = []): array {
675 $thumb = "{$short_code['thumb_size']}ImageUrl";
676 $main = "{$short_code['main_size']}ImageUrl";
677 $tile = (empty($short_code['tile_size']) || 'same' === $short_code['tile_size']) ? $main : "{$short_code['tile_size']}ImageUrl";
678
679 $media = explode(',', $short_code['media']);
680 $videos_ok = in_array('videos', $media, true) || in_array('all', $media, true);
681 $photos_ok = in_array('photos', $media, true) || in_array('all', $media, true);
682
683 $photo_objects = [];
684 $sizes = [
685 'thumb_size' => sanitize_text_field($short_code['thumb_size']),
686 'main_size' => sanitize_text_field($short_code['main_size']),
687 'tile_size' => sanitize_text_field((empty($short_code['tile_size']) || 'same' === $short_code['tile_size']) ? $short_code['main_size'] : $short_code['tile_size']),
688 ];
689
690 if (is_array($response) && count($response) > 0) {
691 foreach ($response as $image) {
692 if ((empty($image->IsVideo) && !$photos_ok) || (!empty($image->IsVideo) && !$videos_ok)) {
693 continue;
694 }
695 $image_sizes = $image->Uris->ImageSizes->ImageSizes;
696
697 $photonic_photo = new Photo();
698 $photonic_photo->thumbnail = esc_url($image_sizes->{$thumb} ?? $this->construct_custom_size($thumb, $image_sizes));
699 $photonic_photo->main_image = esc_url($image_sizes->{$main} ?? $this->construct_custom_size($main, $image_sizes)); // $image_sizes->{$main};
700 $photonic_photo->tile_image = esc_url($image_sizes->{$tile} ?? $this->construct_custom_size($tile, $image_sizes)); // $image_sizes->{$tile};
701
702 $this->calculate_sizes($image, $photonic_photo, $sizes);
703
704 $photonic_photo->title = wp_kses_post($image->Title);
705 $photonic_photo->alt_title = $photonic_photo->title;
706 $photonic_photo->description = wp_kses_post($image->Caption);
707 if (isset($image->WebUri)) {
708 $photonic_photo->main_page = esc_url($image->WebUri);
709 $photonic_photo->buy_link = esc_url($image->WebUri . '/buy');
710 }
711
712 if (!empty($image->IsVideo)) {
713 $photonic_photo->video = esc_url($image_sizes->{$short_code['video_size'] . 'VideoUrl'});
714 }
715
716 if (isset($image->ArchivedUri)) {
717 $photonic_photo->download = esc_url($image->ArchivedUri);
718 }
719
720 $photonic_photo->id = $image->ImageKey;
721
722 if (!empty($image->DateTimeOriginal)) {
723 $photonic_photo->taken_on = sanitize_text_field($image->DateTimeOriginal);
724 }
725
726 if (!empty($image->DateTimeUploaded)) {
727 $photonic_photo->uploaded_on = sanitize_text_field($image->DateTimeUploaded);
728 }
729
730 $photo_objects[] = $photonic_photo;
731 }
732 }
733 return $photo_objects;
734 }
735
736 public function build_level_2_objects($objects_or_response, array $short_code, array $filter_list = [], array &$options = [], ?Pagination &$pagination = null): array {
737 global $photonic_smug_hide_password_protected_thumbnail, $photonic_gallery_template_page;
738
739 $named_albums = $filter_list;
740 foreach ($named_albums as $idx => $album) {
741 if (false !== stripos($album, '_')) {
742 $named_albums[$idx] = substr($album, stripos($album, '_') + 1);
743 }
744 }
745
746 $objects = [];
747 if (is_array($objects_or_response) && count($objects_or_response) > 0) {
748 $main = "{$short_code['main_size']}ImageUrl";
749 $tile = (empty($short_code['tile_size']) || 'same' === $short_code['tile_size']) ? $main : "{$short_code['tile_size']}ImageUrl";
750
751 $sizes = [
752 'thumb_size' => $short_code['thumb_size'],
753 'tile_size' => (empty($short_code['tile_size']) || 'same' === $short_code['tile_size']) ? $short_code['main_size'] : $short_code['tile_size'],
754 ];
755
756 foreach ($objects_or_response as $album) {
757 if (!empty($photonic_smug_hide_password_protected_thumbnail) && isset($album->SecurityType) && 'None' !== $album->SecurityType) {
758 continue;
759 }
760
761 $highlight = $album->Uris->HighlightImage;
762 if ((!isset($album->ImageCount) || 0 !== $album->ImageCount)) {
763 $photonic_album = new Album();
764
765 if (isset($highlight->Image)) {
766 $thumbURL = esc_url($highlight->Image->Uris->ImageSizes->ImageSizes->{$short_code['thumb_size'] . 'ImageUrl'});
767 $tileURL = esc_url($highlight->Image->Uris->ImageSizes->ImageSizes->$tile);
768 $this->calculate_sizes($highlight->Image, $photonic_album, $sizes);
769 }
770 else {
771 if (in_array($short_code['thumb_size'], ['Small', 'Thumb', 'Tiny', 'Sm', 'Th', 'Ti'], true)) {
772 $thumbURL = esc_url(trailingslashit(PHOTONIC_URL) . 'include/images/placeholder-' . substr($short_code['thumb_size'], 0, 2) . '.png');
773 }
774 else {
775 $thumbURL = esc_url(trailingslashit(PHOTONIC_URL) . 'include/images/placeholder.png');
776 }
777 $tileURL = esc_url(trailingslashit(PHOTONIC_URL) . 'include/images/placeholder.png');
778 }
779
780 if (isset($album->AlbumKey)) {
781 $photonic_album->id = $album->AlbumKey;
782 }
783 else {
784 $uri = $album->Uris->Album->Uri;
785 $uri = explode('/', $uri);
786 $photonic_album->id = $uri[count($uri) - 1];
787 }
788 $photonic_album->thumbnail = $thumbURL;
789 $photonic_album->tile_image = $tileURL;
790
791 $photonic_album->main_page = esc_url($album->WebUri);
792
793 $photonic_album->title = wp_kses_post($album->Name);
794 $photonic_album->description = !empty($album->Description) ? wp_kses_post($album->Description) : '';
795
796 if ('page' === $short_code['popup'] && !empty($photonic_gallery_template_page) && is_string(get_post_status($photonic_gallery_template_page))) {
797 $internal_short_code = $short_code;
798 $internal_short_code['view'] = 'album';
799 $internal_short_code['album'] = $photonic_album->id;
800 $internal_short_code['layout'] = empty($short_code['photo_layout']) ? $short_code['layout'] : $short_code['photo_layout'];
801
802 $photonic_album->gallery_url = $this->get_gallery_url($internal_short_code, ['title' => $photonic_album->title]);
803 }
804
805 if (isset($album->ImageCount)) {
806 $photonic_album->counter = $album->ImageCount;
807 }
808
809 if (isset($album->SecurityType) && 'Password' === $album->SecurityType) {
810 $photonic_album->classes = ['photonic-smug-passworded'];
811 $photonic_album->passworded = 1;
812 }
813
814 if (empty($named_albums) || (!empty($named_albums) && in_array($album->AlbumKey, $named_albums, true) && 'exclude' !== strtolower($short_code['filter_type'])) ||
815 (!empty($named_albums) && !in_array($album->AlbumKey, $named_albums, true) && 'exclude' === strtolower($short_code['filter_type']))) {
816 $objects[] = $photonic_album;
817 }
818 }
819 }
820 }
821 return $objects;
822 }
823
824 private function calculate_sizes($image, &$photo_object, $sizes) {
825 if (isset($image->Uris->ImageSizeDetails) && isset($image->Uris->LargestImage)) {
826 $image_size_details = $image->Uris->ImageSizeDetails;
827 $largest_image = $image->Uris->LargestImage;
828
829 foreach ($sizes as $size => $size_value) {
830 if ((isset($largest_image->LargestImage) && 'Largest' === $size_value) || isset($image_size_details->ImageSizeDetails->{'ImageSize' . $size_value})) {
831 if ('Largest' === $size_value) {
832 $ref = $largest_image->LargestImage;
833 }
834 else {
835 $ref = $image_size_details->ImageSizeDetails->{'ImageSize' . $size_value};
836 }
837 $photo_object->{$size} = [
838 'w' => $ref->Width,
839 'h' => $ref->Height,
840 ];
841 }
842 }
843 }
844 }
845
846 /**
847 * Access Token URL
848 *
849 * @return string
850 */
851 public function access_token_URL(): string {
852 return 'https://api.smugmug.com/services/oauth/1.0a/getAccessToken';
853 }
854
855 /**
856 * Authenticate URL
857 *
858 * @return string
859 */
860 public function authenticate_URL(): string {
861 return 'https://api.smugmug.com/services/oauth/1.0a/authorize';
862 }
863
864 /**
865 * Authorize URL
866 *
867 * @return string
868 */
869 public function authorize_URL(): string {
870 return 'https://api.smugmug.com/services/oauth/1.0a/authorize';
871 }
872
873 /**
874 * Request Token URL
875 *
876 * @return string
877 */
878 public function request_token_URL(): string {
879 return 'https://api.smugmug.com/services/oauth/1.0a/getRequestToken';
880 }
881
882 /**
883 * Tests to see if the OAuth Access Token that is cached is still valid. This is important because a user might have manually revoked
884 * access for your app through the provider's control panel.
885 *
886 * @param $token
887 * @return array|WP_Error
888 */
889 public function check_access_token() {
890 $signed_parameters = $this->sign_call($this->base_url . 'user/sayontan', 'GET', []);
891 $end_point = $this->base_url . 'user/sayontan?' . self::build_query($signed_parameters);
892 return Photonic::http($end_point, 'GET', null);
893 }
894
895 /**
896 * Takes the response for the "Check access token", then tries to determine whether the check was successful or not.
897 *
898 * @param $response
899 * @return bool
900 */
901 public function is_access_token_valid($response): bool {
902 if (is_wp_error($response)) {
903 return false;
904 }
905
906 $response = $response['response'];
907 if (200 === $response['code']) {
908 return true;
909 }
910 return false;
911 }
912
913 /**
914 * @param $pages
915 * @param array $short_code
916 * @return Pagination
917 */
918 public function get_pagination($pages, array $short_code = []): Pagination {
919 $pagination = new Pagination();
920 if (!empty($pages)) {
921 $pagination->total = $pages->Total;
922 $pagination->start = $pages->Start;
923 $pagination->end = $pages->Start + $pages->Count - 1;
924 $pagination->per_page = $pages->RequestedCount;
925 }
926 return $pagination;
927 }
928 }
929