PluginProbe
Photonic Gallery & Lightbox for Flickr, SmugMug & Others / 3.36
Photonic Gallery & Lightbox for Flickr, SmugMug & Others v3.36
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.36, at Platforms/Zenfolio.php

1,261 lines 45.7 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($attr['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 else {
336 $object_id = $attr['object_id'];
337 }
338
339 $chained_methods[] = 'LoadGroup';
340 $f = stripos($object_id, 'f');
341 if (false !== $f) {
342 $object_id = substr($object_id, $f + 1);
343 }
344 $zenfolio_params['groupId'] = $object_id;
345 $zenfolio_params['level'] = 'Full';
346 $zenfolio_params['includeChildren'] = true;
347 break;
348 }
349 }
350
351 if (!empty($attr['panel'])) {
352 $attr['display'] = 'modal';
353 }
354 else {
355 $attr['display'] = 'local';
356 }
357
358 $header_display = $this->get_header_display($attr);
359 $attr['header_display'] = $header_display;
360
361 $call_return = $this->make_chained_calls($chained_methods, $zenfolio_params, $attr);
362 $this->pop_from_stack();
363
364 if (!empty($this->stack_trace[$this->gallery_index])) {
365 $call_return[] = $this->stack_trace[$this->gallery_index];
366 }
367
368 return $call_return;
369 }
370
371 /**
372 * Calls a Zenfolio method with the passed parameters.
373 *
374 * @param $method
375 * @param null $params
376 * @param null $keyring
377 * @return array|mixed|null|object|string|string[]|WP_Error
378 */
379 private function make_wp_call($method, $params = null, $keyring = null) {
380 $request = $this->prepare_request($method, $params, $keyring);
381 $response = $this->make_wp_request($method, $request);
382 return $response;
383 }
384
385 /**
386 * Makes a sequence of calls to different Zenfolio methods. This is particularly useful in case of authenticated calls, where
387 * first the authentication happens, then the content is displayed, all in the same call.
388 *
389 * @param array $methods
390 * @param array $zenfolio_args
391 * @param array $short_code
392 * @return array
393 */
394 private function make_chained_calls($methods, $zenfolio_args, &$short_code = []) {
395 $this->push_to_stack('Make chained calls');
396 $components = [];
397
398 $keyring = null;
399 $original_params = [];
400 foreach ($zenfolio_args as $param => $value) {
401 $original_params[$param] = $value;
402 }
403
404 $pagination = new Pagination();
405 $pagination->start = $short_code['offset'];
406 $pagination->per_page = $short_code['limit'];
407
408 foreach ($methods as $method) {
409 $this->push_to_stack("Making call for $method");
410 $keyring_params = [];
411 if ('KeyringGetUnlockedRealms' === $method) {
412 $keyring_params['keyring'] = $zenfolio_args['keyring'];
413 $response = $this->make_wp_call($method, $keyring_params);
414 if (isset($response->result)) {
415 $this->unlocked_realms = $response->result;
416 }
417 }
418 elseif ('KeyringAddKeyPlain' === $method) {
419 if (in_array($zenfolio_args['realmId'], $this->unlocked_realms, true)) {
420 continue;
421 }
422 $keyring_params['keyring'] = $zenfolio_args['keyring'];
423 $keyring_params['realmId'] = $zenfolio_args['realmId'];
424 $keyring_params['password'] = $zenfolio_args['password'];
425 $response = $this->make_wp_call($method, $keyring_params);
426
427 if (!empty($response->result)) {
428 // Sometimes the cookie isn't set by the setcookie command (happens when the password is passed as a shortcode parameter
429 // instead of the password prompt)
430 $keyring = $response->result;
431 if (!in_array($keyring_params['realmId'], $this->unlocked_realms, true)) {
432 $this->unlocked_realms[] = $keyring_params['realmId'];
433 $this->temp_keyring = $keyring;
434 }
435
436 if (!$short_code['headers_already_called']) {
437 setcookie('photonic-zf-keyring', $keyring, time() + 60 * 60 * 24, COOKIEPATH);
438 }
439 }
440 else {
441 $components[] = $this->password_protected;
442 break;
443 }
444 }
445 else {
446 foreach ($original_params as $param => $value) {
447 $zenfolio_args[$param] = $value;
448 }
449
450 $keyring_fields = ['keyring', 'realmId', 'password'];
451 foreach ($zenfolio_args as $param => $value) {
452 if (in_array($param, $keyring_fields, true)) {
453 unset($zenfolio_args[$param]);
454 }
455 }
456
457 if ('LoadPhotoSetPhotos' === $method) {
458 unset($zenfolio_args['level']);
459 unset($zenfolio_args['includePhotos']);
460 }
461 elseif ('LoadPhotoSet' === $method) {
462 unset($zenfolio_args['startingIndex']);
463 unset($zenfolio_args['numberOfPhotos']);
464 }
465
466 $response = $this->make_wp_call($method, $zenfolio_args, $keyring);
467 $this->pop_from_stack();
468 $this->push_to_stack('Processing response');
469
470 $components = array_merge($components, $this->process_response($method, $response, $short_code, $pagination));
471 $this->pop_from_stack();
472 }
473 }
474
475 $this->pop_from_stack();
476 return $components;
477 }
478
479 /**
480 * Routing function that takes the response and redirects it to the appropriate processing function.
481 *
482 * @param $method
483 * @param $response
484 * @param array $short_code
485 * @param Pagination $pagination
486 * @return string|array
487 */
488 private function process_response($method, $response, &$short_code, Pagination &$pagination) {
489 $header_display = $short_code['header_display'];
490
491 if (!empty($response->result)) {
492 $result = $response->result;
493 $components = [];
494
495 switch ($method) {
496 case 'GetPopularPhotos':
497 case 'GetRecentPhotos':
498 case 'SearchPhotoByText':
499 case 'SearchPhotoByCategory':
500 $pagination->total = $result->TotalCount;
501 $components[] = $this->process_photos($result, 'stream', $short_code, $pagination);
502 break;
503
504 case 'LoadPhoto':
505 $component = $this->process_photo($result, $short_code);
506 if (!is_null($component)) {
507 $components[] = $component;
508 }
509 break;
510
511 case 'GetPopularSets':
512 case 'GetRecentSets':
513 case 'SearchSetByText':
514 case 'SearchSetByCategory':
515 $components[] = $this->process_sets($result, $short_code);
516 break;
517
518 case 'LoadPhotoSet':
519 case 'LoadPhotoSetPhotos':
520 if (isset($result->ImageCount)) {
521 $pagination->total = $result->ImageCount;
522 }
523 $components = array_merge($components, $this->process_set($result, ['header_display' => $header_display], $short_code, $pagination));
524 break;
525
526 case 'LoadGroupHierarchy':
527 $component = $this->process_group_hierarchy($result, ['header_display' => $header_display], $short_code);
528 if (!is_null($component)) {
529 $components[] = $component;
530 }
531 break;
532
533 case 'LoadGroup':
534 $component = $this->process_group($result, ['header_display' => $header_display], $short_code, 0);
535 if (!is_null($component)) {
536 $components[] = $component;
537 }
538 break;
539 }
540 return $components;
541 }
542 elseif ($response === $this->password_protected) {
543 return [$response];
544 }
545 elseif (!empty($response->error)) {
546 if (!empty($response->error->message)) {
547 return [new Error(esc_html__('Zenfolio returned an error:', 'photonic') . "<br/>\n" . $response->error->message)];
548 }
549 else {
550 return [new Error(esc_html__('Unknown error', 'photonic'))];
551 }
552 }
553 else {
554 return [new Error(esc_html__('Unknown error', 'photonic'))];
555 }
556 }
557
558 /**
559 * Takes an array of photos and displays each as a thumbnail. Each thumbnail, upon clicking launches a lightbox.
560 *
561 * @param $response
562 * @param string $parent
563 * @param array $short_code
564 * @param Pagination|null $pagination
565 * @return Photo_List|Error
566 */
567 private function process_photos($response, string $parent, array $short_code, Pagination $pagination) {
568 if (!is_array($response)) {
569 if (empty($response->Photos) || !is_array($response->Photos)) {
570 return new Error(esc_html__('Response is not an array', 'photonic'));
571 }
572 $response = $response->Photos;
573 }
574
575 global $photonic_zenfolio_photos_per_row_constraint, $photonic_zenfolio_photo_title_display, $photonic_zenfolio_photos_constrain_by_count;
576 $row_constraints = ['constraint-type' => $photonic_zenfolio_photos_per_row_constraint, 'count' => $photonic_zenfolio_photos_constrain_by_count];
577 $photo_objects = $this->build_level_1_objects($response, $short_code);
578
579 $pagination->end = $pagination->start + count($response);
580
581 $photo_list = new Photo_List($short_code);
582 $photo_list->photos = $photo_objects;
583 $photo_list->title_position = $photonic_zenfolio_photo_title_display;
584 $photo_list->row_constraints = $row_constraints;
585 $photo_list->parent = $parent;
586 $photo_list->pagination = $pagination;
587
588 return $photo_list;
589 }
590
591 public function build_level_1_objects($response, array $short_code = [], $module_parameters = [], $options = []): array {
592 if (!is_array($response)) {
593 if (empty($response->Photos) || !is_array($response->Photos)) {
594 return [];
595 }
596 $response = $response->Photos;
597 }
598
599 $tile_size = (empty($short_code['tile_size']) || 'same' === $short_code['tile_size']) ? $short_code['main_size'] : $short_code['tile_size'];
600
601 $type = '$type';
602 $photo_objects = [];
603
604 $media = explode(',', $short_code['media']);
605 $videos_ok = in_array('videos', $media, true) || in_array('all', $media, true);
606 $photos_ok = in_array('photos', $media, true) || in_array('all', $media, true);
607
608 $sizes = [
609 'thumb_size' => intval($short_code['thumb_size']),
610 'tile_size' => intval($tile_size),
611 'main_size' => intval($short_code['main_size']),
612 ];
613 foreach ($response as $photo) {
614 if (empty($photo->$type) || 'Photo' !== $photo->$type || ($photo->IsVideo && !$videos_ok) || (!$photo->IsVideo && !$photos_ok)) {
615 continue;
616 }
617
618 $appendage = [];
619 if (isset($photo->Sequence)) {
620 $appendage[] = 'sn=' . $photo->Sequence;
621 }
622 if (isset($photo->UrlToken)) {
623 $appendage[] = 'tk=' . $photo->UrlToken;
624 }
625
626 $photonic_photo = new Photo();
627 $photonic_photo->thumbnail = esc_url('https://' . $photo->UrlHost . $photo->UrlCore . '-' . $short_code['thumb_size'] . '.jpg');
628 $photonic_photo->main_image = esc_url('https://' . $photo->UrlHost . $photo->UrlCore . '-' . $short_code['main_size'] . '.jpg');
629 $photonic_photo->tile_image = esc_url('https://' . $photo->UrlHost . $photo->UrlCore . '-' . $tile_size . '.jpg');
630
631 $this->calculate_sizes($photo, $photonic_photo, $sizes);
632
633 if ($photo->IsVideo) {
634 $photonic_photo->video = esc_url(substr($photo->OriginalUrl, 0, strlen($photo->OriginalUrl) - 7) . $short_code['video_size'] . '.mp4');
635 }
636
637 $photonic_photo->download = esc_url($photonic_photo->main_image . '?' . implode('&', $appendage));
638 $photonic_photo->title = wp_kses_post($photo->Title ?? '');
639 $photonic_photo->alt_title = $photonic_photo->title;
640 $photonic_photo->description = wp_kses_post($photo->Caption ?? '');
641 $photonic_photo->main_page = esc_url($photo->PageUrl);
642 $photonic_photo->id = $photo->Id;
643
644 if (!empty($photo->UploadedOn) && !empty($photo->UploadedOn->Value)) {
645 $photonic_photo->uploaded_on = sanitize_text_field($photo->UploadedOn->Value);
646 }
647
648 if (!empty($photo->TakenOn) && !empty($photo->TakenOn->Value)) {
649 $photonic_photo->taken_on = sanitize_text_field($photo->TakenOn->Value);
650 }
651
652 $photo_objects[] = $photonic_photo;
653 }
654
655 return $photo_objects;
656 }
657
658 public function build_level_2_objects($objects_or_response, array $short_code, array $filter_list = [], array $options = [], ?Pagination &$pagination = null): array {
659 global $photonic_zenfolio_hide_password_protected_thumbnail, $photonic_gallery_template_page;
660 $tile_size = (empty($short_code['tile_size']) || 'same' === $short_code['tile_size']) ? $short_code['main_size'] : $short_code['tile_size'];
661
662 $sizes = [
663 'thumb_size' => intval($short_code['thumb_size']),
664 'tile_size' => intval($tile_size),
665 ];
666
667 $filter_list = [];
668 if (!empty($short_code['filter'])) {
669 $filter_list = explode(',', $short_code['filter']);
670 }
671
672 $objects = [];
673 foreach ($objects_or_response as $photoset) {
674 if (empty($photoset->TitlePhoto)) {
675 continue;
676 }
677 if (!empty($photoset->AccessDescriptor) && !empty($photoset->AccessDescriptor->AccessType) && 'Password' === $photoset->AccessDescriptor->AccessType && !empty($photonic_zenfolio_hide_password_protected_thumbnail)) {
678 continue;
679 }
680
681 $photonic_album = new Album();
682
683 $photo = $photoset->TitlePhoto;
684
685 $photonic_album->id = $photoset->Id;
686 $photonic_album->thumbnail = esc_url('https://' . $photo->UrlHost . $photo->UrlCore . '-' . $short_code['thumb_size'] . '.jpg');
687 $photonic_album->tile_image = esc_url('https://' . $photo->UrlHost . $photo->UrlCore . '-' . $tile_size . '.jpg');
688 $this->calculate_sizes($photo, $photonic_album, $sizes);
689 $photonic_album->main_page = esc_url($photoset->PageUrl);
690 $photonic_album->title = wp_kses_post($photoset->Title);
691 $photonic_album->counter = $photoset->PhotoCount;
692
693 if (!empty($photoset->AccessDescriptor) && !empty($photoset->AccessDescriptor->AccessType) && 'Password' === $photoset->AccessDescriptor->AccessType) {
694 if (!in_array($photoset->AccessDescriptor->Id, $this->unlocked_realms, true)) {
695 $photonic_album->classes = ['photonic-zenfolio-passworded'];
696 $photonic_album->passworded = 1;
697 $photonic_album->realm_id = $photoset->AccessDescriptor->Id;
698 $photonic_album->data_attributes['realm'] = $photoset->AccessDescriptor->Id;
699 }
700 }
701
702 $internal_short_code = $short_code;
703 $internal_short_code['view'] = 'photosets';
704 $internal_short_code['object_id'] = $photoset->Id;
705
706 $internal_short_code['layout'] = empty($short_code['photo_layout']) ? $short_code['layout'] : $short_code['photo_layout'];
707 $internal_short_code['limit'] = empty($short_code['photo_count']) ? $short_code['limit'] : $short_code['photo_count'];
708 $internal_short_code['more'] = empty($short_code['photo_more']) ? $short_code['more'] : $short_code['photo_more'];
709
710 if ('page' === $short_code['popup'] && !empty($photonic_gallery_template_page) && is_string(get_post_status($photonic_gallery_template_page))) {
711 $photonic_album->gallery_url = $this->get_gallery_url(
712 $internal_short_code,
713 [
714 'title' => $photonic_album->title,
715 ]
716 );
717 }
718
719 $page_url = wp_parse_url($photoset->PageUrl);
720 $page_url = $page_url['path'];
721 $page_url = explode('/', $page_url);
722 if (count($page_url) > 1) {
723 $page_url = $page_url[1];
724 }
725
726 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'])) ||
727 (count($filter_list) > 0 && !in_array($page_url, $filter_list, true) && 'exclude' === strtolower($short_code['filter_type'])))) {
728 $objects[] = $photonic_album;
729 }
730 elseif (is_array($page_url)) { // Something went wrong. Let's be safe and add the object.
731 $objects[] = $photonic_album;
732 }
733 }
734 return $objects;
735 }
736
737 private function calculate_sizes($photo, &$object, $sizes) {
738 $width = $photo->Width;
739 $height = $photo->Height;
740 $aspect_ratio = 0;
741 if ($width && $height) {
742 $aspect_ratio = $width / $height;
743 }
744
745 if ($aspect_ratio > 0) {
746 foreach ($sizes as $size => $size_value) {
747 if ($width >= $this->standard_sizes[$size_value]['w'] || $height >= $this->standard_sizes[$size_value]['h']) {
748 $object->{$size} = [
749 'w' => (1 === $size_value) ? 60 : ($aspect_ratio > 1 ? $this->standard_sizes[$size_value]['w'] : ($this->standard_sizes[$size_value]['h'] * $aspect_ratio)),
750 'h' => (1 === $size_value) ? 60 : ($aspect_ratio < 1 ? $this->standard_sizes[$size_value]['h'] : ($this->standard_sizes[$size_value]['w'] / $aspect_ratio)),
751 ];
752 }
753 else {
754 $object->{$size} = [
755 'w' => (1 === $size_value) ? 60 : $width,
756 'h' => (1 === $size_value) ? 60 : $height,
757 ];
758 }
759 }
760 }
761 }
762
763 /**
764 * Prints a single photo with the title as an <h3> and the caption as the image caption.
765 *
766 * @param $photo
767 * @param $short_code
768 * @return Single_Photo
769 */
770 private function process_photo($photo, $short_code) {
771 $type = '$type';
772 if (empty($photo->$type) || 'Photo' !== $photo->$type) {
773 return null;
774 }
775
776 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));
777 }
778
779 /**
780 * Takes an array of photosets and displays a thumbnail for each of them. Password-protected thumbnails might be excluded via the options.
781 *
782 * @param $response
783 * @param array $short_code
784 * @return Album_List|Error
785 */
786 private function process_sets($response, $short_code = []) {
787 if (!is_array($response)) {
788 if (empty($response->PhotoSets) || !is_array($response->PhotoSets)) {
789 return new Error(esc_html__('Response is not an array', 'photonic'));
790 }
791 $response = $response->PhotoSets;
792 }
793
794 global $photonic_zenfolio_sets_per_row_constraint, $photonic_zenfolio_sets_constrain_by_count, $photonic_gallery_template_page,
795 $photonic_zenfolio_set_title_display, $photonic_zenfolio_hide_set_photos_count_display;
796 $row_constraints = ['constraint-type' => $photonic_zenfolio_sets_per_row_constraint, 'count' => $photonic_zenfolio_sets_constrain_by_count];
797 $objects = $this->build_level_2_objects($response, $short_code);
798
799 $album_list = new Album_List($short_code);
800 $album_list->albums = $objects;
801 $album_list->row_constraints = $row_constraints;
802 $album_list->type = 'photosets';
803 $album_list->singular_type = 'set';
804 $album_list->title_position = $photonic_zenfolio_set_title_display;
805 $album_list->level_1_count_display = $photonic_zenfolio_hide_set_photos_count_display;
806 $album_list->album_opens_gallery = ('page' === $short_code['popup'] && !empty($photonic_gallery_template_page) && is_string(get_post_status($photonic_gallery_template_page)));
807
808 return $album_list;
809 }
810
811 /**
812 * Displays a header with a basic summary for a photoset, along with thumbnails for all associated photos.
813 *
814 * @param $response
815 * @param array $options
816 * @param array $short_code
817 * @param Pagination $pagination
818 * @return array
819 */
820 private function process_set($response, $options, &$short_code, Pagination &$pagination): array {
821 $components = [];
822
823 $media = explode(',', $short_code['media']);
824 $videos_ok = in_array('videos', $media, true) || in_array('all', $media, true);
825 $photos_ok = in_array('photos', $media, true) || in_array('all', $media, true);
826
827 if (!is_array($response)) {
828 global $photonic_zenfolio_link_set_page, $photonic_zenfolio_hide_set_thumbnail, $photonic_zenfolio_hide_set_title, $photonic_zenfolio_hide_set_photo_count;
829
830 $header = $this->get_header_object($response, $short_code);
831 $hidden = ['thumbnail' => !empty($photonic_zenfolio_hide_set_thumbnail), 'title' => !empty($photonic_zenfolio_hide_set_title), 'counter' => !empty($photonic_zenfolio_hide_set_photo_count)];
832 $counters = [];
833 if ($photos_ok) {
834 $counters['photos'] = $response->ImageCount;
835 }
836 if ($videos_ok) {
837 $counters['videos'] = $response->VideoCount;
838 }
839
840 $pagination->total = ($photos_ok ? $response->ImageCount : 0) + ($videos_ok ? $response->VideoCount : 0);
841
842 $header->hidden_elements = $this->get_hidden_headers($options['header_display'], $hidden);
843 $header->counters = $counters;
844 $header->enable_link = empty($photonic_zenfolio_link_set_page);
845
846 $components[] = $header;
847 }
848 else {
849 $components[] = $this->process_photos($response, 'set', $short_code, $pagination);
850 }
851 return $components;
852 }
853
854 /**
855 * Takes a Zenfolio response object and converts it into an associative array with a title, a thumbnail URL and a link URL.
856 *
857 * @param $object
858 * @param $short_code
859 * @return Header
860 */
861 public function get_header_object($object, $short_code): Header {
862 $thumb_size = $short_code['thumb_size'];
863
864 $header = new Header();
865 $header->title = wp_kses_post($object->Title ?: '');
866 $header->description = wp_kses_post($object->Caption ?: '');
867 if (!empty($object->Title)) {
868 if (!empty($object->TitlePhoto)) {
869 $photo = $object->TitlePhoto;
870 $header->thumb_url = 'https://' . $photo->UrlHost . $photo->UrlCore . '-' . $thumb_size . '.jpg';
871 }
872 $header->page_url = $object->PageUrl;
873 }
874 $header->header_for = 'set';
875 $header->display_location = $short_code['display'];
876
877 return $header;
878 }
879
880 /**
881 * For a given user this prints out the group hierarchy. This starts with the root level and first prints all immediate
882 * children photosets. It then goes into each child group and recursively displays the photosets for each of them in separate sections.
883 *
884 * @param $response
885 * @param array $options
886 * @param array $short_code
887 * @return Collection|Error
888 */
889 private function process_group_hierarchy($response, $options = [], $short_code = []) {
890 if (empty($response->Elements)) {
891 return new Error(esc_html__('No galleries, collections or groups defined for this user', 'photonic'));
892 }
893
894 $filters = [];
895 if (!empty($short_code['kind']) && in_array(strtolower($short_code['kind']), ['popular', 'recent'], true) && !empty($short_code['login_name']) && empty($short_code['filter'])) {
896 $pr_response = $this->make_wp_call('LoadPublicProfile', ['login_name' => $short_code['login_name']]);
897 if (!empty($pr_response->result)) {
898 $pr_response = $pr_response->result;
899 if ('popular' === strtolower($short_code['kind']) && !empty($pr_response->FeaturedPhotoSets)) {
900 foreach ($pr_response->FeaturedPhotoSets as $photoset) {
901 $filters[] = $photoset->Id;
902 }
903 }
904 elseif ('recent' === strtolower($short_code['kind']) && !empty($pr_response->RecentPhotoSets)) {
905 foreach ($pr_response->RecentPhotoSets as $photoset) {
906 $filters[] = $photoset->Id;
907 }
908 }
909 if (empty($filters)) {
910 return null;
911 }
912 }
913 }
914
915 $all_photosets = [];
916 return $this->process_group($response, $options, $short_code, 0, $all_photosets, $filters);
917 }
918
919 /**
920 * For a given group this displays the immediate children photosets and then recursively displays all children groups.
921 *
922 * @param $group
923 * @param array $options
924 * @param array $short_code
925 * @param $level
926 * @param array $all_photosets
927 * @param array $recent_popular
928 * @return Collection|Error
929 */
930 private function process_group($group, $options, $short_code, $level, &$all_photosets = [], $recent_popular = []) {
931 $collection = new Collection();
932
933 $type = '$type';
934 if (!isset($group->Elements)) {
935 $object_id = $group->Id;
936 $method = 'LoadGroup';
937 $f = stripos($object_id, 'f');
938 if (false !== $f) {
939 $object_id = substr($object_id, $f + 1);
940 }
941 $params = [];
942 $params['groupId'] = $object_id;
943 $params['level'] = 'Full';
944 $params['includeChildren'] = true;
945 $response = $this->make_wp_call($method, $params);
946 if (!empty($response->result)) {
947 $group = $response->result;
948 }
949 }
950
951 if (empty($group->Elements)) {
952 return null;
953 }
954
955 $elements = $group->Elements;
956 $photosets = [];
957 $groups = [];
958 global $photonic_zenfolio_hide_password_protected_thumbnail;
959 $image_count = 0;
960 $requests = [];
961
962 foreach ($elements as $element) {
963 if ('PhotoSet' === $element->$type) {
964 if (!empty($short_code['photoset_type']) && in_array(strtolower($short_code['photoset_type']), ['gallery', 'collection'], true) && $element->Type !== $short_code['photoset_type']) {
965 continue;
966 }
967
968 if (!empty($element->AccessDescriptor) && !empty($element->AccessDescriptor->AccessType) && 'Password' === $element->AccessDescriptor->AccessType && !empty($photonic_zenfolio_hide_password_protected_thumbnail)) {
969 continue;
970 }
971
972 if (!empty($short_code['user_specific_category']) || !empty($short_code['user_specific_text'])) {
973 // Need an extra call here, since the LoadGroup doesn't return Level2 attributes for the children
974 // Calls are slow, so we make all of them together at the end of this instead of calling the API individually
975 $params = [];
976 $params['photosetId'] = $element->Id;
977 $params['level'] = 'Level2';
978 $params['includePhotos'] = false;
979 $method = 'LoadPhotoSet';
980
981 $request = $this->prepare_request($method, $params);
982 $requests[] = [
983 'url' => $this->secure_url,
984 'type' => 'POST',
985 'headers' => $request['headers'],
986 'data' => $request['body'],
987 ];
988 }
989 $photosets[$element->Id] = $element;
990 $image_count += $element->ImageCount;
991 }
992 elseif ('Group' === $element->$type) {
993 $groups[] = $element;
994 }
995 }
996
997 if (!empty($requests)) {
998 $responses = Requests::request_multiple($requests);
999
1000 foreach ($responses as $ps_response) {
1001 if (is_a($ps_response, 'WpOrg\Requests\Response')) {
1002 $found = false;
1003 $ps_response = json_decode($ps_response->body);
1004 if (!empty($ps_response->result)) {
1005 $ps_response = $ps_response->result;
1006 if (empty($ps_response->Categories) && !empty($short_code['user_specific_category'])) {
1007 $found = false;
1008 }
1009 elseif (!empty($short_code['user_specific_category'])) {
1010 $categories = $ps_response->Categories;
1011 foreach ($categories as $category) {
1012 if ($category === $short_code['category_code']) {
1013 $found = true;
1014 }
1015 }
1016 }
1017
1018 if (!$found) {
1019 if (!empty($short_code['user_specific_text'])) {
1020 // Check Title, Caption, Keywords
1021 $text = $short_code['text'];
1022 $text = explode(',', $text);
1023 $text = array_map('trim', $text);
1024 $to_match = [];
1025 foreach ($text as $item) {
1026 $to_match[] = '\b' . $item . '\b';
1027 }
1028 $to_match = '/(' . implode('|', $to_match) . ')/i';
1029 $keywords = implode(',', $ps_response->Keywords);
1030 $found = preg_match($to_match, $ps_response->Title) || preg_match($to_match, $ps_response->Caption) || preg_match($to_match, $keywords);
1031 }
1032 }
1033
1034 if (!$found) {
1035 unset($photosets[$ps_response->Id]);
1036 }
1037 }
1038 }
1039 }
1040 }
1041
1042 if (!empty($recent_popular)) {
1043 foreach ($photosets as $id => $set) {
1044 if (!in_array($id, $recent_popular, true)) {
1045 unset($photosets[$id]);
1046 }
1047 }
1048 }
1049
1050 $all_photosets = array_merge($all_photosets, array_values($photosets));
1051
1052 global $photonic_zenfolio_hide_empty_groups;
1053 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;
1054
1055 $hidden = [
1056 'thumbnail' => true,
1057 'title' => !empty($photonic_zenfolio_hide_group_title),
1058 'counter' => !(empty($photonic_zenfolio_hide_group_photo_count) || empty($photonic_zenfolio_hide_group_group_count) || empty($photonic_zenfolio_hide_group_set_count)),
1059 ];
1060
1061 if (!empty($group->Title) && ($image_count > 0 || empty($photonic_zenfolio_hide_empty_groups))) {
1062 $header = $this->get_header_object($group, $short_code);
1063
1064 $counters = [
1065 'sets' => empty($photonic_zenfolio_hide_group_set_count) ? count($photosets) : 0,
1066 'groups' => empty($photonic_zenfolio_hide_group_group_count) ? count($groups) : 0,
1067 'photos' => empty($photonic_zenfolio_hide_group_photo_count) ? $image_count : 0,
1068 ];
1069
1070 $header->hidden_elements = $this->get_hidden_headers($options['header_display'], $hidden);
1071 if ('flat' !== $short_code['structure']) {
1072 $header->counters = $counters;
1073 $header->enable_link = empty($photonic_zenfolio_link_group_page);
1074
1075 $collection->header = $header;
1076 }
1077 }
1078
1079 if ('flat' !== $short_code['structure']) {
1080 $sets = $this->process_sets($photosets, $short_code);
1081 if (is_a($sets, 'Photonic_Plugin\Components\Album_List')) {
1082 $collection->album_list = $sets;
1083 }
1084 elseif (is_a($sets, 'Photonic_Plugin\Components\Error')) {
1085 return $sets;
1086 }
1087 }
1088
1089 foreach ($groups as $group) {
1090 $out = $this->process_group($group, $options, $short_code, $level + 1, $all_photosets, $recent_popular);
1091 if ('flat' !== $short_code['structure']) {
1092 $collection->collections[] = $out;
1093 }
1094 }
1095
1096 if ('flat' === $short_code['structure'] && 0 === $level) {
1097 if (!empty($header)) {
1098 $counters = [
1099 'sets' => empty($photonic_zenfolio_hide_group_set_count) ? count($all_photosets) : 0,
1100 ];
1101 $header->counters = $counters;
1102
1103 $collection->header = $header;
1104 }
1105
1106 $sets = $this->process_sets($all_photosets, $short_code);
1107 if (is_a($sets, 'Photonic_Plugin\Components\Album_List')) {
1108 $collection->album_list = $sets;
1109 }
1110 elseif (is_a($sets, 'Photonic_Plugin\Components\Error')) {
1111 return $sets;
1112 }
1113 }
1114 return $collection;
1115 }
1116
1117 public function authenticate($password): array {
1118 global $photonic_zenfolio_default_user;
1119 $photonic_authentication = get_option('photonic_authentication');
1120 if (!isset($photonic_authentication['zenfolio'])) {
1121 $photonic_authentication['zenfolio'] = [];
1122 }
1123 $ret = [];
1124 if (!empty($photonic_zenfolio_default_user)) {
1125 $challenge_response = $this->make_wp_call('GetChallenge', ['loginName' => $photonic_zenfolio_default_user]);
1126
1127 if (!empty($challenge_response)) {
1128 $salt = $challenge_response->result->PasswordSalt;
1129 $salt = call_user_func_array('pack', array_merge(['C*'], $salt));
1130 $pass_hash = hash('sha256', $salt . utf8_encode($password), true);
1131
1132 $challenge = $challenge_response->result->Challenge;
1133
1134 $this->perform_back_end_authentication($pass_hash, $challenge);
1135 if (empty($this->token)) {
1136 $ret['error'] = esc_html__('Authentication failed.', 'photonic');
1137 unset($photonic_authentication['zenfolio']['pass_hash']);
1138 }
1139 else {
1140 $photonic_authentication['zenfolio']['pass_hash'] = unpack('C*', $pass_hash);
1141 $ret['success'] = $this->token;
1142 }
1143 }
1144 else {
1145 $ret['error'] = esc_html__('Failed to get challenge.', 'photonic');
1146 }
1147 }
1148 else {
1149 unset($photonic_authentication['zenfolio']['pass_hash']);
1150 $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>');
1151 }
1152 if (current_user_can('edit_theme_options') && check_ajax_referer('zenfolio-save-token')) {
1153 update_option('photonic_authentication', $photonic_authentication);
1154 }
1155 return $ret;
1156 }
1157
1158 private function perform_back_end_authentication($pass_hash = '', $challenge = false) {
1159 global $photonic_zenfolio_default_user;
1160 if (!empty($photonic_zenfolio_default_user)) {
1161 $photonic_authentication = get_option('photonic_authentication');
1162 $auth_done = !empty($photonic_authentication) && !empty($photonic_authentication['zenfolio']) && !empty($photonic_authentication['zenfolio']['pass_hash']);
1163 if ($auth_done || !empty($pass_hash)) {
1164 if (empty($pass_hash)) {
1165 $pass_hash = $photonic_authentication['zenfolio']['pass_hash'];
1166 $pass_hash = call_user_func_array('pack', array_merge(['C*'], $pass_hash));
1167 }
1168
1169 if (empty($challenge)) {
1170 $challenge_response = $this->make_wp_call('GetChallenge', ['loginName' => $photonic_zenfolio_default_user]);
1171 $challenge = $challenge_response->result->Challenge;
1172 }
1173
1174 $challenge_pack = call_user_func_array('pack', array_merge(['C*'], $challenge));
1175 $challenge_hash = hash('sha256', $challenge_pack . $pass_hash, true);
1176 $proof = array_values(unpack('C*', $challenge_hash));
1177
1178 $auth_response = $this->make_wp_call('Authenticate', ['challenge' => $challenge, 'proof' => $proof]);
1179 if (!empty($auth_response->result)) {
1180 $this->token = $auth_response->result;
1181 return;
1182 }
1183 }
1184 }
1185 $this->token = false;
1186 }
1187
1188 /**
1189 * @param string $method
1190 * @param array $params
1191 * @param $keyring
1192 * @return array
1193 */
1194 public function prepare_request($method, $params, $keyring = null): array {
1195 $request = [];
1196 $request['method'] = $method;
1197 $request['params'] = array_values($params);
1198 $request['id'] = 1;
1199 $body_string = wp_json_encode($request);
1200 $body_length = strlen($body_string);
1201
1202 $headers = [];
1203 $headers['Host'] = 'api.zenfolio.com';
1204 $headers['User-Agent'] = $this->user_agent;
1205 if ($this->token && 'GetChallenge' !== $method && 'Authenticate' !== $method) {
1206 $headers['X-Zenfolio-Token'] = $this->token;
1207 }
1208 if (isset($_COOKIE['photonic-zf-keyring'])) {
1209 $headers['X-Zenfolio-Keyring'] = sanitize_text_field(wp_unslash($_COOKIE['photonic-zf-keyring']));
1210 }
1211 elseif (!empty($keyring)) {
1212 $headers['X-Zenfolio-Keyring'] = $keyring;
1213 }
1214 elseif (!empty($this->temp_keyring)) {
1215 $headers['X-Zenfolio-Keyring'] = $this->temp_keyring;
1216 }
1217 $headers['Content-Type'] = 'application/json';
1218 $headers['Content-Length'] = $body_length;
1219 return ['headers' => $headers, 'body' => $body_string];
1220 }
1221
1222 /**
1223 * @param $method
1224 * @param $request
1225 * @return array|mixed|null|object|string|string[]|WP_Error
1226 */
1227 public function make_wp_request($method, $request) {
1228 $args = [
1229 'user-agent' => $this->user_agent,
1230 'sslverify' => PHOTONIC_SSL_VERIFY,
1231 'timeout' => 30,
1232 'headers' => $request['headers'],
1233 'method' => 'POST',
1234 'body' => $request['body'],
1235 ];
1236
1237 $response = wp_remote_request($this->secure_url, $args);
1238 $response = wp_remote_retrieve_body($response);
1239
1240 // PHP's json_decode() function really messes up the "long" ids returned by Zenfolio. The following takes care of this.
1241 // 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
1242 $response = preg_replace('/"Id":(\d+)/', '"Id":"$1"', $response);
1243 $response = preg_replace('/"RealmId":(\d+)/', '"Id":"$1"', $response);
1244 if ('KeyringGetUnlockedRealms' === $method) {
1245 $realm_ids = [];
1246 preg_match('/([\[^,\d]+])/', $response, $realm_ids);
1247 if (!empty($realm_ids)) {
1248 $realm_ids = $realm_ids[0];
1249 $replace = $realm_ids;
1250 $replace = str_replace('[', '["', $replace);
1251 $replace = str_replace(']', '"]', $replace);
1252 $replace = str_replace(',', '","', $replace);
1253 $response = str_replace($realm_ids, $replace, $response);
1254 }
1255 }
1256
1257 $response = json_decode($response);
1258 return $response;
1259 }
1260 }
1261