PluginProbe
Photonic Gallery & Lightbox for Flickr, SmugMug & Others / 2.19
Photonic Gallery & Lightbox for Flickr, SmugMug & Others v2.19
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 / admin / flow / Photonic_Flow_Manager.php

Photonic_Flow_Manager.php in Photonic Gallery & Lightbox for Flickr, SmugMug & Others 2.19, at admin/flow/Photonic_Flow_Manager.php

2,050 lines 84.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class Photonic_Flow_Manager
4 * This is the core module for displaying the gallery builder. This is used with two other files:
5 * - Flow.php: contains the top-level markup for the gallery builder
6 * - Photonic_Flow_Fields.php: contains all the fields by screen
7 *
8 * @package Photonic
9 * @subpackage Flow
10 * @since 2.00
11 */
12 class Photonic_Flow_Manager {
13 var $flow_fields, $field_list, $display_types, $shortcode_attributes, $aka_attributes, $date_parts, $date_part_hints,
14 $error_mandatory, $error_no_response, $error_unknown, $error_not_found, $error_no_data_returned, $error_not_permitted, $error_authentication, $error_missing_api,
15 $force_next_screen, $force_previous_screen, $is_gutenberg;
16
17 function __construct() {
18 require_once("Photonic_Flow_Fields.php");
19 $this->flow_fields = new Photonic_Flow_Fields();
20
21 $this->field_list = $this->flow_fields->get_flow_options();
22 $this->force_next_screen = -1;
23 $this->force_previous_screen = -1;
24
25 $this->error_mandatory = esc_html__('Please fill the mandatory fields. Mandatory fields are marked with a red "*".', 'photonic');
26 $this->error_no_response = esc_html__('No response from server.', 'photonic');
27 $this->error_not_found = esc_html__('Not found.', 'photonic');
28 $this->error_no_data_returned = esc_html__('No data was returned for the user you provided. Please verify that the user has the content you are looking for.', 'photonic');
29 $this->error_not_permitted = esc_html__('Incorrect value passed for "%1$s": %2$s', 'photonic');
30 $this->error_missing_api = esc_html__('Please set up your %1$s and secret under %2$s', 'photonic');
31 $this->error_authentication = esc_html__('Please set up your %1$s Authentication from %2$s', 'photonic');
32
33 $this->date_parts = array(0 => esc_html__('Year', 'photonic'), 1 => esc_html__('Month', 'photonic'), 2 => esc_html__('Date', 'photonic'));
34 $this->date_part_hints = array(0 => esc_html__('Year (0 - 9999)', 'photonic'), 1 => esc_html__('Month (0 - 12)', 'photonic'), 2 => esc_html__('Date (0 - 31)', 'photonic'));
35
36 $this->display_types = array(
37 'single-photo' => 0,
38 'multi-photo' => 1,
39 'album-photo' => 1,
40 'folder-photo' => 1,
41 'user-photo' => 1,
42 'gallery-photo' => 1,
43 'collection-photo' => 1,
44 'current-post' => 1,
45 'another-post' => 1,
46 'multi-album' => 2,
47 'multi-gallery' => 2,
48 'multi-collection' => 2,
49 'multi-gallery-collection' => 2,
50 'collection' => 3,
51 'collections' => 3,
52 'folder' => 3,
53 'tree' => 3,
54 'group' => 3,
55 'group-hierarchy' => 3,
56 );
57
58 $this->shortcode_attributes = array(
59 'common' => array(
60 'columns', 'count', 'more', 'photo_count', 'photo_more', 'title_position', 'caption', 'media', 'main_size', 'thumb_size', 'tile_size', 'video_size', 'popup', 'thumbnail_effect', // All lightbox
61 'speed', 'timeout', 'fx', 'pause', 'strip-style', 'controls', // Slideshow
62 ),
63 'flickr' => array('user_id', 'group_id', 'collections_display', 'tags', 'tag_mode', 'text', 'sort', 'privacy_filter',),
64 'smugmug' => array('nick_name', 'password', 'text', 'keywords', 'sort_order', 'sort_method', 'album_sort_order', ),
65 'google' => array('user_id', 'access', 'protection', 'crop_thumb', 'content_filters',),
66 'zenfolio' => array('login_name', 'text', 'category_code', 'sort_order', 'structure', ),
67 'instagram' => array(),
68 'wp' => array(),
69 );
70 $this->aka_attributes = array(
71 'flickr' => array('per_page' => 'count'),
72 'zenfolio' => array('limit' => 'count'),
73 'wp' => array('slide_size' => 'main_size'),
74 );
75 }
76
77 /**
78 * Gets the content to be displayed on a flow screen. This is invoked by an AJAX call. The current screen number is passed to this function.
79 * The function performs validations behind the scenes and if everything looks OK it returns the content for the next screen, otherwise
80 * an error is returned.
81 *
82 * @return string
83 */
84 function get_screen() {
85 $screen = isset($_POST['screen']) ? sanitize_text_field($_POST['screen']) : 0;
86 $provider = isset($_POST['provider']) ? sanitize_text_field($_POST['provider']) : '';
87 $display_type = isset($_POST['display_type']) ? sanitize_text_field($_POST['display_type']) : '';
88
89 $raw_shortcode = !empty($_POST['photonic-editor-shortcode-raw']) ? sanitize_text_field($_POST['photonic-editor-shortcode-raw']) : '';
90 if (!empty($raw_shortcode)) {
91 $input = base64_decode($raw_shortcode);
92 $input = json_decode($input);
93 if (!empty($input->shortcode) && !empty($input->shortcode->attrs) && !empty($input->shortcode->attrs->named)) {
94 $input = $input->shortcode->attrs->named;
95 }
96 }
97 else {
98 $raw_shortcode = !empty($_POST['photonic-editor-json']) ? stripslashes_deep($_POST['photonic-editor-json']) : '';
99 $input = json_decode($raw_shortcode);
100 if (!empty($_POST['photonic-gutenberg-active'])) {
101 $this->is_gutenberg = true;
102 }
103 }
104
105 $deconstructed = $this->deconstruct_shortcode($input);
106
107 $output = $this->validate($screen, $provider, $deconstructed);
108 if (!empty($output['error'])) {
109 return '<div class="photonic-flow-error">' . $output['error'] . "</div>\n";
110 }
111
112 $screen = ((int)$screen) + 1;
113 if ($this->force_next_screen > -1) {
114 $screen = $this->force_next_screen;
115 }
116
117 $screen_fields = array();
118 if (!empty($this->field_list['screen-' . $screen])) {
119 $screen_fields = $this->field_list['screen-' . $screen];
120 }
121 $ret = '';
122
123 if ($screen == 2 || $screen == '2') {
124 /*
125 * The display_type screen. This gathers inputs about what the user wants to show: photos from an album, a collection of
126 * albums, user trees, single photos etc., along with who the photos are from - the default user, a different user, any user etc.
127 */
128 $fields = $screen_fields[$provider]['display'];
129 $ret .= $this->render_all_fields($fields, $deconstructed);
130 $ret = (empty($screen_fields[$provider]['header']) ? '' : "<h1>" . $screen_fields[$provider]['header'] . "</h1>\n") .
131 (empty($screen_fields[$provider]['desc']) ? '' : "<p>" . $screen_fields[$provider]['desc'] . "</p>\n") .
132 $ret;
133 }
134 else if ($screen == 3) {
135 /*
136 * The Gallery Builder screen, where all the photos / albums / folders are displayed
137 */
138 $fields = $screen_fields[$provider][$display_type]['display'];
139 $ret .= $this->render_all_fields($fields, $deconstructed);
140
141 $ret = (empty($screen_fields[$provider][$display_type]['header']) ? '' : "<h1>" . $screen_fields[$provider][$display_type]['header'] . "</h1>\n") .
142 (empty($screen_fields[$provider][$display_type]['desc']) ? '' : "<p>" . $screen_fields[$provider][$display_type]['desc'] . "</p>\n") .
143 str_replace('{{placeholder_value}}', $output['success'], $ret);
144 }
145 else if ($screen == 4) {
146 /*
147 * The layout selection screen
148 */
149 $ret .= $output['success'];
150 }
151 else {
152 $ret .= $output['success'];
153 }
154
155 return $ret;
156 }
157
158 /**
159 * Checks all inputs amongst themselves. If the inputs are valid, then the content for the next screen is generated by this call.
160 * All heavy logic, including API calls and processing of the responses happens here.
161 *
162 * @param $screen
163 * @param $provider
164 * @param array $existing
165 * @return array|string
166 */
167 function validate($screen, $provider, $existing = array()) {
168 if (empty($screen) || !is_numeric($screen) || (is_numeric($screen) && (intval($screen) <= 0 || intval($screen) > 5))) {
169 return array('error' => sprintf(esc_html__('Invalid screen value: %s', 'photonic'), $screen));
170 }
171 if (!in_array($provider, array('wp', 'flickr', 'picasa', 'google', 'smugmug', 'zenfolio', 'instagram'))) {
172 return array('error' => sprintf(esc_html__('Invalid photo provider: %s', 'photonic'), $provider));
173 }
174
175 $screen = intval($screen);
176 $display_type = isset($_POST['display_type']) ? sanitize_text_field($_POST['display_type']) : '';
177
178 if ($screen == 1) {
179 switch ($provider) {
180 case 'flickr':
181 global $photonic_flickr_api_key, $photonic_flickr_api_secret;
182 if (empty($photonic_flickr_api_key) || empty($photonic_flickr_api_secret)) {
183 return array('error' => sprintf($this->error_missing_api, 'Flickr API key', '<em>Photonic &rarr; Settings &rarr; Flickr &rarr; Flickr Settings</em>'));
184 }
185 break;
186
187 case 'picasa':
188 return array('error' => esc_html__('Google has deprecated the Picasa API with effect from January 2019. Please consider using the Google Photos module.', 'photonic'));
189
190 case 'google':
191 global $photonic_google_client_id, $photonic_google_client_secret, $photonic_google_refresh_token;
192 // if (!empty($photonic_google_use_own_keys) && (empty($photonic_google_client_id) || empty($photonic_google_client_secret))) {
193 if (empty($photonic_google_client_id) || empty($photonic_google_client_secret)) {
194 return array('error' => sprintf($this->error_missing_api, 'Google Client ID', '<em>Photonic &rarr; Settings &rarr; Google Photos &rarr; Google Photos Settings</em>'));
195 }
196 else if (empty($photonic_google_refresh_token)) {
197 return array('error' => sprintf($this->error_authentication, 'Google Photos', '<em>Photonic &rarr; Authentication</em>'));
198 }
199 break;
200
201 case 'smugmug':
202 global $photonic_smug_api_key, $photonic_smug_api_secret;
203 if (empty($photonic_smug_api_key) || empty($photonic_smug_api_secret)) {
204 return array('error' => sprintf($this->error_missing_api, 'SmugMug API Key', '<em>Photonic &rarr; Settings &rarr; SmugMug &rarr; SmugMug Settings</em>'));
205 }
206 break;
207
208 case 'instagram':
209 global $photonic_instagram_access_token;
210 if (empty($photonic_instagram_access_token)) {
211 return array('error' => sprintf($this->error_authentication, 'Instagram', '<em>Photonic &rarr; Authentication</em>'));
212 }
213 break;
214
215 case 'zenfolio':
216 return '';
217
218 default: // wp
219 return '';
220 }
221 }
222 else if ($screen == 2) {
223 $screen_fields = $this->field_list['screen-' . $screen];
224 $fields = $screen_fields[$provider]['display'];
225 $flattened_fields = array();
226 foreach ($fields as $id => $field) {
227 if (!empty($field['type']) && $field['type'] != 'field_list') {
228 $flattened_fields[$id] = $field;
229 }
230 else if (!empty($field['type']) && $field['type'] == 'field_list') {
231 $flattened_fields = array_merge($field['list'], $flattened_fields);
232 }
233 }
234
235 if (empty($display_type) || (empty($_POST['for']) && in_array($provider, array('flickr', 'smugmug', 'zenfolio')) && empty($existing))) {
236 return array('error' => $this->error_mandatory);
237 }
238
239 if (empty($_POST['for']) && in_array($provider, array('flickr', 'smugmug', 'zenfolio')) && !empty($existing)) {
240 return array('error' => esc_html__('While the "For whom?" setting may not be required for the shortcode to function in the front-end, it is required to edit this shortcode in this editor. Please specify a value.', 'photonic'));
241 }
242
243 if (!in_array($display_type, array_keys($flattened_fields['display_type']['options']))) {
244 return array('error' => sprintf(esc_html__('Invalid display type: %s', 'photonic'), $display_type));
245 }
246
247 $for = !empty($_POST['for']) ? sanitize_text_field($_POST['for']) : null;
248
249 switch ($provider) {
250 case 'flickr':
251 if ($display_type != 'multi-photo' && in_array($for, array('group', 'any'))) {
252 $err = esc_html__('Incompatible selections:', 'photonic') . "<br/>\n";
253 $err .= $flattened_fields['display_type']['desc'] . ": " . $flattened_fields['display_type']['options'][$display_type] . "<br/>\n";
254 $err .= $flattened_fields['for']['desc'] . ": " . $flattened_fields['for']['options'][$for] . "<br/>\n";
255 return array('error' => $err);
256 }
257
258 $group = sanitize_text_field($_POST['group']);
259 $login = sanitize_text_field($_POST['login']);
260 global $photonic_flickr_default_user, $photonic_flickr_api_key;
261 if ($for == 'current' && empty($photonic_flickr_default_user)) {
262 return array('error' => sprintf(esc_html__('Default user not defined under %1$s. %2$sSelect "Another user" and put in your user id.', 'photonic'),
263 '<em>Photonic &rarr; Settings &rarr; Flickr &rarr; Flickr Settings &rarr; Default User</em>', '<br/>'));
264 }
265
266 if (($for == 'group' && empty($group)) || ($for == 'other' && empty($login))) {
267 return array('error' => $this->error_mandatory);
268 }
269
270 $parameters = array();
271 $user = $for == 'current' ? $photonic_flickr_default_user : ($for == 'other' ? $login : '');
272 if (($for == 'other' || $for == 'current') && !empty($user)) {
273 $url = 'https://api.flickr.com/services/rest/?format=json&nojsoncallback=1&api_key=' . $photonic_flickr_api_key . '&method=flickr.urls.lookupUser&url=' . urlencode('https://www.flickr.com/photos/') . $user;
274 $response = wp_remote_request($url, array('sslverify' => PHOTONIC_SSL_VERIFY));
275 $response = $this->process_response($response, 'flickr', 'user');
276 if (!empty($response['error'])) {
277 // Maybe the user provided the full URL instead of just the user name?
278 $url = 'https://api.flickr.com/services/rest/?format=json&nojsoncallback=1&api_key=' . $photonic_flickr_api_key . '&method=flickr.urls.lookupUser&url=' . urlencode($user);
279 $response = wp_remote_request($url, array('sslverify' => PHOTONIC_SSL_VERIFY));
280 $response = $this->process_response($response, 'flickr', 'user');
281 if (!empty($response['error'])) {
282 return $response;
283 }
284 $parameters = array_merge($response['success'], $parameters);
285 }
286 else {
287 $parameters = array_merge($response['success'], $parameters);
288 }
289 }
290
291 if ($for == 'group' && !empty($group)) {
292 $url = 'https://api.flickr.com/services/rest/?format=json&nojsoncallback=1&api_key=' . $photonic_flickr_api_key . '&method=flickr.urls.lookupGroup&url=' . urlencode('https://www.flickr.com/groups/') . $group;
293 $response = wp_remote_request($url, array('sslverify' => PHOTONIC_SSL_VERIFY));
294 $response = $this->process_response($response, 'flickr', 'group');
295 if (!empty($response['error'])) {
296 // Maybe the user provided the full URL instead of just the group name?
297 $url = 'https://api.flickr.com/services/rest/?format=json&nojsoncallback=1&api_key=' . $photonic_flickr_api_key . '&method=flickr.urls.lookupGroup&url=' . urlencode($user);
298 $response = wp_remote_request($url, array('sslverify' => PHOTONIC_SSL_VERIFY));
299 $response = $this->process_response($response, 'flickr', 'group');
300 if (!empty($response['error'])) {
301 return $response;
302 }
303 $parameters = array_merge($response['success'], $parameters);
304 }
305 else {
306 $parameters = array_merge($response['success'], $parameters);
307 }
308 }
309
310 // All OK so far. Let's try to get the data for the next screen
311 $parameters['api_key'] = $photonic_flickr_api_key;
312 $parameters['format'] = 'json';
313 $parameters['nojsoncallback'] = 1;
314
315 if ($display_type == 'single-photo') {
316 $parameters['view'] = 'photo';
317 $parameters['method'] = 'flickr.photos.search';
318 $parameters['per_page'] = 500;
319 }
320 else if ($display_type == 'multi-photo') {
321 $parameters['view'] = 'photos';
322 $parameters['method'] = 'flickr.photos.search';
323 }
324 else if ($display_type == 'multi-album' || $display_type == 'album-photo') {
325 $parameters['view'] = 'photosets';
326 $parameters['method'] = 'flickr.photosets.getList';
327 }
328 else if ($display_type == 'multi-gallery' || $display_type == 'gallery-photo') {
329 $parameters['view'] = 'galleries';
330 $parameters['method'] = 'flickr.galleries.getList';
331 $parameters['per_page'] = 500;
332 }
333 else if ($display_type == 'collection' || $display_type == 'collections') {
334 $parameters['view'] = 'collections';
335 $parameters['method'] = 'flickr.collections.getTree';
336 }
337
338 global $photonic_flickr_gallery, $photonic_flickr_access_token;
339 if (!isset($photonic_flickr_gallery)) {
340 $photonic_flickr_gallery = new Photonic_Flickr_Processor();
341 }
342 if (!empty($photonic_flickr_access_token)) {
343 $parameters = $photonic_flickr_gallery->sign_call('https://api.flickr.com/services/rest/', 'GET', $parameters);
344 }
345
346 $url = add_query_arg($parameters, 'https://api.flickr.com/services/rest/');
347 $response = wp_remote_request($url, array('sslverify' => PHOTONIC_SSL_VERIFY));
348 $hidden = array();
349 if (isset($parameters['user_id'])) $hidden['user_id'] = $parameters['user_id'];
350 if (isset($parameters['group_id'])) $hidden['group_id'] = $parameters['group_id'];
351 return $this->process_response($response, 'flickr', $display_type, $hidden, $existing, $url);
352
353 case 'smugmug':
354 global $photonic_smug_default_user, $photonic_smug_api_key;
355 if ($for == 'current' && empty($photonic_smug_default_user)) {
356 return array('error' => sprintf(esc_html__('Default user not defined under %1$s. %2$sSelect "Another user" and put in your user id.', 'photonic'), '<em>Photonic &rarr; Settings &rarr; SmugMug &rarr; SmugMug Settings &rarr; Default User</em>', '<br/>'));
357 }
358
359 if ($for == 'other' && empty($_POST['user'])) {
360 return array('error' => $this->error_mandatory);
361 }
362
363 $nick_name = sanitize_text_field($_POST['user']);
364 $user = $for == 'current' ? $photonic_smug_default_user : ($for == 'other' ? $nick_name : '');
365 $args = array(
366 'APIKey' => $photonic_smug_api_key,
367 '_accept' => 'application/json',
368 '_expandmethod' => 'inline',
369 '_verbosity' => '1',
370 );
371
372 if ($display_type == 'album-photo' || $display_type == 'multi-album') {
373 $call = "https://api.smugmug.com/api/v2/user/$user!albums";
374 $args['_expand'] = 'HighlightImage.ImageSizes';
375 $args['count'] = 500;
376 }
377 else {
378 $call = "https://api.smugmug.com/api/v2/user/$user";
379 }
380
381 global $photonic_smugmug_gallery, $photonic_smug_access_token;
382 if (!isset($photonic_smugmug_gallery)) {
383 $photonic_smugmug_gallery = new Photonic_SmugMug_Processor();
384 }
385
386 if (!empty($photonic_smug_access_token)) {
387 $args = $photonic_smugmug_gallery->sign_call($call, 'GET', $args);
388 }
389
390 $call = add_query_arg($args, $call);
391 if ($display_type == 'folder' || $display_type == 'tree' || $display_type == 'folder-photo' || $display_type == 'user-photo') {
392 $temp = wp_remote_request($call, array('sslverify' => PHOTONIC_SSL_VERIFY));
393 $temp = $this->process_response($temp, 'smugmug', 'user');
394 if (!empty($temp['success'])) {
395 $node = $temp['success'];
396 $call = 'https://api.smugmug.com' . $node . '!children';
397 if ($display_type == 'tree' || $display_type == 'user-photo') {
398 $args['_expand'] = 'NodeCoverImage.ImageSizes';
399 }
400 else {
401 $config = $photonic_smugmug_gallery->get_config_object(500);
402 $config_str = json_encode($config);
403 $args['_config'] = $config_str;
404 }
405 $call = add_query_arg($args, $call);
406 }
407 else {
408 return $temp;
409 }
410 }
411
412 $response = wp_remote_request($call);
413 return $this->process_response($response, 'smugmug', $display_type, array('nick_name' => $user), $existing, $call);
414
415 case 'google':
416 global $photonic_google_refresh_token, $photonic_google_gallery;
417 if (!isset($photonic_google_gallery)) {
418 $photonic_google_gallery = new Photonic_Google_Photos_Processor();
419 }
420
421 $photonic_google_gallery->perform_back_end_authentication($photonic_google_refresh_token);
422
423 $parameters = array();
424
425 // All OK so far. Let's try to get the data for the next screen
426 $parameters['access_token'] = $photonic_google_gallery->access_token;
427
428 $query_url = '';
429 if ($display_type == 'multi-photo') {
430 $query_url = 'https://photoslibrary.googleapis.com/v1/mediaItems:search';
431 }
432 else if ($display_type == 'multi-album' || $display_type == 'album-photo') {
433 $query_url = 'https://photoslibrary.googleapis.com/v1/albums?pageSize=50';
434 }
435
436 $query_url = add_query_arg($parameters, $query_url);
437 $response = wp_remote_request($query_url, array('method' => ($display_type == 'multi-photo' ? 'POST' : 'GET'), 'sslverify' => PHOTONIC_SSL_VERIFY));
438 return $this->process_response($response, 'google', $display_type, array(), $existing, $query_url);
439
440 case 'zenfolio':
441 if ((!in_array($display_type, array('multi-photo', /*'multi-gallery', 'multi-collection'*/)) && $for == 'any') ||
442 (!in_array($display_type, array('single-photo', 'gallery-photo', 'collection-photo', 'multi-gallery', 'multi-collection', 'multi-gallery-collection', 'group', 'group-hierarchy')) && in_array($for, array('current', 'other')))) {
443 $err = esc_html__('Incompatible selections:', 'photonic') . "<br/>\n";
444 $err .= $flattened_fields['display_type']['desc'] . ": " . $flattened_fields['display_type']['options'][$display_type] . "<br/>\n";
445 $err .= $flattened_fields['for']['desc'] . ": " . $flattened_fields['for']['options'][$for] . "<br/>\n";
446 return array('error' => $err);
447 }
448
449 if ($for == 'other' && empty($_POST['login_name'])) {
450 return array('error' => $this->error_mandatory);
451 }
452
453 $login_name = sanitize_text_field($_POST['login_name']);
454 global $photonic_zenfolio_default_user;
455 if ($for == 'current' && empty($photonic_zenfolio_default_user)) {
456 return array('error' => sprintf(esc_html__('Default user not defined under %1$s. %2$sSelect "Another user" and put in your user id.', 'photonic'), '<em>Photonic &rarr; Settings &rarr; Zenfolio &rarr; Zenfolio Photo Settings &rarr; Default User</em>', '<br/>'));
457 }
458
459 $parameters = array();
460 $user = $for == 'current' ? $photonic_zenfolio_default_user : ($for == 'other' ? $login_name : '');
461
462 if ($display_type == 'multi-photo') {
463 $url = 'https://api.zenfolio.com/api/1.8/zfapi.asmx/SearchPhotoByCategory';
464 $parameters['searchId'] = '5';
465 $parameters['sortOrder'] = 'Popularity';
466 $parameters['categoryCode'] = '1018000';
467 $parameters['offset'] = 0;
468 $parameters['limit'] = 500;
469 }
470 else if (in_array($display_type, array('multi-gallery', 'multi-collection')) && $for == 'any') {
471 $url = 'https://api.zenfolio.com/api/1.8/zfapi.asmx/SearchSetByCategory';
472 $parameters['searchId'] = '5';
473 $parameters['sortOrder'] = 'Popularity';
474 $parameters['categoryCode'] = '1018000';
475 $parameters['offset'] = 0;
476 $parameters['limit'] = 500;
477 }
478 else {
479 $url = 'https://api.zenfolio.com/api/1.8/zfapi.asmx/LoadGroupHierarchy';
480 $parameters['loginName'] = $user;
481 }
482
483 $response = wp_remote_request($url, array('sslverify' => PHOTONIC_SSL_VERIFY, 'body' => $parameters));
484 return $this->process_response($response, 'zenfolio', $display_type, array('login_name' => $user), $existing, $url);
485
486 case 'instagram':
487 global $photonic_instagram_access_token;
488 $base_url = 'https://api.instagram.com/v1/users/self/media/recent/?access_token='.$photonic_instagram_access_token;
489 $response = wp_remote_request($base_url);
490 return $this->process_response($response, 'instagram', $display_type, array(), $existing, $base_url);
491
492 default: // wp
493 $this->force_next_screen = 4;
494 $this->force_previous_screen = 2;
495 return array('success' => $this->get_layout_selector($display_type, $existing));
496 }
497 }
498 else if ($screen == 3) {
499 //Check for display_type
500 $screen_fields = $this->field_list['screen-' . $screen];
501 $provider_fields = $screen_fields[$provider];
502 $fields = $provider_fields[$display_type]['display'];
503 foreach ($fields as $id => $field) {
504 $checks = $this->do_basic_option_check($id, $field, true);
505 if (!empty($checks)) {
506 return $checks;
507 }
508
509 if ($id == 'selection' && sanitize_text_field($_POST['selection']) == 'selected' && empty($_POST['selected_data'])) {
510 return array('error' => esc_html__('Please select what you want to show.', 'photonic'));
511 }
512
513 if (in_array($display_type, array('single-photo', 'album-photo', 'gallery-photo', 'folder-photo', 'collection-photo')) && empty($_POST['selected_data'])) {
514 return array('error' => esc_html__('Please select what you want to show.', 'photonic'));
515 }
516 }
517
518 // All OK? Get next screen
519 if ($display_type != 'single-photo') {
520 $output = $this->get_layout_selector($display_type, $existing);
521 }
522 else {
523 $this->force_next_screen = 6;
524 $this->force_previous_screen = 3;
525 $output = $this->construct_shortcode();
526 }
527 return array('success' => $output);
528 }
529 else if ($screen == 4) {
530 $layout = isset($_POST['layout']) ? sanitize_text_field($_POST['layout']) : '';
531 if (empty($layout)) {
532 return array('error' => $this->error_mandatory);
533 }
534 $layout_options = $this->flow_fields->get_layout_options();
535 if (!array_key_exists($layout, $layout_options)) {
536 return array('error' => sprintf(esc_html__('Invalid layout: %s', 'photonic'), $layout));
537 }
538
539 // All good. Next screen:
540 return array('success' => $this->get_layout_options($provider, $display_type, $layout, $existing));
541 }
542 else {
543 $passworded = isset($_POST['selection_passworded']) ? sanitize_text_field($_POST['selection_passworded']) : '';
544 $password = isset($_POST['password']) ? sanitize_text_field($_POST['password']) : '';
545 if (!empty($passworded) && empty($password)) {
546 return array('error' => $this->error_mandatory);
547 }
548 return array('success' => $this->construct_shortcode());
549 }
550 return '';
551 }
552
553 /**
554 * Utility method that takes an array of fields, determines if each member is a "field list" or a "field". If it is a field
555 * list it processes it as a field list, otherwise it processes it as a field.
556 *
557 * @param array $fields
558 * @param array $existing
559 * @return String
560 */
561 private function render_all_fields($fields, $existing = array()) {
562 $output = '';
563 foreach ($fields as $id => $field) {
564 if (!empty($field['type']) && $field['type'] == 'field_list') {
565 $output .= $this->process_field_list($id, $field, $existing);
566 }
567 else if (!empty($field['type'])) {
568 $output .= $this->process_field($id, $field, 0, null, $existing);
569 }
570 }
571 return $output;
572 }
573
574 /**
575 * Takes a "field list" and processes each field in it individually. A "field list" can contain an interdependent sequence
576 * of fields, in which case each member gets a sequential number assigned to it. The actual logic of sequencing is handled on the
577 * front-end.
578 * E.g. If, for Flickr, you select "Multiple Photos", then "Another User", the front-end will show a "User" text field.
579 * But if "Group" is selected, it shows a "Group" text field
580 *
581 * @param $field_list_name
582 * @param array $field_list
583 * @param array $existing
584 * @return string
585 */
586 function process_field_list($field_list_name, $field_list, $existing = array()) {
587 if (!is_array($field_list) || empty($field_list['type']) || $field_list['type'] != 'field_list' || empty($field_list['list'])) {
588 return '';
589 }
590 else {
591 $ret = '';
592 $counter = 0;
593 $sequence_group = null;
594 foreach ($field_list['list'] as $id => $field) {
595 if ($field_list['list_type'] == 'sequence') {
596 $counter++;
597 $sequence_group = $field_list_name;
598 }
599 $ret .= $this->process_field($id, $field, $counter, $sequence_group, $existing);
600 }
601 return $ret;
602 }
603 }
604
605 /**
606 * Main code to render an input element on the flow-screen. Almost all types of inputs have switches for display here.
607 *
608 * @param string $id
609 * @param array $field
610 * @param $sequence
611 * @param null $sequence_group
612 * @param array $existing
613 * @return string
614 */
615 function process_field($id, $field, $sequence, $sequence_group = null, $existing = array()) {
616 if (!is_array($field) || empty($field['type'])) {
617 return '';
618 }
619
620 if (!empty($field['post_condition']) && is_array($field['post_condition'])) {
621 foreach ($field['post_condition'] as $var => $permitted_values) {
622 $pass = false;
623 foreach ($permitted_values as $permitted) {
624 if (isset($_POST[$var]) && $_POST[$var] === $permitted) {
625 $pass = true;
626 break;
627 }
628 }
629 if (!$pass) {
630 // Variable has not been set in a different screen. Hide this field.
631 return '';
632 }
633 }
634 }
635
636 $req = empty($field['req']) ? '' : '<span class="photonic-required"><abbr title="'.esc_html__('Required', 'photonic').'">*</abbr></span>';
637 $default = !empty($existing[$id]) ? $existing[$id] : (isset($field['std']) ? $field['std'] : '');
638 $hint = '';
639 $hint_in = '';
640 if (!empty($field['hint'])) {
641 $hint = "<div class='photonic-flow-hint' role='tooltip' id='{$id}-hint'>{$field['hint']}</div>\n";
642 $hint_in = "aria-describedby='{$id}-hint'";
643 }
644
645 switch ($field['type']) {
646 case 'text':
647 $ret = "<label class='photonic-flow-option-name'>{$field['desc']}$req<input type='text' name='{$id}' value='" . $default . "' $hint_in/>$hint</label>";
648 break;
649
650 case 'radio':
651 $ret = !empty($field['desc']) ? '<div class="photonic-flow-option-name">' . $field['desc'] . $req . '</div>' : '';
652 foreach ($field['options'] as $option_value => $option_description) {
653 $option_condition = (empty($field['option-conditions']) || empty($field['option-conditions'][$option_value])) ? '' :
654 "data-photonic-option-condition='" . json_encode($field['option-conditions'][$option_value]) . "'";
655 $checked = checked($default, $option_value, false);
656 $ret .= "\t<div class='photonic-flow-field-radio'><label><input type='radio' name='{$id}' value='$option_value' $checked $option_condition/>" . $option_description . "</label></div>\n";
657 }
658 break;
659
660 case 'select':
661 $ret = "<label class='photonic-flow-option-name'>{$field['desc']}$req\n\t<select name='{$id}' $hint_in>\n";
662 foreach ($field['options'] as $option_value => $option_description) {
663 $option_condition = (empty($field['option-conditions']) || empty($field['option-conditions'][$option_value])) ? '' :
664 "data-photonic-option-condition='" . json_encode($field['option-conditions'][$option_value]) . "'";
665 $selected = selected($default, $option_value, false);
666 $ret .= "\t\t<option value='$option_value' $selected $option_condition>" . esc_attr($option_description) . "</option>\n";
667 }
668 $ret .= "\t</select>\n$hint</label>\n";
669 break;
670
671 case 'image-select':
672 $ret = '';
673 $ret .= '<div class="photonic-flow-option-name">' . $field['desc'] . '</div>';
674 if (!empty($default)) {
675 $selection = !in_array($default, $field['options']) ? array_keys($field['options'])[0] : $default;
676 }
677 else {
678 $selection = array_keys($field['options'])[0];
679 }
680 foreach ($field['options'] as $option_name => $desc) {
681 $esc_desc = esc_attr($desc);
682 $selected = ($option_name == $selection) ? 'selected' : '';
683 $ret .= "<div class=\"photonic-flow-selector photonic-flow-$id-$option_name $selected\" title=\"$esc_desc\">\n\t<span class=\"photonic-flow-selector-inner photonic-$id\" data-photonic-selection-id=\"$option_name\">&nbsp;</span>\n\t<div class='photonic-flow-selector-info'>$desc</div>\n</div>\n";
684 }
685 if (!empty($ret)) {
686 $ret = "<div class='photonic-flow-selector-container photonic-flow-$id' data-photonic-flow-selector-mode='single-no-plus' data-photonic-flow-selector-for=\"$id\">\n<input type=\"hidden\" id=\"$id\" name=\"$id\" value='$selection'/>\n$ret</div>\n";
687 }
688 break;
689
690 case 'multi-select':
691 $ret = '';
692 $ret .= '<div class="photonic-flow-option-name">' . $field['desc'] . '</div>';
693 $selection = explode(',', $default);
694 foreach ($field['options'] as $option_value => $desc) {
695 $checked = in_array($option_value, $selection) ? 'checked' : '';
696 $ret .= "\t<label class='photonic-multi-select-item'><input type='checkbox' name='{$id}[]' value=\"$option_value\" $checked />$desc</label>\n";
697 }
698 if (!empty($ret)) {
699 $ret = "<div class='photonic-flow-multi-select-container'>\n$ret</div>\n";
700 }
701 break;
702
703 case 'date-filter':
704 $ret = '';
705 $ret .= '<div class="photonic-flow-option-name">' . $field['desc'] . '</div>';
706 $dates = !empty($default) ? explode(',', $default) : array();
707 $count = isset($field['count']) && is_numeric($field['count']) ? intval($field['count']) : 1;
708 $ret .= "<ol data-photonic-date-filter='$id' data-photonic-filter-count='$count'>\n";
709 $ctr = 0;
710 foreach ($dates as $didx => $date) {
711 $y_m_d = explode('/', $date);
712 $ret .= "\t<li>\n";
713 $ret .= "\t\t<div class='photonic-single-date'>\n";
714 for ($pidx = 0; $pidx < 3; $pidx++) {
715 $lower = strtolower($this->date_parts[$pidx]);
716 $ret .= "\t\t\t<label class='photonic-date-filter'>\n" .
717 "\t\t\t\t".substr($this->date_parts[$pidx], 0, 1)."<input type='text' class='photonic-date-$lower' name='{$id}_{$lower}[]' value=\"" . (isset($y_m_d[$pidx]) ? $y_m_d[$pidx] : '') . "\" aria-describedby='{$id}-{$didx}_{$lower}-hint'/>\n" .
718 "\t\t\t\t<div class='photonic-flow-hint' role='tooltip' id='{$id}-{$didx}_{$lower}-hint'>" . $this->date_part_hints[$pidx] . "</div>\n" .
719 "\t\t\t</label> \n";
720 }
721 $ret .= "\t\t</div>\n";
722 $ret .= "\t\t<a href='#' class='photonic-remove-date-filter' title='Remove filter'><span class=\"dashicons dashicons-no\"> </span></a>\n";
723 $ret .= "\t</li>\n";
724 $ctr++;
725 if ($ctr >= $count) {
726 break;
727 }
728 }
729 $ret .= "</ol>\n";
730 $ret .= "<input type='hidden' name='$id' value='$default'/>\n";
731 if ($ctr < $count) {
732 $ret .= "<a href='#' class='photonic-add-date-filter' data-photonic-add-date='$id'><span class=\"dashicons dashicons-plus-alt\"> </span> Add filter</a>\n";
733 }
734
735 break;
736
737 case 'date-range-filter':
738 $ret = '';
739 $ret .= '<div class="photonic-flow-option-name">' . $field['desc'] . '</div>';
740 $date_ranges = !empty($default) ? explode(',', $default) : array();
741 $count = isset($field['count']) && is_numeric($field['count']) ? intval($field['count']) : 1;
742 $ret .= "<ol data-photonic-date-range-filter='$id' data-photonic-filter-count='$count'>\n";
743 $ctr = 0;
744 foreach ($date_ranges as $date_range) {
745 $from_to = explode('-', $date_range);
746 if (count($from_to) != 2) {
747 continue;
748 }
749 $ret .= "\t<li>\n";
750 foreach ($from_to as $didx => $date) {
751 $ret .= "\t\t<div class='photonic-single-date'>\n";
752 $y_m_d = explode('/', $date);
753 $from_or_to = $didx === 0 ? 'start' : 'end';
754 for ($pidx = 0; $pidx < 3; $pidx++) {
755 $lower = strtolower($this->date_parts[$pidx]);
756 $ret .= "\t\t\t<label class='photonic-date-filter'>\n" .
757 "\t\t\t\t".substr($this->date_parts[$pidx], 0, 1)."<input type='text' class='photonic-date-$lower' name='{$id}_{$from_or_to}_{$lower}[]' value=\"" . (isset($y_m_d[$pidx]) ? $y_m_d[$pidx] : '') . "\" aria-describedby='{$id}-{$didx}_{$from_or_to}_{$lower}-hint'/>\n" .
758 "\t\t\t\t<div class='photonic-flow-hint' role='tooltip' id='{$id}-{$didx}_{$from_or_to}_{$lower}-hint'>" . $this->date_part_hints[$pidx] . "</div>\n" .
759 "\t\t\t</label> \n";
760 }
761 $ret .= "\t\t</div>\n";
762 }
763 $ret .= "\t\t<a href='#' class='photonic-remove-date-range-filter' title='Remove filter'><span class=\"dashicons dashicons-no\"> </span></a>\n";
764 $ret .= "\t</li>\n";
765 $ctr++;
766 if ($ctr >= $count) {
767 break;
768 }
769 }
770 $ret .= "</ol>\n";
771 $ret .= "<input type='hidden' name='$id' value='$default'/>\n";
772 if ($ctr < $count) {
773 $ret .= "<a href='#' class='photonic-add-date-range-filter' data-photonic-add-date-range='$id'><span class=\"dashicons dashicons-plus-alt\"> </span> Add filter</a>\n";
774 }
775
776 break;
777
778 case 'thumbnail-selector':
779 $ret = "<div class=\"photonic-flow-selector-container\" data-photonic-flow-selector-mode=\"{$field['mode']}\" data-photonic-flow-selector-for=\"{$field['for']}\">\n{{placeholder_value}}</div>\n";
780
781 $controls = "<div class='thumb-controls'>\n";
782 if ($field['mode'] != 'none') {
783 $controls .= "<input type='text' class='search-thumbs' name='thumb-search' id='thumb-search'/>\n";
784 }
785
786 if ($field['mode'] == 'multi') {
787 $controls .= esc_html__('Mark:', 'photonic').
788 sprintf(esc_html__('%1$sAll%2$s', 'photonic'), "<a href='#' class='photonic-mark photonic-mark-all' data-photonic-mark-for='{$field['for']}'>", '</a>').'|'.
789 sprintf(esc_html__('%1$sNone%2$s', 'photonic'), "<a href='#' class='photonic-mark photonic-mark-none' data-photonic-mark-for='{$field['for']}'>", '</a>');
790 }
791 $controls .= "</div>\n";
792 $ret = $controls.$ret;
793 break;
794
795 default:
796 return '';
797 }
798
799 if (!empty($ret)) {
800 if (!empty($field['hint'])) {
801 // $ret .= "<div class='photonic-flow-hint' role='tooltip' id='{$id}-hint'><div>{$field['hint']}</div></div>\n";
802 }
803
804 $sequence_str = '';
805 if ($sequence !== 0) {
806 $sequence_str = 'data-photonic-flow-sequence="' . $sequence . '"';
807 }
808
809 $sequence_group_str = '';
810 if (!is_null($sequence_group)) {
811 $sequence_group_str = 'data-photonic-flow-sequence-group="' . $sequence_group . '"';
812 }
813
814 $condition = '';
815 if (!empty($field['conditions'])) {
816 $condition = "data-photonic-condition='" . json_encode($field['conditions']) . "'";
817 }
818
819 $ret = "<div class='photonic-flow-field' $sequence_str $condition $sequence_group_str>\n" . $ret . "</div>\n";
820 }
821 return $ret;
822 }
823
824 /**
825 * Performs basic checks against whitelists. More advanced checks are handled in the <code>validate</code> function
826 *
827 * @param $id
828 * @param $field
829 * @param bool $check_required
830 * @return array|bool
831 */
832 private function do_basic_option_check($id, $field, $check_required = false) {
833 if (empty($field['type']) || ($field['type'] != 'select' && $field['type'] != 'radio')) {
834 return false;
835 }
836
837 if ($check_required && !empty($field['req']) && (!isset($_POST[$id]) || trim($_POST[$id]) === '')) {
838 return array('error' => $this->error_mandatory);
839 }
840
841 if (isset($_POST[$id]) && !in_array(sanitize_text_field($_POST[$id]), array_keys($field['options']))) {
842 return array('error' => sprintf($this->error_not_permitted, $field['name'], $_POST[$id]));
843 }
844 return false;
845 }
846
847 /**
848 * A special type of selector not handled by the <code>process_field</code> call. Layouts are used only on one screen,
849 * and are used by almost all types of providers. This displays the available layouts as icons to pick from.
850 *
851 * @param $display_type
852 * @param array $existing
853 * @return string
854 */
855 private function get_layout_selector($display_type, $existing = array()) {
856 global $photonic_thumbnail_style;
857 $output = '';
858 $level = empty($this->display_types[$display_type]) ? -1 : $this->display_types[$display_type];
859 $layout_from_option = empty($existing['layout']) ? (in_array($photonic_thumbnail_style, array('strip-below', 'strip-above', 'strip-right', 'no-strip')) ? 'slideshow' : $photonic_thumbnail_style) : $existing['layout'];
860
861 if ($level > 0) {
862 $layout_options = $this->flow_fields->get_layout_options();
863 foreach ($layout_options as $layout => $desc) {
864 $selected = $layout == $layout_from_option ? 'selected' : '';
865 if (($layout == 'slideshow' && $level == 1) || $layout != 'slideshow') {
866 $esc_desc = esc_attr($desc);
867 $output .= "<div class=\"photonic-flow-selector photonic-flow-layout-$layout $selected\" title=\"$esc_desc\">\n\t<span class=\"photonic-flow-selector-inner photonic-layout\" data-photonic-selection-id=\"$layout\">&nbsp;</span>\n\t<div class='photonic-flow-selector-info'>$desc</div>\n</div>\n";
868 }
869 }
870 if (!empty($output)) {
871 $output = "<div class='photonic-flow-selector-container photonic-flow-layout' data-photonic-flow-selector-mode='single-no-plus' data-photonic-flow-selector-for=\"layout\">\n<input type=\"hidden\" id=\"layout\" name=\"layout\" value='$layout_from_option'/>\n$output</div>\n";
872 }
873 }
874 $output = '<h1>' . esc_html__('Pick Your Layout', 'photonic') . '</h1>' .
875 "<p>" . sprintf(esc_html__('You can configure the default settings from %s.', 'photonic'), '<strong>Photonic &rarr; Settings &rarr; Generic Options &rarr; Generic Settings &rarr; Layouts</strong>') . "</p>\n".
876 $output;
877
878 if ($this->force_next_screen > -1) {
879 $output .= "\n<input type='hidden' name='force_next_screen' value='{$this->force_next_screen}'/>\n";
880 }
881 if ($this->force_previous_screen > -1) {
882 $output .= "\n<input type='hidden' name='force_previous_screen' value='{$this->force_previous_screen}'/>\n";
883 }
884
885 return $output;
886 }
887
888 /**
889 * Layouts use similar constructs such as <code>count</code> and <code>more</code> but also have differences.
890 * E.g. <code>columns</code> are not applicable to the justified grid or mosaic layouts etc. Similarly size options
891 * vary from provider to provider. So in the Photonic_Flow_Fields we have a hierarchy for this screen, by level, provider and layout
892 *
893 * @param $provider
894 * @param $display_type
895 * @param $layout
896 * @param array $existing
897 * @return string
898 */
899 private function get_layout_options($provider, $display_type, $layout, $existing = array()) {
900 // All levels, all layouts - media
901 // L1, L2 All layouts - title position
902 // L1 All layouts - count, more
903 // L1, L2, L3 basic lightbox layouts - # of columns, constrain by etc., thumbnail size, full size
904 // L3 Flickr - auto-expand
905 $level = $this->display_types[$display_type];
906 $output = '<h1>' . esc_html__('Configure Your Layout', 'photonic') . '</h1>';
907
908 $extract = array();
909 if (!empty($this->field_list['screen-5'][$provider]['L' . $level])) {
910 $extract = array_merge($this->field_list['screen-5'][$provider]['L' . $level], $extract);
911 }
912
913 if (!empty($this->field_list['screen-5'][$layout][$provider])) {
914 $extract = array_merge($this->field_list['screen-5'][$layout][$provider], $extract);
915 }
916
917 if (!empty($this->field_list['screen-5'][$provider])) {
918 $extract = array_merge($this->field_list['screen-5'][$provider], $extract);
919 }
920
921 if (!empty($this->field_list['screen-5']['L' . $level])) {
922 $extract = array_merge($this->field_list['screen-5']['L' . $level], $extract);
923 }
924
925 if (!empty($this->field_list['screen-5'][$layout])) {
926 $extract = array_merge($this->field_list['screen-5'][$layout], $extract);
927 }
928
929 $output .= $this->render_all_fields($extract, $existing);
930 return $output;
931 }
932
933 /**
934 * Builds out the shortcode based on inputs from all previous screens. Some attributes are passed as they are, e.g. <code>count</code>.
935 * Others are used to determine other attributes, e.g. <code>display_type</code>
936 *
937 * @return string
938 */
939 private function construct_shortcode() {
940 global $photonic_alternative_shortcode;
941
942 $provider = sanitize_text_field($_POST['provider']);
943 $display_type = sanitize_text_field($_POST['display_type']);
944
945 $short_code = array();
946 if ($provider !== 'wp') {
947 $short_code['type'] = $provider;
948 }
949
950 switch ($provider) {
951 case 'flickr':
952 if ($display_type == 'single-photo') {
953 $short_code['view'] = 'photo';
954 $short_code['photo_id'] = sanitize_text_field($_POST['selected_data']);
955 }
956 else if ($display_type == 'multi-photo') {
957 $short_code['view'] = 'photos';
958 }
959 else if ($display_type == 'album-photo') {
960 $short_code['view'] = 'photosets';
961 $short_code['photoset_id'] = sanitize_text_field($_POST['selected_data']);
962 }
963 else if ($display_type == 'gallery-photo') {
964 $short_code['view'] = 'galleries';
965 $short_code['gallery_id'] = sanitize_text_field($_POST['selected_data']);
966 }
967 else if ($display_type == 'multi-album') {
968 $short_code['view'] = 'photosets';
969 }
970 else if ($display_type == 'multi-gallery') {
971 $short_code['view'] = 'galleries';
972 }
973 else if ($display_type == 'collection') {
974 $short_code['view'] = 'collections';
975 $short_code['collection_id'] = sanitize_text_field($_POST['selected_data']);
976 }
977 else if ($display_type == 'collections') {
978 $short_code['view'] = 'collections';
979 }
980 break;
981
982 case 'smugmug':
983 if ($display_type == 'album-photo') {
984 $short_code['view'] = 'album';
985 $short_code['album'] = sanitize_text_field($_POST['selected_data']);
986 }
987 else if ($display_type == 'folder-photo') {
988 $short_code['view'] = 'images';
989 $short_code['folder'] = sanitize_text_field($_POST['selected_data']);
990 }
991 else if ($display_type == 'user-photo') {
992 $short_code['view'] = 'images';
993 }
994 else if ($display_type == 'multi-album') {
995 $short_code['view'] = 'albums';
996 }
997 else if ($display_type == 'tree') {
998 $short_code['view'] = 'tree';
999 }
1000 else if ($display_type == 'folder') {
1001 $short_code['view'] = 'folder';
1002 $short_code['folder'] = sanitize_text_field($_POST['selected_data']);
1003 }
1004 break;
1005
1006 case 'google':
1007 if ($display_type == 'album-photo') {
1008 $short_code['view'] = 'photos';
1009 $short_code['album_id'] = sanitize_text_field($_POST['selected_data']);
1010 }
1011 else if ($display_type == 'multi-photo') {
1012 $short_code['view'] = 'photos';
1013 $date_filters = !empty($_POST['date_filters']) ? sanitize_text_field($_POST['date_filters']) : '';
1014 $date_range_filters = !empty($_POST['date_range_filters']) ? sanitize_text_field($_POST['date_range_filters']) : '';
1015 $short_code['date_filters'] = trim($date_filters.','.$date_range_filters, ',');
1016 }
1017 else if ($display_type == 'multi-album') {
1018 $short_code['view'] = 'albums';
1019 }
1020 break;
1021
1022 case 'zenfolio':
1023 if ($display_type == 'multi-photo') {
1024 $short_code['view'] = 'photos';
1025 }
1026 else if ($display_type == 'single-photo') {
1027 $short_code['view'] = 'photos';
1028 $short_code['object_id'] = sanitize_text_field($_POST['selected_data']);
1029 }
1030 else if ($display_type == 'gallery-photo'/* || $display_type == 'collection-photo'*/) {
1031 $short_code['view'] = 'photosets';
1032 $short_code['object_id'] = sanitize_text_field($_POST['selected_data']);
1033 }
1034 else if ($display_type == 'multi-gallery' || $display_type == 'multi-collection' || $display_type == 'multi-gallery-collection') {
1035 $short_code['view'] = 'photosets';
1036 if ($display_type == 'multi-gallery') {
1037 $short_code['photoset_type'] = 'Gallery';
1038 }
1039 else if ($display_type == 'multi-collection') {
1040 $short_code['photoset_type'] = 'Collection';
1041 }
1042 }
1043 else if ($display_type == 'group') {
1044 $short_code['view'] = 'group';
1045 $short_code['object_id'] = sanitize_text_field($_POST['selected_data']);
1046 }
1047 else if ($display_type == 'group-hierarchy') {
1048 $short_code['view'] = 'hierarchy';
1049 }
1050 break;
1051
1052 case 'instagram':
1053 if ($display_type == 'single-photo') {
1054 $short_code['media_id'] = sanitize_text_field($_POST['selected_data']);
1055 }
1056 break;
1057
1058 default:
1059 if ($display_type == 'current-post' && !empty($_POST['post_id'])) {
1060 $short_code['id'] = sanitize_text_field($_POST['post_id']);
1061 }
1062 else if (!empty($_POST['selected_data'])) {
1063 $short_code['ids'] = sanitize_text_field($_POST['selected_data']);
1064 }
1065 break;
1066 }
1067
1068 if (!empty($_POST['selection'])) {
1069 if ($_POST['selection'] != 'all') {
1070 $short_code['filter'] = sanitize_text_field($_POST['selected_data']);
1071 }
1072 if ($_POST['selection'] == 'not-selected') {
1073 $short_code['filter_type'] = 'exclude';
1074 }
1075 }
1076
1077 if (isset($_POST['headers']) && trim($_POST['headers'] !== '')) {
1078 if (trim($_POST['headers']) == 'none') {
1079 $short_code['headers'] = '';
1080 }
1081 else {
1082 $short_code['headers'] = sanitize_text_field($_POST['headers']);
1083 }
1084 }
1085
1086 $additional_attrs = array_merge(
1087 $this->shortcode_attributes[$provider],
1088 $this->shortcode_attributes['common']
1089 );
1090 foreach ($additional_attrs as $attr) {
1091 if (!empty($_POST[$attr]) && is_array($_POST[$attr])) {
1092 $short_code[$attr] = implode(',', $_POST[$attr]);
1093 }
1094 else if (!empty($_POST[$attr])) {
1095 $short_code[$attr] = $_POST[$attr];
1096 }
1097 }
1098
1099 if (!empty($_POST['layout'])) {
1100 $key = $provider == 'wp' ? 'style' : 'layout';
1101 if ($_POST['layout'] != 'slideshow') {
1102 $short_code[$key] = sanitize_text_field($_POST['layout']);
1103 }
1104 else {
1105 $short_code[$key] = sanitize_text_field($_POST['slideshow-style']);
1106 }
1107 }
1108 // layout
1109
1110 $raw_shortcode = !empty($_POST['photonic-editor-shortcode-raw']) ? sanitize_text_field($_POST['photonic-editor-shortcode-raw']) : '';
1111 if (!empty($raw_shortcode)) {
1112 $input = base64_decode($raw_shortcode);
1113 $input = json_decode($input);
1114 if (!empty($input->shortcode) && !empty($input->shortcode->attrs) && !empty($input->shortcode->attrs->named)) {
1115 $input = $input->shortcode->attrs->named;
1116 }
1117 }
1118 else {
1119 $raw_shortcode = !empty($_POST['photonic-editor-json']) ? stripslashes_deep($_POST['photonic-editor-json']) : '';
1120 $input = json_decode($raw_shortcode);
1121 }
1122
1123 if (!empty($input)) {
1124 $attr_array = (array)$input;
1125
1126 // If the type changes, regardless of everything else blank out the attributes
1127 if (!(empty($short_code['type']) && (empty($attr_array['type']) || in_array($attr_array['type'], array('wp', 'default')))) &&
1128 ($attr_array['type'] != $short_code['type'])) {
1129 $attr_array = array();
1130 }
1131 else {
1132 foreach ($short_code as $key => $value) {
1133 unset($attr_array[$key]);
1134 }
1135 }
1136
1137 if (!empty($this->aka_attributes[$provider])) {
1138 $aka = $this->aka_attributes[$provider];
1139 foreach ($aka as $key => $value) {
1140 if (isset($short_code[$key]) || isset($short_code[$value])) unset($attr_array[$key]);
1141 }
1142 }
1143
1144 // Others ...
1145 if (!empty($short_code['type']) && $short_code['type'] == 'instagram') {
1146 if (!empty($short_code['media_id'])) {
1147 unset($attr_array['view']);
1148 unset($attr_array['media']);
1149 }
1150 else if (!empty($short_code['media'])) {
1151 unset($attr_array['media_id']);
1152 }
1153 }
1154 $short_code = array_merge($short_code, $attr_array);
1155 }
1156
1157 if (!$this->is_gutenberg) {
1158 $output = '<h1>' . esc_html__('Your shortcode', 'photonic') . '</h1>';
1159 $output .= "<code id='photonic_shortcode'>[" . (empty($photonic_alternative_shortcode) ? 'gallery' : $photonic_alternative_shortcode).' ';
1160 $shortcode_attrs = array();
1161 foreach ($short_code as $attr => $value) {
1162 $shortcode_attrs[] = $attr . "='" . esc_attr($value) . "'";
1163 }
1164
1165 $output .= implode(' ', $shortcode_attrs);
1166 $output .= ']</code>';
1167 $output .= '<p>'.esc_html__('The above shortcode was generated based on your selections. You can either copy the above and paste it manually into your post, or click on the buttons below to insert it into or update your post', 'photonic').'</p>';
1168 }
1169 else {
1170 $output = '<h1>' . esc_html__('Your Gallery', 'photonic') . '</h1>';
1171 $output .= '<p>'.esc_html__('Based on your selections a gallery with the following attributes will be generated. Please click on the buttons below to insert it into or update your post:', 'photonic').'</p>';
1172 $shortcode_attrs = array();
1173 foreach ($short_code as $attr => $value) {
1174 $shortcode_attrs[] = '<code>'.$attr . ": " . esc_attr($value).'</code>';
1175 }
1176 $output .= implode("<br/>\n", $shortcode_attrs);
1177
1178 $output .= '<p>'.esc_html__('The following is the corresponding shortcode for this block. If for some reason you are unable to create a Photonic block you can paste this into a Shortcode block:', 'photonic').'</p>';
1179 $output .= "<code>[" . (empty($photonic_alternative_shortcode) ? 'gallery' : $photonic_alternative_shortcode).' ';
1180 $shortcode_attrs = array();
1181 foreach ($short_code as $attr => $value) {
1182 $shortcode_attrs[] = $attr . "='" . esc_attr($value) . "'";
1183 }
1184
1185 $output .= implode(' ', $shortcode_attrs);
1186 $output .= ']</code>';
1187
1188 $output .= "<input id='photonic_shortcode' name='photonic_shortcode' type='hidden' value='".esc_attr(json_encode($short_code))."' />\n";
1189 }
1190
1191 if ($this->force_next_screen > -1) {
1192 $output .= "\n<input type='hidden' name='force_next_screen' value='{$this->force_next_screen}'/>\n";
1193 }
1194 if ($this->force_previous_screen > -1) {
1195 $output .= "\n<input type='hidden' name='force_previous_screen' value='{$this->force_previous_screen}'/>\n";
1196 }
1197 return $output;
1198 }
1199
1200 /**
1201 * This is the inverse of the <code>construct_shortcode</code> method. If the Editor has a shortcode selected, this method
1202 * splits it out into the relevant screens.
1203 *
1204 * @param $input
1205 * @return array
1206 */
1207 private function deconstruct_shortcode($input) {
1208 $deconstructed = array();
1209 if (!empty($input)) {
1210 if ((!empty($input->type) && in_array($input->type, array('wp', 'default', 'flickr', 'smugmug', 'picasa', 'google', 'zenfolio', 'instagram'))) ||
1211 (empty($input->type) && !empty($input->style)) && in_array($input->style, array('square', 'circle', 'random', 'masonry', 'mosaic', 'strip-above', 'strip-below', 'strip-right', 'no-strip'))
1212 ) {
1213 $deconstructed['provider'] = !empty($input->type) ? $input->type : 'wp';
1214
1215 switch ($deconstructed['provider']) {
1216 case 'flickr':
1217 // Potential for an existing (old) shortcode to not have a user_id. E.g. Just defining photoset_id.
1218 // Rather than defaulting to the default user, we will make the user put it in this time.
1219 if (!empty($input->user_id)) {
1220 $deconstructed['for'] = 'other';
1221 $deconstructed['login'] = $input->user_id;
1222 }
1223 else if (!empty($input->group_id)) {
1224 $deconstructed['for'] = 'group';
1225 $deconstructed['group'] = $input->group_id;
1226 }
1227
1228 if (!empty($input->view)) {
1229 if ($input->view == 'collections' && empty($input->collection_id)) {
1230 $deconstructed['display_type'] = 'collections';
1231 }
1232 else if ($input->view == 'galleries' && empty($input->gallery_id)) {
1233 $deconstructed['display_type'] = 'multi-gallery';
1234 }
1235 else if ($input->view == 'photosets' && empty($input->photoset_id)) {
1236 $deconstructed['display_type'] = 'multi-album';
1237 }
1238 else if ($input->view == 'photos') {
1239 $deconstructed['display_type'] = 'multi-photo';
1240 }
1241 }
1242
1243 if (!empty($input->collection_id)) {
1244 $deconstructed['display_type'] = 'collection';
1245 $deconstructed['selected_data'] = $input->collection_id;
1246 }
1247 else if (!empty($input->gallery_id)) {
1248 $deconstructed['display_type'] = 'gallery-photo';
1249 $deconstructed['selected_data'] = $input->gallery_id;
1250 }
1251 else if (!empty($input->photoset_id)) {
1252 $deconstructed['display_type'] = 'album-photo';
1253 $deconstructed['selected_data'] = $input->photoset_id;
1254 }
1255 else if (!empty($input->photo_id)) {
1256 $deconstructed['display_type'] = 'single-photo';
1257 $deconstructed['selected_data'] = $input->photo_id;
1258 }
1259
1260 break;
1261
1262 case 'smugmug':
1263 if (!empty($input->view)) {
1264 if ($input->view == 'tree') {
1265 $deconstructed['display_type'] = 'tree';
1266 }
1267 else if ($input->view == 'folder') {
1268 $deconstructed['display_type'] = 'folder';
1269 }
1270 else if ($input->view == 'albums') {
1271 $deconstructed['display_type'] = 'multi-album';
1272 }
1273 else if ($input->view == 'images' || $input->view == 'album') {
1274 if (!empty($input->album) || !empty($input->album_key) || !empty($input->album_id)) {
1275 $deconstructed['display_type'] = 'album-photo';
1276 }
1277 else if (!empty($input->folder)) {
1278 $deconstructed['display_type'] = 'folder-photo';
1279 }
1280 else if (!empty($input->nick_name)) {
1281 $deconstructed['display_type'] = 'user-photo';
1282 }
1283 }
1284
1285 if (!empty($input->folder)) {
1286 $deconstructed['selected_data'] = trim($input->folder);
1287 }
1288 else if (!empty($input->album_key)) { // old syntax
1289 $deconstructed['selected_data'] = $input->album_key;
1290 }
1291 else if (!empty($input->album)) {
1292 $album = explode('_', $input->album);
1293 if (count($album) == 2) {
1294 $deconstructed['selected_data'] = $album[1];
1295 }
1296 else if (count($album) == 1) {
1297 $deconstructed['selected_data'] = $album[0];
1298 }
1299 }
1300
1301 if (!empty($input->nick_name)) {
1302 $deconstructed['for'] = 'other';
1303 $deconstructed['user'] = $input->nick_name;
1304 }
1305 }
1306 break;
1307
1308 case 'google':
1309 if (!empty($input->view)) {
1310 if ($input->view == 'photos') {
1311 if (!empty($input->album_id)) {
1312 $deconstructed['display_type'] = 'album-photo';
1313 $deconstructed['selected_data'] = sanitize_text_field($input->album_id);
1314 }
1315 else {
1316 $deconstructed['display_type'] = 'multi-photo';
1317 if (!empty($input->date_filters)) {
1318 $filters = sanitize_text_field($input->date_filters);
1319 $filters = explode(',', $filters);
1320 $date_filters = array();
1321 $date_range_filters = array();
1322 foreach ($filters as $filter) {
1323 $maybe_range = explode('-', $filter);
1324 if (count($maybe_range) == 2) {
1325 $date_range_filters[] = $filter;
1326 }
1327 else if (count($maybe_range) == 1) {
1328 $date_filters[] = $filter;
1329 }
1330 }
1331 if (count($date_range_filters) > 5) {
1332 $date_range_filters = array_slice($date_range_filters, 0, 5);
1333 }
1334 if (count($date_filters) > 5) {
1335 $date_filters = array_slice($date_filters, 0, 5);
1336 }
1337 $date_range_filters = implode(',', $date_range_filters);
1338 $date_filters = implode(',', $date_filters);
1339 if (!empty($date_filters)) {
1340 $deconstructed['date_filters'] = $date_filters;
1341 }
1342 if (!empty($date_range_filters)) {
1343 $deconstructed['date_range_filters'] = $date_range_filters;
1344 }
1345 }
1346 }
1347 }
1348 else if ($input->view == 'albums') {
1349 $deconstructed['display_type'] = 'multi-album';
1350 }
1351 }
1352 break;
1353
1354 case 'zenfolio':
1355 if (!empty($input->view)) {
1356 if ($input->view == 'photos' && empty($input->object_id)) {
1357 $deconstructed['display_type'] = 'multi-photo';
1358 }
1359 else if ($input->view == 'photos' && !empty($input->object_id)) {
1360 $deconstructed['display_type'] = 'single-photo';
1361 $deconstructed['selected_data'] = $input->object_id;
1362 }
1363 else if ($input->view == 'photosets') {
1364 if (!empty($input->object_id)) {
1365 $deconstructed['display_type'] = 'gallery-photo';
1366 $deconstructed['selected_data'] = $input->object_id;
1367 }
1368 else if (empty($input->photoset_type)) {
1369 $deconstructed['display_type'] = 'multi-gallery-collection';
1370 }
1371 else if (strtolower($input->photoset_type) == 'collection') {
1372 $deconstructed['display_type'] = 'multi-collection';
1373 }
1374 else if (strtolower($input->photoset_type) == 'gallery') {
1375 $deconstructed['display_type'] = 'multi-gallery';
1376 }
1377 }
1378 else if ($input->view == 'hierarchy') {
1379 $deconstructed['display_type'] = 'group-hierarchy';
1380 }
1381 else if ($input->view == 'group') {
1382 $deconstructed['display_type'] = 'group';
1383 if (!empty($input->object_id)) {
1384 $deconstructed['selected_data'] = $input->object_id;
1385 }
1386 }
1387
1388 if ($input->view == 'photosets' || $input->view == 'hierarchy' || $input->view == 'group' ||
1389 $deconstructed['display_type'] == 'single-photo') {
1390 global $photonic_zenfolio_default_user;
1391 if (!isset($input->login_name) && !empty($photonic_zenfolio_default_user)) {
1392 $deconstructed['for'] = 'current';
1393 }
1394 else if (!empty($input->login_name)) {
1395 $deconstructed['login_name'] = $input->login_name;
1396 $deconstructed['for'] = 'other';
1397 }
1398 }
1399 }
1400 break;
1401
1402 case 'instagram':
1403 if (!empty($input->media_id)) {
1404 $deconstructed['display_type'] = 'single-photo';
1405 $deconstructed['selected_data'] = $input->media_id;
1406 }
1407 else {
1408 $deconstructed['display_type'] = 'multi-photo';
1409 }
1410 break;
1411
1412 default:
1413 case 'wp':
1414 case 'default':
1415 if (empty($input->id) && empty($input->ids) && empty($input->include)) {
1416 $deconstructed['display_type'] = 'current-post';
1417 }
1418 else if (!empty($input->ids) || !empty($input->include)) {
1419 $deconstructed['display_type'] = 'multi-photo';
1420 $deconstructed['selected_data'] = !empty($input->ids) ? $input->ids : $input->include;
1421 }
1422 break;
1423 }
1424
1425 if (!empty($input->filter)) {
1426 $deconstructed['selected_data'] = $input->filter;
1427 if (empty($input->filter_type) && in_array($input->filter_type, array('include', 'exclude'))) {
1428 $deconstructed['selection'] = $input->filter_type;
1429 }
1430 else {
1431 $deconstructed['selection'] = 'selected';
1432 }
1433 }
1434 else {
1435 $deconstructed['selection'] = 'all';
1436 }
1437
1438 if (isset($this->aka_attributes[$deconstructed['provider']])) {
1439 $aka_attributes = $this->aka_attributes[$deconstructed['provider']];
1440 foreach ($aka_attributes as $attr => $aka) {
1441 if (isset($input->{$attr}) && !isset($deconstructed[$aka])) {
1442 $deconstructed[$aka] = sanitize_text_field($input->{$attr});
1443 }
1444 }
1445 }
1446
1447 $layout = empty($input->layout) ? (empty($input->style) ? '' : $input->style) : $input->layout;
1448 if (!empty($layout)) {
1449 if (in_array($layout, array('square', 'circle', 'random', 'masonry', 'mosaic',))) {
1450 $deconstructed['layout'] = $layout;
1451 }
1452 else if (in_array($layout, array('strip-above', 'strip-below', 'strip-right', 'no-strip'))) {
1453 $deconstructed['layout'] = 'slideshow';
1454 $deconstructed['slideshow-style'] = $layout;
1455 }
1456 }
1457
1458 $same_name_attrs = array_merge($this->shortcode_attributes['common'], $this->shortcode_attributes[$deconstructed['provider']]);
1459 foreach ($same_name_attrs as $attr) {
1460 if (isset($input->{$attr}) && !isset($deconstructed[$attr])) {
1461 $deconstructed[$attr] = $input->{$attr};
1462 }
1463 }
1464 }
1465 }
1466 return $deconstructed;
1467 }
1468
1469 /**
1470 * Displays an array of L1, L2 or L3 objects as a series of selectable thumbnails. Titles are deliberately not displayed because they mess
1471 * with the layout if they are too long.
1472 *
1473 * @param array $objects
1474 * @param bool $provider
1475 * @param array $existing
1476 * @param array $present
1477 * @param bool $more
1478 * @return string
1479 */
1480 function get_thumbnail_display($objects, $provider = false, $existing = array(), &$present = array(), $more = false) {
1481 $output = '';
1482 $selected_data = empty($existing['selected_data']) ? array() : explode(',', $existing['selected_data']);
1483 foreach ($objects as $object) {
1484 if (!is_array($object)) {
1485 $output .= '<h4>'.$object."</h4>\n";
1486 continue;
1487 }
1488 $selected = '';
1489 if (in_array($object['id'], $selected_data) || (!empty($object['alt_id']) && in_array($object['alt_id'], $selected_data))
1490 || (!empty($object['alt_id2']) && in_array($object['alt_id2'], $selected_data))) {
1491 $selected = 'selected';
1492 $present[] = $object['id'];
1493 }
1494 $passworded = !empty($object['passworded']) ? 'passworded' : '';
1495
1496 $title = !empty($object['title']) ? esc_attr($object['title']) : '';
1497 $counts = !empty($object['counters']) ? ' (' . esc_attr(implode(', ', $object['counters'])) . ')' : '';
1498 $alt = !empty($object['alt_id']) ? "data-photonic-selection-alt-id='{$object['alt_id']}'" : '';
1499 $alt_2 = !empty($object['alt_id2']) ? "data-photonic-selection-alt-id-2='{$object['alt_id2']}'" : '';
1500
1501 $output .= "<div class='photonic-flow-selector $provider $selected $passworded'>\n";
1502 $output .= "\t<div class='photonic-flow-selector-inner' data-photonic-selection-id='{$object['id']}' $alt $alt_2>\n";
1503 $output .= "\t\t<img src='{$object['thumbnail']}' alt='$title$counts' title='$title$counts' />\n";
1504 $output .= "\t</div>\n";
1505 $output .= "</div>\n";
1506 }
1507 if ($more) {
1508 return $output;
1509 }
1510
1511 $output .= "<input type='hidden' name='existing_selection' id='existing_selection' value='" . (!empty($present) ? implode(',', $present) : '') . "'/>\n";
1512 return $output;
1513 }
1514
1515 /**
1516 * A recursive call to traverse a SmugMug node and generate a list of objects, with each object corresponding to a folder.
1517 * Actually a node is used instead of the folder because the folder object is deprecated by SmugMug, but nodes are only being
1518 * used for folders in Photonic.
1519 *
1520 * @param array $objects
1521 * @param $node
1522 */
1523 private function get_smugmug_folders(&$objects, $node) {
1524 $object = array();
1525 if ($node->Type == 'Folder') {
1526 $object['id'] = $node->NodeID;
1527 $object['title'] = !empty($node->Name) ? esc_attr($node->Name) : '';
1528
1529 if (isset($node->Uris->NodeCoverImage->Image->Uris->ImageSizes->ImageSizes->ThumbImageUrl)) {
1530 $object['thumbnail'] = $node->Uris->NodeCoverImage->Image->Uris->ImageSizes->ImageSizes->ThumbImageUrl;
1531 }
1532 else {
1533 $object['thumbnail'] = trailingslashit(PHOTONIC_URL) . 'include/images/placeholder-Th.png';
1534 }
1535
1536 if (isset($node->Uris->ChildNodes->Node)) {
1537 $child_nodes = $node->Uris->ChildNodes->Node;
1538 if (is_array($child_nodes)) {
1539 foreach ($child_nodes as $child_node) {
1540 $this->get_smugmug_folders($objects, $child_node);
1541 }
1542 }
1543 }
1544 $objects[] = $object;
1545 }
1546 }
1547
1548 /**
1549 * A recursive call to traverse a Zenfolio group and generate a list of objects, with each object corresponding to a photoset.
1550 *
1551 * @param $objects
1552 * @param $elements
1553 * @param $display_type
1554 */
1555 private function get_zenfolio_groups(&$objects, $elements, $display_type) {
1556 if (!empty($elements->PhotoSet) && $display_type != 'group') {
1557 foreach ($elements->PhotoSet as $photoset) {
1558 if ((($display_type == 'gallery-photo' || $display_type == 'multi-gallery') && $photoset->Type == 'Gallery') ||
1559 (($display_type == 'collection-photo' || $display_type == 'multi-collection') && $photoset->Type == 'Collection') ||
1560 $display_type == 'multi-gallery-collection' || $display_type == 'group-hierarchy' || $display_type == 'single-photo'
1561 ) {
1562 $object = array();
1563 $page_url = parse_url($photoset->PageUrl);
1564 $page_url = $page_url['path'];
1565 $page_url = explode('/', $page_url);
1566 if (count($page_url) > 1) {
1567 $page_url = $page_url[1];
1568 }
1569
1570 if (!is_array($page_url) && preg_match('/^p\d{9}/', $page_url) === 0) {
1571 $page_url = array();
1572 }
1573
1574 $object['id'] = !is_array($page_url) ? $page_url : $photoset->Id;
1575 $object['alt_id'] = $photoset->Id;
1576 $object['title'] = esc_attr($photoset->Title);
1577 $object['counters'] = array(esc_html(sprintf(_n('%s media item', '%s media items', $photoset->PhotoCount, 'photonic'), $photoset->PhotoCount)));
1578
1579 $photo = $photoset->TitlePhoto;
1580 $object['thumbnail'] = 'https://' . $photo->UrlHost . $photo->UrlCore . '-1.jpg';
1581
1582 if (!empty($photoset->AccessDescriptor) && !empty($photoset->AccessDescriptor->AccessType) && $photoset->AccessDescriptor->AccessType == 'Password') {
1583 $object['passworded'] = 1;
1584 }
1585
1586 $objects[] = $object;
1587 }
1588 }
1589 }
1590
1591 if (!empty($elements->Group)) {
1592 foreach ($elements->Group as $group) {
1593 if ($display_type == 'group' || $display_type == 'group-hierarchy') {
1594 $object = array();
1595 $object['id'] = $group->Id;
1596 $object['title'] = $group->Title;
1597 $object['thumbnail'] = trailingslashit(PHOTONIC_URL) . 'include/images/placeholder-Ti.png';
1598 $objects[] = $object;
1599 }
1600
1601 if ($display_type != 'group-hierarchy' && !empty($group->Elements)) {
1602 $this->get_zenfolio_groups($objects, $group->Elements, $display_type);
1603 }
1604 }
1605 }
1606 }
1607
1608 /**
1609 * A wrapper function that invokes the individual <code>process_response</code> calls by provider.
1610 * - If the call is successful the results are passed as a thumbnail grid in an array with a "success" key
1611 * - If the call is unsuccessful an error message is passed in an array with an "error" key
1612 *
1613 * @param $response
1614 * @param $provider
1615 * @param null $display_type
1616 * @param array $form_parameters
1617 * @param array $existing
1618 * @param null $url
1619 * @param bool $more
1620 * @return array
1621 */
1622 function process_response($response, $provider, $display_type = null, $form_parameters = array(), $existing = array(), $url = null, $more = false) {
1623 if (!is_wp_error($response)) {
1624 if (isset($response['response']) && isset($response['response']['code'])) {
1625 if ($response['response']['code'] == 200) {
1626 $pagination = array();
1627 if ($provider == 'flickr') {
1628 $objects = $this->process_flickr_response($response, $display_type, $url, $pagination);
1629 }
1630 else if ($provider == 'smugmug') {
1631 $objects = $this->process_smugmug_response($response, $display_type, $url, $pagination);
1632 }
1633 else if ($provider == 'google') {
1634 $objects = $this->process_google_response($response, $url, $pagination);
1635 }
1636 else if ($provider == 'zenfolio') {
1637 $objects = $this->process_zenfolio_response($response, $display_type);
1638 }
1639 else if ($provider == 'instagram') {
1640 $objects = $this->process_instagram_response($response, $display_type, $url, $pagination);
1641 }
1642
1643 if (empty($objects)) {
1644 return array('error' => $this->error_no_data_returned);
1645 }
1646 else if (!empty($objects['error']) || !empty($objects['success'])) {
1647 // Happens for "Find user" kind of calls
1648 return $objects;
1649 }
1650
1651 $present = array();
1652 $output = $this->get_thumbnail_display($objects, $provider, $existing, $present, $more);
1653
1654 $selected_data = empty($existing['selected_data']) ? array() : explode(',', $existing['selected_data']);
1655 $missing = array_diff($selected_data, $present);
1656 if (!empty($missing)) {
1657 //
1658 }
1659
1660 if (!empty($pagination['url'])) {
1661 $data_display_type = empty($display_type) ? '' : $display_type;
1662 $output .= "<div class='photonic-more-wrapper'>\n".
1663 "\t<a href='#' class='photonic-flow-more' data-photonic-more-link='{$pagination['url']}' data-photonic-display-type='$data_display_type' data-photonic-provider='$provider'>".esc_html__('Load More', 'photonic')."</a>\n".
1664 "</div>";
1665 }
1666
1667 if (!$more) {
1668 foreach ($form_parameters as $id => $value) {
1669 $output .= "<input type='hidden' name='$id' value='" . esc_attr($value) . "' />\n";
1670 }
1671 }
1672 return array('success' => $output);
1673 }
1674 else {
1675 Photonic::log($response['response']);
1676 return array('error' => sprintf(esc_html__('No data returned. Error code %s', 'photonic'), $response['response']['code']));
1677 }
1678 }
1679 else {
1680 Photonic::log($response);
1681 return array('error' => esc_html__('No data returned. Empty response, or empty error code.', 'photonic'));
1682 }
1683 }
1684 else {
1685 return array('error' => $response->get_error_message());
1686 }
1687 }
1688
1689 /**
1690 * Processes a response from Flickr to build it out into a gallery of thumbnails. Flickr has L1, L2 and L3 displays in the flow.
1691 *
1692 * @param $response
1693 * @param $display_type
1694 * @param null $url
1695 * @param array $pagination
1696 * @return array
1697 */
1698 private function process_flickr_response($response, $display_type, $url = null, &$pagination = array()) {
1699 $objects = array();
1700 $body = json_decode(wp_remote_retrieve_body($response));
1701
1702 if (isset($body->stat) && $body->stat == 'fail') {
1703 Photonic::log($response);
1704 return array('error' => $body->message);
1705 }
1706
1707 if (isset($body->photosets) && isset($body->photosets->photoset)) {
1708 // $page = (int)($body->photosets->page); // Not needed because apparently the photosets.getList call returns all photosets for now
1709 // $pages = (int)($body->photosets->pages); // Not needed because apparently the photosets.getList call returns all photosets for now
1710 $photosets = $body->photosets->photoset;
1711 foreach ($photosets as $flickr_object) {
1712 $object = array();
1713 $object['id'] = $flickr_object->id;
1714 $object['title'] = esc_attr($flickr_object->title->_content);
1715 $object['counters'] = array();
1716 if (!empty($flickr_object->photos)) $object['counters'][] = esc_html(sprintf(_n('%s photo', '%s photos', $flickr_object->photos, 'photonic'), $flickr_object->photos));
1717 if (!empty($flickr_object->videos)) $object['counters'][] = esc_html(sprintf(_n('%s video', '%s videos', $flickr_object->videos, 'photonic'), $flickr_object->videos));
1718 $object['thumbnail'] = "https://farm" . $flickr_object->farm . ".static.flickr.com/" . $flickr_object->server . "/" . $flickr_object->primary . "_" . $flickr_object->secret . "_q.jpg";
1719 $objects[] = $object;
1720 }
1721 }
1722 else if (isset($body->galleries) && isset($body->galleries->gallery)) {
1723 $page = (int)($body->galleries->page);
1724 $pages = (int)($body->galleries->pages);
1725 $galleries = $body->galleries->gallery;
1726 foreach ($galleries as $flickr_object) {
1727 $object = array();
1728 $object['id'] = $flickr_object->id;
1729 $object['title'] = esc_attr($flickr_object->title->_content);
1730 $object['counters'] = array();
1731 if (!empty($flickr_object->count_photos)) $object['counters'][] = esc_html(sprintf(_n('%s photo', '%s photos', $flickr_object->count_photos, 'photonic'), $flickr_object->count_photos));
1732 if (!empty($flickr_object->count_videos)) $object['counters'][] = esc_html(sprintf(_n('%s video', '%s videos', $flickr_object->count_videos, 'photonic'), $flickr_object->count_videos));
1733 $object['thumbnail'] = "https://farm" . $flickr_object->primary_photo_farm . ".static.flickr.com/" . $flickr_object->primary_photo_server . "/" . $flickr_object->primary_photo_id . "_" . $flickr_object->primary_photo_secret . "_q.jpg";
1734 $objects[] = $object;
1735 }
1736 }
1737 else if (isset($body->photos) && isset($body->photos->photo)) {
1738 if ($display_type == 'single-photo') {
1739 $page = (int)($body->photos->page);
1740 $pages = (int)($body->photos->pages);
1741 if ($page < $pages && !empty($url)) {
1742 $url = remove_query_arg('page', $url);
1743 $pagination['url'] = add_query_arg(array('page' => $page + 1), $url);
1744 }
1745 }
1746 $photos = $body->photos->photo;
1747 foreach ($photos as $flickr_object) {
1748 $object = array();
1749 $object['id'] = $flickr_object->id;
1750 $object['title'] = esc_attr($flickr_object->title);
1751 $object['thumbnail'] = 'https://farm' . $flickr_object->farm . '.static.flickr.com/' . $flickr_object->server . '/' . $flickr_object->id . '_' . $flickr_object->secret . '_q.jpg';
1752 $objects[] = $object;
1753 }
1754 }
1755 else if (isset($body->collections) && isset($body->collections->collection)) {
1756 $collections = $body->collections->collection;
1757 foreach ($collections as $flickr_object) {
1758 $object = array();
1759 $object['id'] = $flickr_object->id;
1760 $object['title'] = esc_attr($flickr_object->title);
1761 $object['counters'] = array();
1762 if (!empty($flickr_object->set)) $object['counters'][] = esc_html(sprintf(_n('%s album', '%s albums', count($flickr_object->set), 'photonic'), count($flickr_object->set)));
1763 $object['thumbnail'] = $flickr_object->iconlarge;
1764 $objects[] = $object;
1765 }
1766 }
1767 else if (isset($body->user)) {
1768 return array('success' => array('user_id' => $body->user->id));
1769 }
1770 else if (isset($body->group)) {
1771 return array('success' => array('group_id' => $body->group->id));
1772 }
1773
1774 if (!empty($page) && !empty($pages) && $page < $pages && !empty($url)) {
1775 $url = remove_query_arg('page', $url);
1776 $pagination['url'] = add_query_arg(array('page' => $page + 1), $url);
1777 }
1778 return $objects;
1779 }
1780
1781 /**
1782 * Processes a response from SmugMug to build it out into a gallery of thumbnails. SmugMug has L1, L2 and L3 displays in the flow.
1783 *
1784 * @param $response
1785 * @param $display_type
1786 * @param null $url
1787 * @param array $pagination
1788 * @return array
1789 */
1790 private function process_smugmug_response($response, $display_type, $url = null, &$pagination = array()) {
1791 $objects = array();
1792 $body = json_decode(wp_remote_retrieve_body($response));
1793 if (isset($body->Response) && isset($body->Response->Album)) {
1794 $albums = $body->Response->Album;
1795 foreach ($albums as $album) {
1796 $object = array();
1797 if (isset($album->AlbumKey)) {
1798 $object['id'] = $album->AlbumKey;
1799 }
1800 else {
1801 $uri = $album->Uris->Album->Uri;
1802 $uri = explode('/', $uri);
1803 $object['id'] = $uri[count($uri) - 1];
1804 }
1805 $object['title'] = !empty($album->Name) ? esc_attr($album->Name) : '';
1806 if (isset($album->ImageCount)) {
1807 $object['counters'] = array(esc_html(sprintf(_n('%s media item', '%s media items', $album->ImageCount, 'photonic'), $album->ImageCount)));
1808 }
1809
1810 $highlight = $album->Uris->HighlightImage;
1811 if (isset($highlight->Image)) {
1812 $thumbURL = $highlight->Image->Uris->ImageSizes->ImageSizes->ThumbImageUrl;
1813 }
1814 else {
1815 $thumbURL = trailingslashit(PHOTONIC_URL) . 'include/images/placeholder-Th.png';
1816 }
1817
1818 $object['thumbnail'] = $thumbURL;
1819 if (isset($album->SecurityType) && $album->SecurityType == 'Password') {
1820 $object['passworded'] = 1;
1821 }
1822
1823 $objects[] = $object;
1824 }
1825
1826 if (isset($body->Response->Pages)) {
1827 $pages = $body->Response->Pages;
1828 if ($pages->Start + $pages->Count - 1 < $pages->Total) {
1829 $pagination['url'] = add_query_arg(array('start' => $pages->Start + $pages->Count), remove_query_arg(array('start'), $url));
1830 }
1831 }
1832 }
1833 else if (isset($body->Response) && isset($body->Response->Node)) {
1834 $nodes = $body->Response->Node;
1835 foreach ($nodes as $node) {
1836 if ($display_type == 'folder' && $node->Type == 'Album') {
1837 continue;
1838 }
1839 else if ($display_type == 'folder') {
1840 $this->get_smugmug_folders($objects, $node);
1841 }
1842 else {
1843 $object = array();
1844 if ($node->Type == 'Album') {
1845 $uri = $node->Uris->Album->Uri;
1846 $uri = explode('/', $uri);
1847 $uri = $uri[count($uri) - 1];
1848 $object['id'] = $uri;
1849 }
1850 else if ($node->Type == 'Folder') {
1851 $object['id'] = $node->NodeID;
1852 }
1853 $object['title'] = !empty($node->Name) ? esc_attr($node->Name) : '';
1854 if ($display_type == 'tree') {
1855 $object['title'] .= " ({$node->Type})";
1856 }
1857 if (isset($node->Uris->NodeCoverImage->Image->Uris->ImageSizes->ImageSizes->ThumbImageUrl)) {
1858 $object['thumbnail'] = $node->Uris->NodeCoverImage->Image->Uris->ImageSizes->ImageSizes->ThumbImageUrl;
1859 }
1860 else {
1861 $object['thumbnail'] = trailingslashit(PHOTONIC_URL) . 'include/images/placeholder-Th.png';
1862 }
1863 $objects[] = $object;
1864 }
1865 }
1866 }
1867 else if (isset($body->Response) && isset($body->Response->User)) {
1868 $user = $body->Response->User;
1869 return array('success' => $user->Uris->Node->Uri);
1870 }
1871 else {
1872 Photonic::log($body);
1873 return array('error' => $this->error_not_found);
1874 }
1875
1876 return $objects;
1877 }
1878
1879 /**
1880 * Processes a response from Google to build it out into a gallery of thumbnails. Google has both, L1 and L2 displays in the flow.
1881 *
1882 * @param $response
1883 * @param $url
1884 * @param array $pagination
1885 * @return array
1886 */
1887 function process_google_response($response, $url, &$pagination = array()) {
1888 $objects = array();
1889 $body = json_decode(wp_remote_retrieve_body($response));
1890 if (isset($body->albums)) {
1891 $albums = $body->albums;
1892 foreach ($albums as $album) {
1893 $object = array();
1894 $object['id'] = $album->id;
1895 $object['title'] = !empty($album->title) ? esc_attr($album->title) : '';
1896 $object['counters'] = array(esc_html(sprintf(_n('%s media item', '%s media items', $album->mediaItemsCount, 'photonic'), $album->mediaItemsCount)));
1897 $object['thumbnail'] = $album->coverPhotoBaseUrl . "=w150-h150-c";
1898 $objects[] = $object;
1899 }
1900 if (!empty($body->nextPageToken)) {
1901 $pagination['url'] = add_query_arg(array('pageToken' => $body->nextPageToken), remove_query_arg(array('pageToken'), $url));
1902 }
1903 }
1904 else if (isset($body->mediaItems)) {
1905 $photos = $body->mediaItems;
1906 foreach ($photos as $photo) {
1907 $object = array();
1908 $object['id'] = $photo->id;
1909 $object['title'] = !empty($photo->description) ? esc_attr($photo->description) : '';
1910 $object['thumbnail'] = $photo->baseUrl . "=w150-h150-c";
1911 $objects[] = $object;
1912 }
1913 }
1914 else if (isset($body->error)) {
1915 $objects['error'] = $body->error->message;
1916 }
1917 return $objects;
1918 }
1919
1920 /**
1921 * Processes a response from Zenfolio to build it out into a gallery of thumbnails. Zenfolio has both, L1 and L2 displays in the flow.
1922 *
1923 * @param $response
1924 * @param $display_type
1925 * @return array
1926 */
1927 function process_zenfolio_response($response, $display_type) {
1928 $body = wp_remote_retrieve_body($response);
1929 $body = preg_replace('/"Id":(\d+)/', '"Id":"$1"', $body);
1930 $body = simplexml_load_string($body);
1931 $objects = array();
1932 if ($display_type != 'multi-photo') {
1933 if (!empty($body->Elements)) {
1934 $elements = $body->Elements;
1935 $this->get_zenfolio_groups($objects, $elements, $display_type);
1936 }
1937 else if (!empty($body->PhotoSets)) {
1938 $photosets = $body->PhotoSets;
1939 $this->get_zenfolio_groups($objects, $photosets, $display_type);
1940 }
1941
1942 if ($display_type == 'single-photo') {
1943 $photo_array = array();
1944 global $photonic_zenfolio_gallery;
1945 if (!isset($photonic_zenfolio_gallery)) {
1946 $photonic_zenfolio_gallery = new Photonic_Zenfolio_Processor();
1947 }
1948 $requests = array();
1949 foreach ($objects as $object) {
1950 $parameters = array();
1951 $parameters['photoSetId'] = substr($object['id'], 1);
1952 $parameters['level'] = 'Level1';
1953 $parameters['includePhotos'] = 'true';
1954
1955 $request = $photonic_zenfolio_gallery->prepare_request('LoadPhotoSet', $parameters);
1956 $requests[] = array(
1957 'url' => 'https://api.zenfolio.com/api/1.8/zfapi.asmx',
1958 'type' => 'POST',
1959 'headers' => $request['headers'],
1960 'data' => $request['body'],
1961 );
1962 }
1963 $responses = Requests::request_multiple($requests);
1964 if (!empty($responses)) {
1965 foreach ($responses as $ps_response) {
1966 if (is_a($ps_response, 'Requests_Response')) {
1967 $ps_response = json_decode($ps_response->body);
1968 if (!empty($ps_response->result)) {
1969 $ps_response = $ps_response->result;
1970 if (!empty($ps_response->Photos)) {
1971 $photo_array['psid'.$ps_response->Id] = $ps_response->Title;
1972 foreach ($ps_response->Photos as $ps_photo) {
1973 if (array_key_exists($ps_photo->Id, $photo_array)) {
1974 continue;
1975 }
1976 $photo = array();
1977 $photo['id'] = $ps_photo->Id;
1978 $url_parts = explode('/', $ps_photo->UrlCore);
1979 $photo['alt_id'] = 'h'.dechex((int)substr($url_parts[count($url_parts) - 1], 1));
1980 $photo['alt_id2'] = $url_parts[count($url_parts) - 1];
1981 $photo['title'] = esc_attr($ps_photo->Title);
1982 $photo['thumbnail'] = 'https://' . $ps_photo->UrlHost . $ps_photo->UrlCore . '-1.jpg';
1983 $photo_array[$ps_photo->Id] = $photo;
1984 }
1985 }
1986 }
1987 }
1988 }
1989 }
1990 $objects = array_values($photo_array);
1991 }
1992 return $objects;
1993 }
1994 else {
1995 $photos = $body->Photos;
1996 foreach ($photos->Photo as $photo) {
1997 $object = array();
1998 $object['id'] = $photo->Id;
1999 $object['title'] = esc_attr($photo->Title);
2000 $object['thumbnail'] = 'https://' . $photo->UrlHost . $photo->UrlCore . '-1.jpg';
2001 $objects[] = $object;
2002 }
2003 return $objects;
2004 }
2005 }
2006
2007 /**
2008 * Processes a response from Instagram to build it out into a gallery of thumbnails. Instagram only has L1 displays.
2009 *
2010 * @param $response
2011 * @param $display_type
2012 * @param $url
2013 * @param array $pagination
2014 * @return array
2015 */
2016 function process_instagram_response($response, $display_type, $url, &$pagination = array()) {
2017 $objects = array();
2018 $body = json_decode($response['body']);
2019 if (isset($body->data)) {
2020 $data = $body->data;
2021 foreach ($data as $photo) {
2022 if (isset($photo->type) && ($photo->type == 'image' || $photo->type == 'carousel' || $photo->type == 'video') && isset($photo->images)) {
2023 $object = array();
2024 $link = $photo->link;
2025 $link = explode('/', $link);
2026 if (empty($link[count($link) - 1])) {
2027 $link = $link[count($link) - 2];
2028 }
2029 else {
2030 $link = $link[count($link) - 1];
2031 }
2032 $object['id'] = $link;
2033 if (isset($photo->caption) && isset($photo->caption->text)) {
2034 $object['title'] = esc_attr($photo->caption->text);
2035 }
2036 else {
2037 $object['title'] = '';
2038 }
2039 $object['thumbnail'] = $photo->images->thumbnail->url;
2040 $objects[] = $object;
2041 }
2042 }
2043 if (isset($body->pagination) && isset($body->pagination->next_max_id) && $display_type == 'single-photo') {
2044 $pagination['url'] = add_query_arg(array('max_id' => $body->pagination->next_max_id), remove_query_arg(array('max_id'), $url));
2045 }
2046 }
2047 return $objects;
2048 }
2049 }
2050