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 / Zenfolio.php

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

1,257 lines 45.6 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\Components\Single_Photo;
12 use Photonic_Plugin\Core\Photonic;
13 use Photonic_Plugin\Components\Album;
14 use Photonic_Plugin\Components\Photo;
15 use WP_Error;
16 use WpOrg\Requests\Requests;
17
18 require_once 'Base.php';
19 require_once 'Level_One_Module.php';
20 require_once 'Level_Two_Module.php';
21 require_once PHOTONIC_PATH . '/Components/Collection.php';
22
23 /**
24 * Processor for Zenfolio photos. This extends the Photonic_Plugin\Platforms\Base class and defines methods local to Zenfolio.
25 */
26 class Zenfolio extends Base implements Level_One_Module, Level_Two_Module {
27 public $user_name;
28 public $user_agent;
29 public $token;
30 public $service_url;
31 public $secure_url;
32 public $unlocked_realms;
33 public $temp_keyring;
34 public $standard_sizes;
35 private static $instance = null;
36
37 protected function __construct() {
38 parent::__construct();
39 global $photonic_zenfolio_disable_title_link;
40 $this->provider = 'zenfolio';
41 $this->user_agent = "Photonic for " . get_home_url();
42 $this->link_lightbox_title = empty($photonic_zenfolio_disable_title_link);
43 $this->service_url = 'https://api.zenfolio.com/api/1.8/zfapi.asmx';
44 $this->secure_url = 'https://api.zenfolio.com/api/1.8/zfapi.asmx';
45 $this->unlocked_realms = [];
46 $this->temp_keyring = '';
47
48 $this->doc_links = [
49 'general' => 'https://aquoid.com/plugins/photonic/zenfolio/',
50 'photos' => 'https://aquoid.com/plugins/photonic/zenfolio/photos/',
51 'photosets' => 'https://aquoid.com/plugins/photonic/zenfolio/photosets/',
52 'groups' => 'https://aquoid.com/plugins/photonic/zenfolio/group/',
53 'hierarchies' => 'https://aquoid.com/plugins/photonic/zenfolio/group-hierarchy/',
54 ];
55 $this->perform_back_end_authentication();
56 $this->standard_sizes = [
57 0 => ['w' => 80, 'h' => 80, 'c' => 0],
58 1 => ['w' => 60, 'h' => 60, 'c' => 1],
59 2 => ['w' => 400, 'h' => 400, 'c' => 0],
60 3 => ['w' => 580, 'h' => 450, 'c' => 0],
61 4 => ['w' => 800, 'h' => 630, 'c' => 0],
62 5 => ['w' => 1100, 'h' => 850, 'c' => 0],
63 6 => ['w' => 1550, 'h' => 960, 'c' => 0],
64 10 => ['w' => 120, 'h' => 120, 'c' => 0],
65 11 => ['w' => 120, 'h' => 120, 'c' => 0],
66 ];
67 }
68
69 /**
70 * Main function that fetches the images associated with the shortcode.
71 *
72 * @param array $attr
73 * @return array
74 */
75 public function get_gallery_images($attr = []): array {
76 global $photonic_zenfolio_thumb_size, $photonic_zenfolio_main_size, $photonic_zenfolio_tile_size, $photonic_zenfolio_title_caption, $photonic_zenfolio_video_size, $photonic_zenfolio_media, $photonic_zenfolio_default_user;
77 $this->push_to_stack('Get Gallery Images');
78
79 $attr = array_merge(
80 $this->common_parameters,
81 [
82 'caption' => $photonic_zenfolio_title_caption,
83 'thumb_size' => $photonic_zenfolio_thumb_size,
84 'main_size' => $photonic_zenfolio_main_size,
85 'video_size' => $photonic_zenfolio_video_size,
86 'tile_size' => $photonic_zenfolio_tile_size,
87
88 'count' => 500,
89 'offset' => 0,
90 'media' => $photonic_zenfolio_media,
91 'structure' => 'nested',
92 'login_name' => $photonic_zenfolio_default_user,
93 ],
94 $attr
95 );
96 $attr = array_map('trim', $attr);
97
98 $attr['limit'] = empty($attr['limit']) ? $attr['count'] : $attr['limit'];
99 $attr['photo_count'] = empty($attr['photo_count']) ? $attr['limit'] : $attr['photo_count'];
100
101 $attr['overlay_size'] = empty($attr['overlay_size']) ? $attr['thumb_size'] : $attr['overlay_size'];
102 $attr['overlay_video_size'] = empty($attr['overlay_video_size']) ? $attr['video_size'] : $attr['overlay_video_size'];
103
104 if (isset($_COOKIE['photonic-zf-keyring'])) {
105 $realms = $this->make_wp_call('KeyringGetUnlockedRealms', ['keyring' => sanitize_text_field(wp_unslash($_COOKIE['photonic-zf-keyring']))]);
106 if (!empty($realms) && !empty($realms->result)) {
107 $this->unlocked_realms = $realms->result;
108 }
109 }
110
111 $chained_methods = [];
112 $zenfolio_params = [];
113 $attr['headers_already_called'] = true;
114 if (!empty($attr['view'])) {
115 switch ($attr['view']) {
116 case 'photos':
117 if (!empty($attr['object_id'])) {
118 $object_id = $attr['object_id'];
119 $chained_methods[] = 'LoadPhoto';
120 $h = stripos($object_id, 'h');
121 $p = stripos($object_id, 'p');
122 if (false !== $h) {
123 $object_id = substr($object_id, $h + 1);
124 $object_id = hexdec($object_id);
125 }
126 elseif (false !== $p) {
127 $object_id = substr($object_id, $p + 1);
128 }
129 elseif (strlen($object_id) === 7) {
130 $object_id = hexdec($object_id);
131 }
132
133 $zenfolio_params['photoId'] = $object_id;
134 $zenfolio_params['level'] = 'Full';
135 }
136 elseif (!empty($attr['text']) || !empty($attr['category_code'])) {
137 $zenfolio_params['searchId'] = '';
138 $zenfolio_params['sortOrder'] = $attr['sort_order'] ?? 'Date'; // Popularity | Date | Rank
139 if (!empty($attr['text'])) {
140 $zenfolio_params['query'] = $attr['text'];
141 $chained_methods[] = 'SearchPhotoByText';
142 }
143 elseif (!empty($attr['category_code'])) {
144 $zenfolio_params['categoryCode'] = $attr['category_code'];
145 $chained_methods[] = 'SearchPhotoByCategory';
146 }
147 $zenfolio_params['offset'] = $attr['offset'];
148 $zenfolio_params['limit'] = $attr['limit'];
149 }
150 elseif (!empty($attr['kind'])) {
151 $zenfolio_params['offset'] = $attr['offset'];
152 $zenfolio_params['limit'] = $attr['limit'];
153 switch ($attr['kind']) {
154 case 'popular':
155 $chained_methods[] = 'GetPopularPhotos';
156 break;
157
158 case 'recent':
159 $chained_methods[] = 'GetRecentPhotos';
160 break;
161
162 default:
163 $this->pop_from_stack();
164 return [
165 new Error(sprintf(esc_html__('Invalid %s parameter.', 'photonic'), '<code>kind</code>') . Photonic::doc_link($this->doc_links['photos']))
166 ];
167 }
168 }
169 else {
170 $this->pop_from_stack();
171 return [
172 new Error(sprintf(esc_html__('The %1$s parameter is required if %2$s is not specified.', 'photonic'), '<code>kind</code>', '<code>object_id</code>') . Photonic::doc_link($this->doc_links['photos']))
173 ];
174 }
175 break;
176
177 case 'photosets':
178 if (!empty($attr['object_id'])) {
179 $object_id = $attr['object_id'];
180 $p = stripos($object_id, 'p');
181 if (false !== $p) {
182 $object_id = substr($object_id, $p + 1);
183 }
184
185 $zenfolio_params['photosetId'] = $object_id;
186 $zenfolio_params['level'] = 'Level2';
187 $zenfolio_params['includePhotos'] = false;
188
189 if (!empty($attr['password']) && empty($realm_id)) {
190 $first_call = $this->make_wp_call('LoadPhotoSet', $zenfolio_params);
191 if (isset($first_call->result) && !empty($first_call->result)) {
192 $photoset = $first_call->result;
193 if (isset($photoset->AccessDescriptor)) {
194 $realm_id = $photoset->AccessDescriptor->Id;
195 }
196 }
197 }
198
199 if (!empty($attr['password']) && !empty($realm_id)) {
200 if (!in_array($realm_id, $this->unlocked_realms, true)) {
201 $attr['headers_already_called'] = empty($attr['panel']); // false;
202 $chained_methods[] = 'KeyringAddKeyPlain';
203 $zenfolio_params['keyring'] = empty($_COOKIE['photonic-zf-keyring']) ? '' : sanitize_text_field(wp_unslash($_COOKIE['photonic-zf-keyring']));
204 $zenfolio_params['realmId'] = $realm_id;
205 $zenfolio_params['password'] = $attr['password'];
206 }
207 }
208
209 $chained_methods[] = 'LoadPhotoSet';
210 $zenfolio_params['startingIndex'] = $attr['offset'];
211 $zenfolio_params['numberOfPhotos'] = $attr['limit'];
212 $chained_methods[] = 'LoadPhotoSetPhotos';
213 }
214 elseif (!empty($attr['login_name'])) {
215 $chained_methods[] = 'LoadGroupHierarchy';
216 $attr['structure'] = 'flat';
217 $zenfolio_params['loginName'] = $attr['login_name'];
218 }
219 elseif (!empty($attr['photoset_type'])) {
220 $photoset_type = $attr['photoset_type'];
221 if (!empty($attr['text']) || !empty($attr['category_code'])) {
222 $zenfolio_params['searchId'] = '';
223 $chained_methods[] = !empty($attr['text']) ? 'SearchSetByText' : 'SearchSetByCategory';
224 }
225 elseif (!empty($attr['kind'])) {
226 switch ($attr['kind']) {
227 case 'popular':
228 $chained_methods[] = 'GetPopularSets';
229 break;
230
231 case 'recent':
232 $chained_methods[] = 'GetRecentSets';
233 break;
234
235 default:
236 $this->pop_from_stack();
237 return [
238 new Error(
239 sprintf(esc_html__('Invalid %s parameter.', 'photonic'), '<code>kind</code>') .
240 Photonic::doc_link($this->doc_links['photosets'])
241 )
242 ];
243 }
244 }
245
246 if ('gallery' === strtolower($photoset_type) || 'galleries' === strtolower($photoset_type)) {
247 $zenfolio_params['type'] = 'Gallery';
248 }
249 elseif ('collection' === strtolower($photoset_type) || 'collections' === strtolower($photoset_type)) {
250 $zenfolio_params['type'] = 'Collection';
251 }
252 else {
253 $this->pop_from_stack();
254 return [
255 new Error(
256 sprintf(esc_html__('Invalid %1$s parameter. Permissible values are %2$s or %3$s.', 'photonic'), '<code>photoset_type</code>', '<code>Gallery</code>', '<code>Collection</code>') .
257 Photonic::doc_link($this->doc_links['photosets'])
258 )
259 ];
260 }
261
262 if (!empty($attr['text']) || !empty($attr['category_code'])) {
263 $zenfolio_params['sortOrder'] = !empty($attr['sort_order']) ? $attr['sort_order'] : (!empty($attr['text']) ? 'Rank' : 'Date');
264 if (!empty($attr['text'])) {
265 $zenfolio_params['query'] = $attr['text'];
266 }
267 elseif (!empty($attr['category_code'])) {
268 $zenfolio_params['categoryCode'] = $attr['category_code'];
269 }
270 }
271 $zenfolio_params['offset'] = $attr['offset'];
272 $zenfolio_params['limit'] = $attr['limit'];
273 }
274 elseif (!empty($attr['filter']) && empty($attr['login_name'])) {
275 $this->pop_from_stack();
276 return [
277 new Error(
278 sprintf(esc_html__('The %1$s parameter is required if %2$s is specified.', 'photonic'), '<code>login_name</code>', '<code>filter</code>') .
279 Photonic::doc_link($this->doc_links['photosets'])
280 )
281 ];
282 }
283 elseif (empty($attr['kind'])) {
284 $this->pop_from_stack();
285 return [
286 new Error(
287 sprintf(esc_html__('The %1$s parameter is required if %2$s or %3$s is not specified.', 'photonic'), '<code>kind</code>', '<code>object_id</code>', '<code>login_name</code>') .
288 Photonic::doc_link($this->doc_links['photosets'])
289 )
290 ];
291 }
292 elseif (empty($attr['photoset_type'])) {
293 $this->pop_from_stack();
294 return [
295 new Error(
296 sprintf(esc_html__('The %1$s parameter is required if %2$s or %3$s is not specified.', 'photonic'), '<code>photoset_type</code>', '<code>object_id</code>', '<code>login_name</code>') .
297 Photonic::doc_link($this->doc_links['photosets'])
298 )
299 ];
300 }
301
302 if (!empty($attr['login_name']) && !empty($attr['category_code'])) {
303 $attr['user_specific_category'] = true;
304 }
305
306 if (!empty($attr['login_name']) && !empty($attr['text'])) {
307 $attr['user_specific_text'] = true;
308 }
309 break;
310
311 case 'hierarchy':
312 if (empty($attr['login_name'])) {
313 $this->pop_from_stack();
314 return [
315 new Error(
316 sprintf(esc_html__('The %s parameter is required.', 'photonic'), '<code>login_name</code>') .
317 Photonic::doc_link($this->doc_links['hierarchies'])
318 )
319 ];
320 }
321 $chained_methods[] = 'LoadGroupHierarchy';
322 $zenfolio_params['loginName'] = $attr['login_name'];
323 break;
324
325 case 'group':
326 if (empty($object_id)) {
327 $this->pop_from_stack();
328 return [
329 new Error(
330 sprintf(esc_html__('The %s parameter is required.', 'photonic'), '<code>object_id</code>') .
331 Photonic::doc_link($this->doc_links['groups'])
332 )
333 ];
334 }
335 $chained_methods[] = 'LoadGroup';
336 $f = stripos($object_id, 'f');
337 if (false !== $f) {
338 $object_id = substr($object_id, $f + 1);
339 }
340 $zenfolio_params['groupId'] = $object_id;
341 $zenfolio_params['level'] = 'Full';
342 $zenfolio_params['includeChildren'] = true;
343 break;
344 }
345 }
346
347 if (!empty($attr['panel'])) {
348 $attr['display'] = 'modal';
349 }
350 else {
351 $attr['display'] = 'local';
352 }
353
354 $header_display = $this->get_header_display($attr);
355 $attr['header_display'] = $header_display;
356
357 $call_return = $this->make_chained_calls($chained_methods, $zenfolio_params, $attr);
358 $this->pop_from_stack();
359
360 if (!empty($this->stack_trace[$this->gallery_index])) {
361 $call_return[] = $this->stack_trace[$this->gallery_index];
362 }
363
364 return $call_return;
365 }
366
367 /**
368 * Calls a Zenfolio method with the passed parameters.
369 *
370 * @param $method
371 * @param null $params
372 * @param null $keyring
373 * @return array|mixed|null|object|string|string[]|WP_Error
374 */
375 private function make_wp_call($method, $params = null, $keyring = null) {
376 $request = $this->prepare_request($method, $params, $keyring);
377 $response = $this->make_wp_request($method, $request);
378 return $response;
379 }
380
381 /**
382 * Makes a sequence of calls to different Zenfolio methods. This is particularly useful in case of authenticated calls, where
383 * first the authentication happens, then the content is displayed, all in the same call.
384 *
385 * @param array $methods
386 * @param array $zenfolio_args
387 * @param array $short_code
388 * @return array
389 */
390 private function make_chained_calls($methods, $zenfolio_args, &$short_code = []) {
391 $this->push_to_stack('Make chained calls');
392 $components = [];
393
394 $keyring = null;
395 $original_params = [];
396 foreach ($zenfolio_args as $param => $value) {
397 $original_params[$param] = $value;
398 }
399
400 $pagination = new Pagination();
401 $pagination->start = $short_code['offset'];
402 $pagination->per_page = $short_code['limit'];
403
404 foreach ($methods as $method) {
405 $this->push_to_stack("Making call for $method");
406 $keyring_params = [];
407 if ('KeyringGetUnlockedRealms' === $method) {
408 $keyring_params['keyring'] = $zenfolio_args['keyring'];
409 $response = $this->make_wp_call($method, $keyring_params);
410 if (isset($response->result)) {
411 $this->unlocked_realms = $response->result;
412 }
413 }
414 elseif ('KeyringAddKeyPlain' === $method) {
415 if (in_array($zenfolio_args['realmId'], $this->unlocked_realms, true)) {
416 continue;
417 }
418 $keyring_params['keyring'] = $zenfolio_args['keyring'];
419 $keyring_params['realmId'] = $zenfolio_args['realmId'];
420 $keyring_params['password'] = $zenfolio_args['password'];
421 $response = $this->make_wp_call($method, $keyring_params);
422
423 if (!empty($response->result)) {
424 // Sometimes the cookie isn't set by the setcookie command (happens when the password is passed as a shortcode parameter
425 // instead of the password prompt)
426 $keyring = $response->result;
427 if (!in_array($keyring_params['realmId'], $this->unlocked_realms, true)) {
428 $this->unlocked_realms[] = $keyring_params['realmId'];
429 $this->temp_keyring = $keyring;
430 }
431
432 if (!$short_code['headers_already_called']) {
433 setcookie('photonic-zf-keyring', $keyring, time() + 60 * 60 * 24, COOKIEPATH);
434 }
435 }
436 else {
437 $components[] = $this->password_protected;
438 break;
439 }
440 }
441 else {
442 foreach ($original_params as $param => $value) {
443 $zenfolio_args[$param] = $value;
444 }
445
446 $keyring_fields = ['keyring', 'realmId', 'password'];
447 foreach ($zenfolio_args as $param => $value) {
448 if (in_array($param, $keyring_fields, true)) {
449 unset($zenfolio_args[$param]);
450 }
451 }
452
453 if ('LoadPhotoSetPhotos' === $method) {
454 unset($zenfolio_args['level']);
455 unset($zenfolio_args['includePhotos']);
456 }
457 elseif ('LoadPhotoSet' === $method) {
458 unset($zenfolio_args['startingIndex']);
459 unset($zenfolio_args['numberOfPhotos']);
460 }
461
462 $response = $this->make_wp_call($method, $zenfolio_args, $keyring);
463 $this->pop_from_stack();
464 $this->push_to_stack('Processing response');
465
466 $components = array_merge($components, $this->process_response($method, $response, $short_code, $pagination));
467 $this->pop_from_stack();
468 }
469 }
470
471 $this->pop_from_stack();
472 return $components;
473 }
474
475 /**
476 * Routing function that takes the response and redirects it to the appropriate processing function.
477 *
478 * @param $method
479 * @param $response
480 * @param array $short_code
481 * @param Pagination $pagination
482 * @return string|array
483 */
484 private function process_response($method, $response, &$short_code, Pagination &$pagination) {
485 $header_display = $short_code['header_display'];
486
487 if (!empty($response->result)) {
488 $result = $response->result;
489 $components = [];
490
491 switch ($method) {
492 case 'GetPopularPhotos':
493 case 'GetRecentPhotos':
494 case 'SearchPhotoByText':
495 case 'SearchPhotoByCategory':
496 $pagination->total = $result->TotalCount;
497 $components[] = $this->process_photos($result, 'stream', $short_code, $pagination);
498 break;
499
500 case 'LoadPhoto':
501 $component = $this->process_photo($result, $short_code);
502 if (!is_null($component)) {
503 $components[] = $component;
504 }
505 break;
506
507 case 'GetPopularSets':
508 case 'GetRecentSets':
509 case 'SearchSetByText':
510 case 'SearchSetByCategory':
511 $components[] = $this->process_sets($result, $short_code);
512 break;
513
514 case 'LoadPhotoSet':
515 case 'LoadPhotoSetPhotos':
516 if (isset($result->ImageCount)) {
517 $pagination->total = $result->ImageCount;
518 }
519 $components = array_merge($components, $this->process_set($result, ['header_display' => $header_display], $short_code, $pagination));
520 break;
521
522 case 'LoadGroupHierarchy':
523 $component = $this->process_group_hierarchy($result, ['header_display' => $header_display], $short_code);
524 if (!is_null($component)) {
525 $components[] = $component;
526 }
527 break;
528
529 case 'LoadGroup':
530 $component = $this->process_group($result, ['header_display' => $header_display], $short_code, 0);
531 if (!is_null($component)) {
532 $components[] = $component;
533 }
534 break;
535 }
536 return $components;
537 }
538 elseif ($response === $this->password_protected) {
539 return [$response];
540 }
541 elseif (!empty($response->error)) {
542 if (!empty($response->error->message)) {
543 return [new Error(esc_html__('Zenfolio returned an error:', 'photonic') . "<br/>\n" . $response->error->message)];
544 }
545 else {
546 return [new Error(esc_html__('Unknown error', 'photonic'))];
547 }
548 }
549 else {
550 return [new Error(esc_html__('Unknown error', 'photonic'))];
551 }
552 }
553
554 /**
555 * Takes an array of photos and displays each as a thumbnail. Each thumbnail, upon clicking launches a lightbox.
556 *
557 * @param $response
558 * @param string $parent
559 * @param array $short_code
560 * @param Pagination|null $pagination
561 * @return Photo_List|Error
562 */
563 private function process_photos($response, $parent, $short_code, Pagination $pagination) {
564 if (!is_array($response)) {
565 if (empty($response->Photos) || !is_array($response->Photos)) {
566 return new Error(esc_html__('Response is not an array', 'photonic'));
567 }
568 $response = $response->Photos;
569 }
570
571 global $photonic_zenfolio_photos_per_row_constraint, $photonic_zenfolio_photo_title_display, $photonic_zenfolio_photos_constrain_by_count;
572 $row_constraints = ['constraint-type' => $photonic_zenfolio_photos_per_row_constraint, 'count' => $photonic_zenfolio_photos_constrain_by_count];
573 $photo_objects = $this->build_level_1_objects($response, $short_code);
574
575 $pagination->end = $pagination->start + count($response);
576
577 $photo_list = new Photo_List($short_code);
578 $photo_list->photos = $photo_objects;
579 $photo_list->title_position = $photonic_zenfolio_photo_title_display;
580 $photo_list->row_constraints = $row_constraints;
581 $photo_list->parent = $parent;
582 $photo_list->pagination = $pagination;
583
584 return $photo_list;
585 }
586
587 public function build_level_1_objects($response, array $short_code = [], $module_parameters = [], $options = []): array {
588 if (!is_array($response)) {
589 if (empty($response->Photos) || !is_array($response->Photos)) {
590 return [];
591 }
592 $response = $response->Photos;
593 }
594
595 $tile_size = (empty($short_code['tile_size']) || 'same' === $short_code['tile_size']) ? $short_code['main_size'] : $short_code['tile_size'];
596
597 $type = '$type';
598 $photo_objects = [];
599
600 $media = explode(',', $short_code['media']);
601 $videos_ok = in_array('videos', $media, true) || in_array('all', $media, true);
602 $photos_ok = in_array('photos', $media, true) || in_array('all', $media, true);
603
604 $sizes = [
605 'thumb_size' => intval($short_code['thumb_size']),
606 'tile_size' => intval($tile_size),
607 'main_size' => intval($short_code['main_size']),
608 ];
609 foreach ($response as $photo) {
610 if (empty($photo->$type) || 'Photo' !== $photo->$type || ($photo->IsVideo && !$videos_ok) || (!$photo->IsVideo && !$photos_ok)) {
611 continue;
612 }
613
614 $appendage = [];
615 if (isset($photo->Sequence)) {
616 $appendage[] = 'sn=' . $photo->Sequence;
617 }
618 if (isset($photo->UrlToken)) {
619 $appendage[] = 'tk=' . $photo->UrlToken;
620 }
621
622 $photonic_photo = new Photo();
623 $photonic_photo->thumbnail = esc_url('https://' . $photo->UrlHost . $photo->UrlCore . '-' . $short_code['thumb_size'] . '.jpg');
624 $photonic_photo->main_image = esc_url('https://' . $photo->UrlHost . $photo->UrlCore . '-' . $short_code['main_size'] . '.jpg');
625 $photonic_photo->tile_image = esc_url('https://' . $photo->UrlHost . $photo->UrlCore . '-' . $tile_size . '.jpg');
626
627 $this->calculate_sizes($photo, $photonic_photo, $sizes);
628
629 if ($photo->IsVideo) {
630 $photonic_photo->video = esc_url(substr($photo->OriginalUrl, 0, strlen($photo->OriginalUrl) - 7) . $short_code['video_size'] . '.mp4');
631 }
632
633 $photonic_photo->download = esc_url($photonic_photo->main_image . '?' . implode('&', $appendage));
634 $photonic_photo->title = wp_kses_post($photo->Title ?? '');
635 $photonic_photo->alt_title = $photonic_photo->title;
636 $photonic_photo->description = wp_kses_post($photo->Caption ?? '');
637 $photonic_photo->main_page = esc_url($photo->PageUrl);
638 $photonic_photo->id = $photo->Id;
639
640 if (!empty($photo->UploadedOn) && !empty($photo->UploadedOn->Value)) {
641 $photonic_photo->uploaded_on = sanitize_text_field($photo->UploadedOn->Value);
642 }
643
644 if (!empty($photo->TakenOn) && !empty($photo->TakenOn->Value)) {
645 $photonic_photo->taken_on = sanitize_text_field($photo->TakenOn->Value);
646 }
647
648 $photo_objects[] = $photonic_photo;
649 }
650
651 return $photo_objects;
652 }
653
654 public function build_level_2_objects($objects_or_response, array $short_code, array $filter_list = [], array &$options = [], ?Pagination &$pagination = null): array {
655 global $photonic_zenfolio_hide_password_protected_thumbnail, $photonic_gallery_template_page;
656 $tile_size = (empty($short_code['tile_size']) || 'same' === $short_code['tile_size']) ? $short_code['main_size'] : $short_code['tile_size'];
657
658 $sizes = [
659 'thumb_size' => intval($short_code['thumb_size']),
660 'tile_size' => intval($tile_size),
661 ];
662
663 $filter_list = [];
664 if (!empty($short_code['filter'])) {
665 $filter_list = explode(',', $short_code['filter']);
666 }
667
668 $objects = [];
669 foreach ($objects_or_response as $photoset) {
670 if (empty($photoset->TitlePhoto)) {
671 continue;
672 }
673 if (!empty($photoset->AccessDescriptor) && !empty($photoset->AccessDescriptor->AccessType) && 'Password' === $photoset->AccessDescriptor->AccessType && !empty($photonic_zenfolio_hide_password_protected_thumbnail)) {
674 continue;
675 }
676
677 $photonic_album = new Album();
678
679 $photo = $photoset->TitlePhoto;
680
681 $photonic_album->id = $photoset->Id;
682 $photonic_album->thumbnail = esc_url('https://' . $photo->UrlHost . $photo->UrlCore . '-' . $short_code['thumb_size'] . '.jpg');
683 $photonic_album->tile_image = esc_url('https://' . $photo->UrlHost . $photo->UrlCore . '-' . $tile_size . '.jpg');
684 $this->calculate_sizes($photo, $photonic_album, $sizes);
685 $photonic_album->main_page = esc_url($photoset->PageUrl);
686 $photonic_album->title = wp_kses_post($photoset->Title);
687 $photonic_album->counter = $photoset->PhotoCount;
688
689 if (!empty($photoset->AccessDescriptor) && !empty($photoset->AccessDescriptor->AccessType) && 'Password' === $photoset->AccessDescriptor->AccessType) {
690 if (!in_array($photoset->AccessDescriptor->Id, $this->unlocked_realms, true)) {
691 $photonic_album->classes = ['photonic-zenfolio-passworded'];
692 $photonic_album->passworded = 1;
693 $photonic_album->realm_id = $photoset->AccessDescriptor->Id;
694 $photonic_album->data_attributes['realm'] = $photoset->AccessDescriptor->Id;
695 }
696 }
697
698 $internal_short_code = $short_code;
699 $internal_short_code['view'] = 'photosets';
700 $internal_short_code['object_id'] = $photoset->Id;
701
702 $internal_short_code['layout'] = empty($short_code['photo_layout']) ? $short_code['layout'] : $short_code['photo_layout'];
703 $internal_short_code['limit'] = empty($short_code['photo_count']) ? $short_code['limit'] : $short_code['photo_count'];
704 $internal_short_code['more'] = empty($short_code['photo_more']) ? $short_code['more'] : $short_code['photo_more'];
705
706 if ('page' === $short_code['popup'] && !empty($photonic_gallery_template_page) && is_string(get_post_status($photonic_gallery_template_page))) {
707 $photonic_album->gallery_url = $this->get_gallery_url(
708 $internal_short_code,
709 [
710 'title' => $photonic_album->title,
711 ]
712 );
713 }
714
715 $page_url = wp_parse_url($photoset->PageUrl);
716 $page_url = $page_url['path'];
717 $page_url = explode('/', $page_url);
718 if (count($page_url) > 1) {
719 $page_url = $page_url[1];
720 }
721
722 if (!is_array($page_url) && (count($filter_list) === 0 || (count($filter_list) > 0 && in_array($page_url, $filter_list, true) && 'exclude' !== strtolower($short_code['filter_type'])) ||
723 (count($filter_list) > 0 && !in_array($page_url, $filter_list, true) && 'exclude' === strtolower($short_code['filter_type'])))) {
724 $objects[] = $photonic_album;
725 }
726 elseif (is_array($page_url)) { // Something went wrong. Let's be safe and add the object.
727 $objects[] = $photonic_album;
728 }
729 }
730 return $objects;
731 }
732
733 private function calculate_sizes($photo, &$object, $sizes) {
734 $width = $photo->Width;
735 $height = $photo->Height;
736 $aspect_ratio = 0;
737 if ($width && $height) {
738 $aspect_ratio = $width / $height;
739 }
740
741 if ($aspect_ratio > 0) {
742 foreach ($sizes as $size => $size_value) {
743 if ($width >= $this->standard_sizes[$size_value]['w'] || $height >= $this->standard_sizes[$size_value]['h']) {
744 $object->{$size} = [
745 'w' => (1 === $size_value) ? 60 : ($aspect_ratio > 1 ? $this->standard_sizes[$size_value]['w'] : ($this->standard_sizes[$size_value]['h'] * $aspect_ratio)),
746 'h' => (1 === $size_value) ? 60 : ($aspect_ratio < 1 ? $this->standard_sizes[$size_value]['h'] : ($this->standard_sizes[$size_value]['w'] / $aspect_ratio)),
747 ];
748 }
749 else {
750 $object->{$size} = [
751 'w' => (1 === $size_value) ? 60 : $width,
752 'h' => (1 === $size_value) ? 60 : $height,
753 ];
754 }
755 }
756 }
757 }
758
759 /**
760 * Prints a single photo with the title as an <h3> and the caption as the image caption.
761 *
762 * @param $photo
763 * @param $short_code
764 * @return Single_Photo
765 */
766 private function process_photo($photo, $short_code) {
767 $type = '$type';
768 if (empty($photo->$type) || 'Photo' !== $photo->$type) {
769 return null;
770 }
771
772 return new Single_Photo(esc_url_raw('https://' . $photo->UrlHost . $photo->UrlCore . '-' . $short_code['main_size'] . '.jpg'), esc_url($photo->PageUrl), wp_kses_post($photo->Title), wp_kses_post($photo->Caption));
773 }
774
775 /**
776 * Takes an array of photosets and displays a thumbnail for each of them. Password-protected thumbnails might be excluded via the options.
777 *
778 * @param $response
779 * @param array $short_code
780 * @return Album_List|Error
781 */
782 private function process_sets($response, $short_code = []) {
783 if (!is_array($response)) {
784 if (empty($response->PhotoSets) || !is_array($response->PhotoSets)) {
785 return new Error(esc_html__('Response is not an array', 'photonic'));
786 }
787 $response = $response->PhotoSets;
788 }
789
790 global $photonic_zenfolio_sets_per_row_constraint, $photonic_zenfolio_sets_constrain_by_count, $photonic_gallery_template_page,
791 $photonic_zenfolio_set_title_display, $photonic_zenfolio_hide_set_photos_count_display;
792 $row_constraints = ['constraint-type' => $photonic_zenfolio_sets_per_row_constraint, 'count' => $photonic_zenfolio_sets_constrain_by_count];
793 $objects = $this->build_level_2_objects($response, $short_code);
794
795 $album_list = new Album_List($short_code);
796 $album_list->albums = $objects;
797 $album_list->row_constraints = $row_constraints;
798 $album_list->type = 'photosets';
799 $album_list->singular_type = 'set';
800 $album_list->title_position = $photonic_zenfolio_set_title_display;
801 $album_list->level_1_count_display = $photonic_zenfolio_hide_set_photos_count_display;
802 $album_list->album_opens_gallery = ('page' === $short_code['popup'] && !empty($photonic_gallery_template_page) && is_string(get_post_status($photonic_gallery_template_page)));
803
804 return $album_list;
805 }
806
807 /**
808 * Displays a header with a basic summary for a photoset, along with thumbnails for all associated photos.
809 *
810 * @param $response
811 * @param array $options
812 * @param array $short_code
813 * @param Pagination $pagination
814 * @return array
815 */
816 private function process_set($response, $options, &$short_code, Pagination &$pagination): array {
817 $components = [];
818
819 $media = explode(',', $short_code['media']);
820 $videos_ok = in_array('videos', $media, true) || in_array('all', $media, true);
821 $photos_ok = in_array('photos', $media, true) || in_array('all', $media, true);
822
823 if (!is_array($response)) {
824 global $photonic_zenfolio_link_set_page, $photonic_zenfolio_hide_set_thumbnail, $photonic_zenfolio_hide_set_title, $photonic_zenfolio_hide_set_photo_count;
825
826 $header = $this->get_header_object($response, $short_code);
827 $hidden = ['thumbnail' => !empty($photonic_zenfolio_hide_set_thumbnail), 'title' => !empty($photonic_zenfolio_hide_set_title), 'counter' => !empty($photonic_zenfolio_hide_set_photo_count)];
828 $counters = [];
829 if ($photos_ok) {
830 $counters['photos'] = $response->ImageCount;
831 }
832 if ($videos_ok) {
833 $counters['videos'] = $response->VideoCount;
834 }
835
836 $pagination->total = ($photos_ok ? $response->ImageCount : 0) + ($videos_ok ? $response->VideoCount : 0);
837
838 $header->hidden_elements = $this->get_hidden_headers($options['header_display'], $hidden);
839 $header->counters = $counters;
840 $header->enable_link = empty($photonic_zenfolio_link_set_page);
841
842 $components[] = $header;
843 }
844 else {
845 $components[] = $this->process_photos($response, 'set', $short_code, $pagination);
846 }
847 return $components;
848 }
849
850 /**
851 * Takes a Zenfolio response object and converts it into an associative array with a title, a thumbnail URL and a link URL.
852 *
853 * @param $object
854 * @param $short_code
855 * @return Header
856 */
857 public function get_header_object($object, $short_code): Header {
858 $thumb_size = $short_code['thumb_size'];
859
860 $header = new Header();
861 $header->title = wp_kses_post($object->Title ?: '');
862 $header->description = wp_kses_post($object->Caption ?: '');
863 if (!empty($object->Title)) {
864 if (!empty($object->TitlePhoto)) {
865 $photo = $object->TitlePhoto;
866 $header->thumb_url = 'https://' . $photo->UrlHost . $photo->UrlCore . '-' . $thumb_size . '.jpg';
867 }
868 $header->page_url = $object->PageUrl;
869 }
870 $header->header_for = 'set';
871 $header->display_location = $short_code['display'];
872
873 return $header;
874 }
875
876 /**
877 * For a given user this prints out the group hierarchy. This starts with the root level and first prints all immediate
878 * children photosets. It then goes into each child group and recursively displays the photosets for each of them in separate sections.
879 *
880 * @param $response
881 * @param array $options
882 * @param array $short_code
883 * @return Collection|Error
884 */
885 private function process_group_hierarchy($response, $options = [], $short_code = []) {
886 if (empty($response->Elements)) {
887 return new Error(esc_html__('No galleries, collections or groups defined for this user', 'photonic'));
888 }
889
890 $filters = [];
891 if (!empty($short_code['kind']) && in_array(strtolower($short_code['kind']), ['popular', 'recent'], true) && !empty($short_code['login_name']) && empty($short_code['filter'])) {
892 $pr_response = $this->make_wp_call('LoadPublicProfile', ['login_name' => $short_code['login_name']]);
893 if (!empty($pr_response->result)) {
894 $pr_response = $pr_response->result;
895 if ('popular' === strtolower($short_code['kind']) && !empty($pr_response->FeaturedPhotoSets)) {
896 foreach ($pr_response->FeaturedPhotoSets as $photoset) {
897 $filters[] = $photoset->Id;
898 }
899 }
900 elseif ('recent' === strtolower($short_code['kind']) && !empty($pr_response->RecentPhotoSets)) {
901 foreach ($pr_response->RecentPhotoSets as $photoset) {
902 $filters[] = $photoset->Id;
903 }
904 }
905 if (empty($filters)) {
906 return null;
907 }
908 }
909 }
910
911 $all_photosets = [];
912 return $this->process_group($response, $options, $short_code, 0, $all_photosets, $filters);
913 }
914
915 /**
916 * For a given group this displays the immediate children photosets and then recursively displays all children groups.
917 *
918 * @param $group
919 * @param array $options
920 * @param array $short_code
921 * @param $level
922 * @param array $all_photosets
923 * @param array $recent_popular
924 * @return Collection|Error
925 */
926 private function process_group($group, $options, $short_code, $level, &$all_photosets = [], $recent_popular = []) {
927 $collection = new Collection();
928
929 $type = '$type';
930 if (!isset($group->Elements)) {
931 $object_id = $group->Id;
932 $method = 'LoadGroup';
933 $f = stripos($object_id, 'f');
934 if (false !== $f) {
935 $object_id = substr($object_id, $f + 1);
936 }
937 $params = [];
938 $params['groupId'] = $object_id;
939 $params['level'] = 'Full';
940 $params['includeChildren'] = true;
941 $response = $this->make_wp_call($method, $params);
942 if (!empty($response->result)) {
943 $group = $response->result;
944 }
945 }
946
947 if (empty($group->Elements)) {
948 return null;
949 }
950
951 $elements = $group->Elements;
952 $photosets = [];
953 $groups = [];
954 global $photonic_zenfolio_hide_password_protected_thumbnail;
955 $image_count = 0;
956 $requests = [];
957
958 foreach ($elements as $element) {
959 if ('PhotoSet' === $element->$type) {
960 if (!empty($short_code['photoset_type']) && in_array(strtolower($short_code['photoset_type']), ['gallery', 'collection'], true) && $element->Type !== $short_code['photoset_type']) {
961 continue;
962 }
963
964 if (!empty($element->AccessDescriptor) && !empty($element->AccessDescriptor->AccessType) && 'Password' === $element->AccessDescriptor->AccessType && !empty($photonic_zenfolio_hide_password_protected_thumbnail)) {
965 continue;
966 }
967
968 if (!empty($short_code['user_specific_category']) || !empty($short_code['user_specific_text'])) {
969 // Need an extra call here, since the LoadGroup doesn't return Level2 attributes for the children
970 // Calls are slow, so we make all of them together at the end of this instead of calling the API individually
971 $params = [];
972 $params['photosetId'] = $element->Id;
973 $params['level'] = 'Level2';
974 $params['includePhotos'] = false;
975 $method = 'LoadPhotoSet';
976
977 $request = $this->prepare_request($method, $params);
978 $requests[] = [
979 'url' => $this->secure_url,
980 'type' => 'POST',
981 'headers' => $request['headers'],
982 'data' => $request['body'],
983 ];
984 }
985 $photosets[$element->Id] = $element;
986 $image_count += $element->ImageCount;
987 }
988 elseif ('Group' === $element->$type) {
989 $groups[] = $element;
990 }
991 }
992
993 if (!empty($requests)) {
994 $responses = Requests::request_multiple($requests);
995
996 foreach ($responses as $ps_response) {
997 if (is_a($ps_response, 'WpOrg\Requests\Response')) {
998 $found = false;
999 $ps_response = json_decode($ps_response->body);
1000 if (!empty($ps_response->result)) {
1001 $ps_response = $ps_response->result;
1002 if (empty($ps_response->Categories) && !empty($short_code['user_specific_category'])) {
1003 $found = false;
1004 }
1005 elseif (!empty($short_code['user_specific_category'])) {
1006 $categories = $ps_response->Categories;
1007 foreach ($categories as $category) {
1008 if ($category === $short_code['category_code']) {
1009 $found = true;
1010 }
1011 }
1012 }
1013
1014 if (!$found) {
1015 if (!empty($short_code['user_specific_text'])) {
1016 // Check Title, Caption, Keywords
1017 $text = $short_code['text'];
1018 $text = explode(',', $text);
1019 $text = array_map('trim', $text);
1020 $to_match = [];
1021 foreach ($text as $item) {
1022 $to_match[] = '\b' . $item . '\b';
1023 }
1024 $to_match = '/(' . implode('|', $to_match) . ')/i';
1025 $keywords = implode(',', $ps_response->Keywords);
1026 $found = preg_match($to_match, $ps_response->Title) || preg_match($to_match, $ps_response->Caption) || preg_match($to_match, $keywords);
1027 }
1028 }
1029
1030 if (!$found) {
1031 unset($photosets[$ps_response->Id]);
1032 }
1033 }
1034 }
1035 }
1036 }
1037
1038 if (!empty($recent_popular)) {
1039 foreach ($photosets as $id => $set) {
1040 if (!in_array($id, $recent_popular, true)) {
1041 unset($photosets[$id]);
1042 }
1043 }
1044 }
1045
1046 $all_photosets = array_merge($all_photosets, array_values($photosets));
1047
1048 global $photonic_zenfolio_hide_empty_groups;
1049 global $photonic_zenfolio_link_group_page, $photonic_zenfolio_hide_group_title, $photonic_zenfolio_hide_group_photo_count, $photonic_zenfolio_hide_group_group_count, $photonic_zenfolio_hide_group_set_count;
1050
1051 $hidden = [
1052 'thumbnail' => true,
1053 'title' => !empty($photonic_zenfolio_hide_group_title),
1054 'counter' => !(empty($photonic_zenfolio_hide_group_photo_count) || empty($photonic_zenfolio_hide_group_group_count) || empty($photonic_zenfolio_hide_group_set_count)),
1055 ];
1056
1057 if (!empty($group->Title) && ($image_count > 0 || empty($photonic_zenfolio_hide_empty_groups))) {
1058 $header = $this->get_header_object($group, $short_code);
1059
1060 $counters = [
1061 'sets' => empty($photonic_zenfolio_hide_group_set_count) ? count($photosets) : 0,
1062 'groups' => empty($photonic_zenfolio_hide_group_group_count) ? count($groups) : 0,
1063 'photos' => empty($photonic_zenfolio_hide_group_photo_count) ? $image_count : 0,
1064 ];
1065
1066 $header->hidden_elements = $this->get_hidden_headers($options['header_display'], $hidden);
1067 if ('flat' !== $short_code['structure']) {
1068 $header->counters = $counters;
1069 $header->enable_link = empty($photonic_zenfolio_link_group_page);
1070
1071 $collection->header = $header;
1072 }
1073 }
1074
1075 if ('flat' !== $short_code['structure']) {
1076 $sets = $this->process_sets($photosets, $short_code);
1077 if (is_a($sets, 'Photonic_Plugin\Components\Album_List')) {
1078 $collection->album_list = $sets;
1079 }
1080 elseif (is_a($sets, 'Photonic_Plugin\Components\Error')) {
1081 return $sets;
1082 }
1083 }
1084
1085 foreach ($groups as $group) {
1086 $out = $this->process_group($group, $options, $short_code, $level + 1, $all_photosets, $recent_popular);
1087 if ('flat' !== $short_code['structure']) {
1088 $collection->collections[] = $out;
1089 }
1090 }
1091
1092 if ('flat' === $short_code['structure'] && 0 === $level) {
1093 if (!empty($header)) {
1094 $counters = [
1095 'sets' => empty($photonic_zenfolio_hide_group_set_count) ? count($all_photosets) : 0,
1096 ];
1097 $header->counters = $counters;
1098
1099 $collection->header = $header;
1100 }
1101
1102 $sets = $this->process_sets($all_photosets, $short_code);
1103 if (is_a($sets, 'Photonic_Plugin\Components\Album_List')) {
1104 $collection->album_list = $sets;
1105 }
1106 elseif (is_a($sets, 'Photonic_Plugin\Components\Error')) {
1107 return $sets;
1108 }
1109 }
1110 return $collection;
1111 }
1112
1113 public function authenticate($password): array {
1114 global $photonic_zenfolio_default_user;
1115 $photonic_authentication = get_option('photonic_authentication');
1116 if (!isset($photonic_authentication['zenfolio'])) {
1117 $photonic_authentication['zenfolio'] = [];
1118 }
1119 $ret = [];
1120 if (!empty($photonic_zenfolio_default_user)) {
1121 $challenge_response = $this->make_wp_call('GetChallenge', ['loginName' => $photonic_zenfolio_default_user]);
1122
1123 if (!empty($challenge_response)) {
1124 $salt = $challenge_response->result->PasswordSalt;
1125 $salt = call_user_func_array('pack', array_merge(['C*'], $salt));
1126 $pass_hash = hash('sha256', $salt . utf8_encode($password), true);
1127
1128 $challenge = $challenge_response->result->Challenge;
1129
1130 $this->perform_back_end_authentication($pass_hash, $challenge);
1131 if (empty($this->token)) {
1132 $ret['error'] = esc_html__('Authentication failed.', 'photonic');
1133 unset($photonic_authentication['zenfolio']['pass_hash']);
1134 }
1135 else {
1136 $photonic_authentication['zenfolio']['pass_hash'] = unpack('C*', $pass_hash);
1137 $ret['success'] = $this->token;
1138 }
1139 }
1140 else {
1141 $ret['error'] = esc_html__('Failed to get challenge.', 'photonic');
1142 }
1143 }
1144 else {
1145 unset($photonic_authentication['zenfolio']['pass_hash']);
1146 $ret['error'] = sprintf(esc_html__('Default user not defined. Please define one under %s', 'photonic'), '<em>Photonic &rarr; Settings &rarr; Zenfolio &rarr; Zenfolio Photo Settings &rarr; Default User</em>');
1147 }
1148 if (current_user_can('edit_theme_options') && check_ajax_referer('zenfolio-save-token')) {
1149 update_option('photonic_authentication', $photonic_authentication);
1150 }
1151 return $ret;
1152 }
1153
1154 private function perform_back_end_authentication($pass_hash = '', $challenge = false) {
1155 global $photonic_zenfolio_default_user;
1156 if (!empty($photonic_zenfolio_default_user)) {
1157 $photonic_authentication = get_option('photonic_authentication');
1158 $auth_done = !empty($photonic_authentication) && !empty($photonic_authentication['zenfolio']) && !empty($photonic_authentication['zenfolio']['pass_hash']);
1159 if ($auth_done || !empty($pass_hash)) {
1160 if (empty($pass_hash)) {
1161 $pass_hash = $photonic_authentication['zenfolio']['pass_hash'];
1162 $pass_hash = call_user_func_array('pack', array_merge(['C*'], $pass_hash));
1163 }
1164
1165 if (empty($challenge)) {
1166 $challenge_response = $this->make_wp_call('GetChallenge', ['loginName' => $photonic_zenfolio_default_user]);
1167 $challenge = $challenge_response->result->Challenge;
1168 }
1169
1170 $challenge_pack = call_user_func_array('pack', array_merge(['C*'], $challenge));
1171 $challenge_hash = hash('sha256', $challenge_pack . $pass_hash, true);
1172 $proof = array_values(unpack('C*', $challenge_hash));
1173
1174 $auth_response = $this->make_wp_call('Authenticate', ['challenge' => $challenge, 'proof' => $proof]);
1175 if (!empty($auth_response->result)) {
1176 $this->token = $auth_response->result;
1177 return;
1178 }
1179 }
1180 }
1181 $this->token = false;
1182 }
1183
1184 /**
1185 * @param string $method
1186 * @param array $params
1187 * @param $keyring
1188 * @return array
1189 */
1190 public function prepare_request($method, $params, $keyring = null): array {
1191 $request = [];
1192 $request['method'] = $method;
1193 $request['params'] = array_values($params);
1194 $request['id'] = 1;
1195 $body_string = wp_json_encode($request);
1196 $body_length = strlen($body_string);
1197
1198 $headers = [];
1199 $headers['Host'] = 'api.zenfolio.com';
1200 $headers['User-Agent'] = $this->user_agent;
1201 if ($this->token && 'GetChallenge' !== $method && 'Authenticate' !== $method) {
1202 $headers['X-Zenfolio-Token'] = $this->token;
1203 }
1204 if (isset($_COOKIE['photonic-zf-keyring'])) {
1205 $headers['X-Zenfolio-Keyring'] = sanitize_text_field(wp_unslash($_COOKIE['photonic-zf-keyring']));
1206 }
1207 elseif (!empty($keyring)) {
1208 $headers['X-Zenfolio-Keyring'] = $keyring;
1209 }
1210 elseif (!empty($this->temp_keyring)) {
1211 $headers['X-Zenfolio-Keyring'] = $this->temp_keyring;
1212 }
1213 $headers['Content-Type'] = 'application/json';
1214 $headers['Content-Length'] = $body_length;
1215 return ['headers' => $headers, 'body' => $body_string];
1216 }
1217
1218 /**
1219 * @param $method
1220 * @param $request
1221 * @return array|mixed|null|object|string|string[]|WP_Error
1222 */
1223 public function make_wp_request($method, $request) {
1224 $args = [
1225 'user-agent' => $this->user_agent,
1226 'sslverify' => PHOTONIC_SSL_VERIFY,
1227 'timeout' => 30,
1228 'headers' => $request['headers'],
1229 'method' => 'POST',
1230 'body' => $request['body'],
1231 ];
1232
1233 $response = wp_remote_request($this->secure_url, $args);
1234 $response = wp_remote_retrieve_body($response);
1235
1236 // PHP's json_decode() function really messes up the "long" ids returned by Zenfolio. The following takes care of this.
1237 // Can't pass the 4th argument as outlined here: https://php.net/manual/en/function.json-decode.php, since it only came into existence in PHP 5.4
1238 $response = preg_replace('/"Id":(\d+)/', '"Id":"$1"', $response);
1239 $response = preg_replace('/"RealmId":(\d+)/', '"Id":"$1"', $response);
1240 if ('KeyringGetUnlockedRealms' === $method) {
1241 $realm_ids = [];
1242 preg_match('/([\[^,\d]+])/', $response, $realm_ids);
1243 if (!empty($realm_ids)) {
1244 $realm_ids = $realm_ids[0];
1245 $replace = $realm_ids;
1246 $replace = str_replace('[', '["', $replace);
1247 $replace = str_replace(']', '"]', $replace);
1248 $replace = str_replace(',', '","', $replace);
1249 $response = str_replace($realm_ids, $replace, $response);
1250 }
1251 }
1252
1253 $response = json_decode($response);
1254 return $response;
1255 }
1256 }
1257