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

2,066 lines 85.1 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 if ($display_type == 'folder' || $display_type == 'tree' || $display_type == 'folder-photo' || $display_type == 'user-photo') {
391 // Not able to use wp_remote_request - getting a signature_invalid response
392 $temp = Photonic::http($call, 'GET', $args, null, 300);
393 $temp = $this->process_response($temp, 'smugmug', 'user');
394
395 if (!empty($temp['success'])) {
396 $node = $temp['success'];
397 $call = 'https://api.smugmug.com' . $node . '!children';
398 if ($display_type == 'tree' || $display_type == 'user-photo') {
399 $args['_expand'] = 'NodeCoverImage.ImageSizes';
400 }
401 else {
402 $config = $photonic_smugmug_gallery->get_config_object(500);
403 $config_str = json_encode($config);
404 $args['_config'] = $config_str;
405 }
406
407 // Sign call again because we made an interim call
408 if (!empty($photonic_smug_access_token)) {
409 unset($args['oauth_consumer_key']);
410 unset($args['oauth_nonce']);
411 unset($args['oauth_signature']);
412 unset($args['oauth_signature_method']);
413 unset($args['oauth_timestamp']);
414 unset($args['oauth_version']);
415 unset($args['oauth_token']);
416 unset($args['oauth_verifier']);
417 $args = $photonic_smugmug_gallery->sign_call($call, 'GET', $args);
418 }
419 }
420 else {
421 return $temp;
422 }
423 }
424
425 $response = Photonic::http($call, 'GET', $args, null, 300);
426 return $this->process_response($response, 'smugmug', $display_type, array('nick_name' => $user), $existing, $call);
427
428 case 'google':
429 global $photonic_google_refresh_token, $photonic_google_gallery;
430 if (!isset($photonic_google_gallery)) {
431 $photonic_google_gallery = new Photonic_Google_Photos_Processor();
432 }
433
434 $photonic_google_gallery->perform_back_end_authentication($photonic_google_refresh_token);
435
436 $parameters = array();
437
438 // All OK so far. Let's try to get the data for the next screen
439 $parameters['access_token'] = $photonic_google_gallery->access_token;
440
441 $query_url = '';
442 if ($display_type == 'multi-photo') {
443 $query_url = 'https://photoslibrary.googleapis.com/v1/mediaItems:search';
444 }
445 else if ($display_type == 'multi-album' || $display_type == 'album-photo') {
446 $query_url = 'https://photoslibrary.googleapis.com/v1/albums?pageSize=50';
447 }
448
449 $query_url = add_query_arg($parameters, $query_url);
450 $response = wp_remote_request($query_url, array('method' => ($display_type == 'multi-photo' ? 'POST' : 'GET'), 'sslverify' => PHOTONIC_SSL_VERIFY));
451 return $this->process_response($response, 'google', $display_type, array(), $existing, $query_url);
452
453 case 'zenfolio':
454 if ((!in_array($display_type, array('multi-photo', /*'multi-gallery', 'multi-collection'*/)) && $for == 'any') ||
455 (!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')))) {
456 $err = esc_html__('Incompatible selections:', 'photonic') . "<br/>\n";
457 $err .= $flattened_fields['display_type']['desc'] . ": " . $flattened_fields['display_type']['options'][$display_type] . "<br/>\n";
458 $err .= $flattened_fields['for']['desc'] . ": " . $flattened_fields['for']['options'][$for] . "<br/>\n";
459 return array('error' => $err);
460 }
461
462 if ($for == 'other' && empty($_POST['login_name'])) {
463 return array('error' => $this->error_mandatory);
464 }
465
466 $login_name = sanitize_text_field($_POST['login_name']);
467 global $photonic_zenfolio_default_user;
468 if ($for == 'current' && empty($photonic_zenfolio_default_user)) {
469 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/>'));
470 }
471
472 $parameters = array();
473 $user = $for == 'current' ? $photonic_zenfolio_default_user : ($for == 'other' ? $login_name : '');
474
475 if ($display_type == 'multi-photo') {
476 $url = 'https://api.zenfolio.com/api/1.8/zfapi.asmx/SearchPhotoByCategory';
477 $parameters['searchId'] = '5';
478 $parameters['sortOrder'] = 'Popularity';
479 $parameters['categoryCode'] = '1018000';
480 $parameters['offset'] = 0;
481 $parameters['limit'] = 500;
482 }
483 else if (in_array($display_type, array('multi-gallery', 'multi-collection')) && $for == 'any') {
484 $url = 'https://api.zenfolio.com/api/1.8/zfapi.asmx/SearchSetByCategory';
485 $parameters['searchId'] = '5';
486 $parameters['sortOrder'] = 'Popularity';
487 $parameters['categoryCode'] = '1018000';
488 $parameters['offset'] = 0;
489 $parameters['limit'] = 500;
490 }
491 else {
492 $url = 'https://api.zenfolio.com/api/1.8/zfapi.asmx/LoadGroupHierarchy';
493 $parameters['loginName'] = $user;
494 }
495
496 $response = wp_remote_request($url, array('sslverify' => PHOTONIC_SSL_VERIFY, 'body' => $parameters));
497 return $this->process_response($response, 'zenfolio', $display_type, array('login_name' => $user), $existing, $url);
498
499 case 'instagram':
500 global $photonic_instagram_access_token;
501 $base_url = 'https://api.instagram.com/v1/users/self/media/recent/?access_token='.$photonic_instagram_access_token;
502 $response = wp_remote_request($base_url);
503 return $this->process_response($response, 'instagram', $display_type, array(), $existing, $base_url);
504
505 default: // wp
506 $this->force_next_screen = 4;
507 $this->force_previous_screen = 2;
508 return array('success' => $this->get_layout_selector($display_type, $existing));
509 }
510 }
511 else if ($screen == 3) {
512 //Check for display_type
513 $screen_fields = $this->field_list['screen-' . $screen];
514 $provider_fields = $screen_fields[$provider];
515 $fields = $provider_fields[$display_type]['display'];
516 foreach ($fields as $id => $field) {
517 $checks = $this->do_basic_option_check($id, $field, true);
518 if (!empty($checks)) {
519 return $checks;
520 }
521
522 if ($id == 'selection' && sanitize_text_field($_POST['selection']) == 'selected' && empty($_POST['selected_data'])) {
523 return array('error' => esc_html__('Please select what you want to show.', 'photonic'));
524 }
525
526 if (in_array($display_type, array('single-photo', 'album-photo', 'gallery-photo', 'folder-photo', 'collection-photo')) && empty($_POST['selected_data'])) {
527 return array('error' => esc_html__('Please select what you want to show.', 'photonic'));
528 }
529 }
530
531 // All OK? Get next screen
532 if ($display_type != 'single-photo') {
533 $output = $this->get_layout_selector($display_type, $existing);
534 }
535 else {
536 $this->force_next_screen = 6;
537 $this->force_previous_screen = 3;
538 $output = $this->construct_shortcode();
539 }
540 return array('success' => $output);
541 }
542 else if ($screen == 4) {
543 $layout = isset($_POST['layout']) ? sanitize_text_field($_POST['layout']) : '';
544 if (empty($layout)) {
545 return array('error' => $this->error_mandatory);
546 }
547 $layout_options = $this->flow_fields->get_layout_options();
548 if (!array_key_exists($layout, $layout_options)) {
549 return array('error' => sprintf(esc_html__('Invalid layout: %s', 'photonic'), $layout));
550 }
551
552 // All good. Next screen:
553 return array('success' => $this->get_layout_options($provider, $display_type, $layout, $existing));
554 }
555 else {
556 $passworded = isset($_POST['selection_passworded']) ? sanitize_text_field($_POST['selection_passworded']) : '';
557 $password = isset($_POST['password']) ? sanitize_text_field($_POST['password']) : '';
558 if (!empty($passworded) && empty($password)) {
559 return array('error' => $this->error_mandatory);
560 }
561 return array('success' => $this->construct_shortcode());
562 }
563 return '';
564 }
565
566 /**
567 * Utility method that takes an array of fields, determines if each member is a "field list" or a "field". If it is a field
568 * list it processes it as a field list, otherwise it processes it as a field.
569 *
570 * @param array $fields
571 * @param array $existing
572 * @return String
573 */
574 private function render_all_fields($fields, $existing = array()) {
575 $output = '';
576 foreach ($fields as $id => $field) {
577 if (!empty($field['type']) && $field['type'] == 'field_list') {
578 $output .= $this->process_field_list($id, $field, $existing);
579 }
580 else if (!empty($field['type'])) {
581 $output .= $this->process_field($id, $field, 0, null, $existing);
582 }
583 }
584 return $output;
585 }
586
587 /**
588 * Takes a "field list" and processes each field in it individually. A "field list" can contain an interdependent sequence
589 * of fields, in which case each member gets a sequential number assigned to it. The actual logic of sequencing is handled on the
590 * front-end.
591 * E.g. If, for Flickr, you select "Multiple Photos", then "Another User", the front-end will show a "User" text field.
592 * But if "Group" is selected, it shows a "Group" text field
593 *
594 * @param $field_list_name
595 * @param array $field_list
596 * @param array $existing
597 * @return string
598 */
599 function process_field_list($field_list_name, $field_list, $existing = array()) {
600 if (!is_array($field_list) || empty($field_list['type']) || $field_list['type'] != 'field_list' || empty($field_list['list'])) {
601 return '';
602 }
603 else {
604 $ret = '';
605 $counter = 0;
606 $sequence_group = null;
607 foreach ($field_list['list'] as $id => $field) {
608 if ($field_list['list_type'] == 'sequence') {
609 $counter++;
610 $sequence_group = $field_list_name;
611 }
612 $ret .= $this->process_field($id, $field, $counter, $sequence_group, $existing);
613 }
614 return $ret;
615 }
616 }
617
618 /**
619 * Main code to render an input element on the flow-screen. Almost all types of inputs have switches for display here.
620 *
621 * @param string $id
622 * @param array $field
623 * @param $sequence
624 * @param null $sequence_group
625 * @param array $existing
626 * @return string
627 */
628 function process_field($id, $field, $sequence, $sequence_group = null, $existing = array()) {
629 if (!is_array($field) || empty($field['type'])) {
630 return '';
631 }
632
633 if (!empty($field['post_condition']) && is_array($field['post_condition'])) {
634 foreach ($field['post_condition'] as $var => $permitted_values) {
635 $pass = false;
636 foreach ($permitted_values as $permitted) {
637 if (isset($_POST[$var]) && $_POST[$var] === $permitted) {
638 $pass = true;
639 break;
640 }
641 }
642 if (!$pass) {
643 // Variable has not been set in a different screen. Hide this field.
644 return '';
645 }
646 }
647 }
648
649 $req = empty($field['req']) ? '' : '<span class="photonic-required"><abbr title="'.esc_html__('Required', 'photonic').'">*</abbr></span>';
650 $default = !empty($existing[$id]) ? $existing[$id] : (isset($field['std']) ? $field['std'] : '');
651 $hint = '';
652 $hint_in = '';
653 if (!empty($field['hint'])) {
654 $hint = "<div class='photonic-flow-hint' role='tooltip' id='{$id}-hint'>{$field['hint']}</div>\n";
655 $hint_in = "aria-describedby='{$id}-hint'";
656 }
657
658 switch ($field['type']) {
659 case 'text':
660 $ret = "<label class='photonic-flow-option-name'>{$field['desc']}$req<input type='text' name='{$id}' value='" . $default . "' $hint_in/>$hint</label>";
661 break;
662
663 case 'radio':
664 $ret = !empty($field['desc']) ? '<div class="photonic-flow-option-name">' . $field['desc'] . $req . '</div>' : '';
665 foreach ($field['options'] as $option_value => $option_description) {
666 $option_condition = (empty($field['option-conditions']) || empty($field['option-conditions'][$option_value])) ? '' :
667 "data-photonic-option-condition='" . json_encode($field['option-conditions'][$option_value]) . "'";
668 $checked = checked($default, $option_value, false);
669 $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";
670 }
671 break;
672
673 case 'select':
674 $ret = "<label class='photonic-flow-option-name'>{$field['desc']}$req\n\t<select name='{$id}' $hint_in>\n";
675 foreach ($field['options'] as $option_value => $option_description) {
676 $option_condition = (empty($field['option-conditions']) || empty($field['option-conditions'][$option_value])) ? '' :
677 "data-photonic-option-condition='" . json_encode($field['option-conditions'][$option_value]) . "'";
678 $selected = selected($default, $option_value, false);
679 $ret .= "\t\t<option value='$option_value' $selected $option_condition>" . esc_attr($option_description) . "</option>\n";
680 }
681 $ret .= "\t</select>\n$hint</label>\n";
682 break;
683
684 case 'image-select':
685 $ret = '';
686 $ret .= '<div class="photonic-flow-option-name">' . $field['desc'] . '</div>';
687 if (!empty($default)) {
688 $selection = !in_array($default, $field['options']) ? array_keys($field['options'])[0] : $default;
689 }
690 else {
691 $selection = array_keys($field['options'])[0];
692 }
693 foreach ($field['options'] as $option_name => $desc) {
694 $esc_desc = esc_attr($desc);
695 $selected = ($option_name == $selection) ? 'selected' : '';
696 $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";
697 }
698 if (!empty($ret)) {
699 $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";
700 }
701 break;
702
703 case 'multi-select':
704 $ret = '';
705 $ret .= '<div class="photonic-flow-option-name">' . $field['desc'] . '</div>';
706 $selection = explode(',', $default);
707 foreach ($field['options'] as $option_value => $desc) {
708 $checked = in_array($option_value, $selection) ? 'checked' : '';
709 $ret .= "\t<label class='photonic-multi-select-item'><input type='checkbox' name='{$id}[]' value=\"$option_value\" $checked />$desc</label>\n";
710 }
711 if (!empty($ret)) {
712 $ret = "<div class='photonic-flow-multi-select-container'>\n$ret</div>\n";
713 }
714 break;
715
716 case 'date-filter':
717 $ret = '';
718 $ret .= '<div class="photonic-flow-option-name">' . $field['desc'] . '</div>';
719 $dates = !empty($default) ? explode(',', $default) : array();
720 $count = isset($field['count']) && is_numeric($field['count']) ? intval($field['count']) : 1;
721 $ret .= "<ol data-photonic-date-filter='$id' data-photonic-filter-count='$count'>\n";
722 $ctr = 0;
723 foreach ($dates as $didx => $date) {
724 $y_m_d = explode('/', $date);
725 $ret .= "\t<li>\n";
726 $ret .= "\t\t<div class='photonic-single-date'>\n";
727 for ($pidx = 0; $pidx < 3; $pidx++) {
728 $lower = strtolower($this->date_parts[$pidx]);
729 $ret .= "\t\t\t<label class='photonic-date-filter'>\n" .
730 "\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" .
731 "\t\t\t\t<div class='photonic-flow-hint' role='tooltip' id='{$id}-{$didx}_{$lower}-hint'>" . $this->date_part_hints[$pidx] . "</div>\n" .
732 "\t\t\t</label> \n";
733 }
734 $ret .= "\t\t</div>\n";
735 $ret .= "\t\t<a href='#' class='photonic-remove-date-filter' title='Remove filter'><span class=\"dashicons dashicons-no\"> </span></a>\n";
736 $ret .= "\t</li>\n";
737 $ctr++;
738 if ($ctr >= $count) {
739 break;
740 }
741 }
742 $ret .= "</ol>\n";
743 $ret .= "<input type='hidden' name='$id' value='$default'/>\n";
744 if ($ctr < $count) {
745 $ret .= "<a href='#' class='photonic-add-date-filter' data-photonic-add-date='$id'><span class=\"dashicons dashicons-plus-alt\"> </span> Add filter</a>\n";
746 }
747
748 break;
749
750 case 'date-range-filter':
751 $ret = '';
752 $ret .= '<div class="photonic-flow-option-name">' . $field['desc'] . '</div>';
753 $date_ranges = !empty($default) ? explode(',', $default) : array();
754 $count = isset($field['count']) && is_numeric($field['count']) ? intval($field['count']) : 1;
755 $ret .= "<ol data-photonic-date-range-filter='$id' data-photonic-filter-count='$count'>\n";
756 $ctr = 0;
757 foreach ($date_ranges as $date_range) {
758 $from_to = explode('-', $date_range);
759 if (count($from_to) != 2) {
760 continue;
761 }
762 $ret .= "\t<li>\n";
763 foreach ($from_to as $didx => $date) {
764 $ret .= "\t\t<div class='photonic-single-date'>\n";
765 $y_m_d = explode('/', $date);
766 $from_or_to = $didx === 0 ? 'start' : 'end';
767 for ($pidx = 0; $pidx < 3; $pidx++) {
768 $lower = strtolower($this->date_parts[$pidx]);
769 $ret .= "\t\t\t<label class='photonic-date-filter'>\n" .
770 "\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" .
771 "\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" .
772 "\t\t\t</label> \n";
773 }
774 $ret .= "\t\t</div>\n";
775 }
776 $ret .= "\t\t<a href='#' class='photonic-remove-date-range-filter' title='Remove filter'><span class=\"dashicons dashicons-no\"> </span></a>\n";
777 $ret .= "\t</li>\n";
778 $ctr++;
779 if ($ctr >= $count) {
780 break;
781 }
782 }
783 $ret .= "</ol>\n";
784 $ret .= "<input type='hidden' name='$id' value='$default'/>\n";
785 if ($ctr < $count) {
786 $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";
787 }
788
789 break;
790
791 case 'thumbnail-selector':
792 $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";
793
794 $controls = "<div class='thumb-controls'>\n";
795 if ($field['mode'] != 'none') {
796 $controls .= "<input type='text' class='search-thumbs' name='thumb-search' id='thumb-search'/>\n";
797 }
798
799 if ($field['mode'] == 'multi') {
800 $controls .= esc_html__('Mark:', 'photonic').
801 sprintf(esc_html__('%1$sAll%2$s', 'photonic'), "<a href='#' class='photonic-mark photonic-mark-all' data-photonic-mark-for='{$field['for']}'>", '</a>').'|'.
802 sprintf(esc_html__('%1$sNone%2$s', 'photonic'), "<a href='#' class='photonic-mark photonic-mark-none' data-photonic-mark-for='{$field['for']}'>", '</a>');
803 }
804 $controls .= "</div>\n";
805 $ret = $controls.$ret;
806 break;
807
808 default:
809 return '';
810 }
811
812 if (!empty($ret)) {
813 if (!empty($field['hint'])) {
814 // $ret .= "<div class='photonic-flow-hint' role='tooltip' id='{$id}-hint'><div>{$field['hint']}</div></div>\n";
815 }
816
817 $sequence_str = '';
818 if ($sequence !== 0) {
819 $sequence_str = 'data-photonic-flow-sequence="' . $sequence . '"';
820 }
821
822 $sequence_group_str = '';
823 if (!is_null($sequence_group)) {
824 $sequence_group_str = 'data-photonic-flow-sequence-group="' . $sequence_group . '"';
825 }
826
827 $condition = '';
828 if (!empty($field['conditions'])) {
829 $condition = "data-photonic-condition='" . json_encode($field['conditions']) . "'";
830 }
831
832 $ret = "<div class='photonic-flow-field' $sequence_str $condition $sequence_group_str>\n" . $ret . "</div>\n";
833 }
834 return $ret;
835 }
836
837 /**
838 * Performs basic checks against whitelists. More advanced checks are handled in the <code>validate</code> function
839 *
840 * @param $id
841 * @param $field
842 * @param bool $check_required
843 * @return array|bool
844 */
845 private function do_basic_option_check($id, $field, $check_required = false) {
846 if (empty($field['type']) || ($field['type'] != 'select' && $field['type'] != 'radio')) {
847 return false;
848 }
849
850 if ($check_required && !empty($field['req']) && (!isset($_POST[$id]) || trim($_POST[$id]) === '')) {
851 return array('error' => $this->error_mandatory);
852 }
853
854 if (isset($_POST[$id]) && !in_array(sanitize_text_field($_POST[$id]), array_keys($field['options']))) {
855 return array('error' => sprintf($this->error_not_permitted, $field['name'], $_POST[$id]));
856 }
857 return false;
858 }
859
860 /**
861 * A special type of selector not handled by the <code>process_field</code> call. Layouts are used only on one screen,
862 * and are used by almost all types of providers. This displays the available layouts as icons to pick from.
863 *
864 * @param $display_type
865 * @param array $existing
866 * @return string
867 */
868 private function get_layout_selector($display_type, $existing = array()) {
869 global $photonic_thumbnail_style;
870 $output = '';
871 $level = empty($this->display_types[$display_type]) ? -1 : $this->display_types[$display_type];
872 $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'];
873
874 if ($level > 0) {
875 $layout_options = $this->flow_fields->get_layout_options();
876 foreach ($layout_options as $layout => $desc) {
877 $selected = $layout == $layout_from_option ? 'selected' : '';
878 if (($layout == 'slideshow' && $level == 1) || $layout != 'slideshow') {
879 $esc_desc = esc_attr($desc);
880 $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";
881 }
882 }
883 if (!empty($output)) {
884 $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";
885 }
886 }
887 $output = '<h1>' . esc_html__('Pick Your Layout', 'photonic') . '</h1>' .
888 "<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".
889 $output;
890
891 if ($this->force_next_screen > -1) {
892 $output .= "\n<input type='hidden' name='force_next_screen' value='{$this->force_next_screen}'/>\n";
893 }
894 if ($this->force_previous_screen > -1) {
895 $output .= "\n<input type='hidden' name='force_previous_screen' value='{$this->force_previous_screen}'/>\n";
896 }
897
898 return $output;
899 }
900
901 /**
902 * Layouts use similar constructs such as <code>count</code> and <code>more</code> but also have differences.
903 * E.g. <code>columns</code> are not applicable to the justified grid or mosaic layouts etc. Similarly size options
904 * vary from provider to provider. So in the Photonic_Flow_Fields we have a hierarchy for this screen, by level, provider and layout
905 *
906 * @param $provider
907 * @param $display_type
908 * @param $layout
909 * @param array $existing
910 * @return string
911 */
912 private function get_layout_options($provider, $display_type, $layout, $existing = array()) {
913 // All levels, all layouts - media
914 // L1, L2 All layouts - title position
915 // L1 All layouts - count, more
916 // L1, L2, L3 basic lightbox layouts - # of columns, constrain by etc., thumbnail size, full size
917 // L3 Flickr - auto-expand
918 $level = $this->display_types[$display_type];
919 $output = '<h1>' . esc_html__('Configure Your Layout', 'photonic') . '</h1>';
920
921 $extract = array();
922 if (!empty($this->field_list['screen-5'][$provider]['L' . $level])) {
923 $extract = array_merge($this->field_list['screen-5'][$provider]['L' . $level], $extract);
924 }
925
926 if (!empty($this->field_list['screen-5'][$layout][$provider])) {
927 $extract = array_merge($this->field_list['screen-5'][$layout][$provider], $extract);
928 }
929
930 if (!empty($this->field_list['screen-5'][$provider])) {
931 $extract = array_merge($this->field_list['screen-5'][$provider], $extract);
932 }
933
934 if (!empty($this->field_list['screen-5']['L' . $level])) {
935 $extract = array_merge($this->field_list['screen-5']['L' . $level], $extract);
936 }
937
938 if (!empty($this->field_list['screen-5'][$layout])) {
939 $extract = array_merge($this->field_list['screen-5'][$layout], $extract);
940 }
941
942 $output .= $this->render_all_fields($extract, $existing);
943 return $output;
944 }
945
946 /**
947 * Builds out the shortcode based on inputs from all previous screens. Some attributes are passed as they are, e.g. <code>count</code>.
948 * Others are used to determine other attributes, e.g. <code>display_type</code>
949 *
950 * @return string
951 */
952 private function construct_shortcode() {
953 global $photonic_alternative_shortcode;
954
955 $provider = sanitize_text_field($_POST['provider']);
956 $display_type = sanitize_text_field($_POST['display_type']);
957
958 $short_code = array();
959 if ($provider !== 'wp') {
960 $short_code['type'] = $provider;
961 }
962
963 switch ($provider) {
964 case 'flickr':
965 if ($display_type == 'single-photo') {
966 $short_code['view'] = 'photo';
967 $short_code['photo_id'] = sanitize_text_field($_POST['selected_data']);
968 }
969 else if ($display_type == 'multi-photo') {
970 $short_code['view'] = 'photos';
971 }
972 else if ($display_type == 'album-photo') {
973 $short_code['view'] = 'photosets';
974 $short_code['photoset_id'] = sanitize_text_field($_POST['selected_data']);
975 }
976 else if ($display_type == 'gallery-photo') {
977 $short_code['view'] = 'galleries';
978 $short_code['gallery_id'] = sanitize_text_field($_POST['selected_data']);
979 }
980 else if ($display_type == 'multi-album') {
981 $short_code['view'] = 'photosets';
982 }
983 else if ($display_type == 'multi-gallery') {
984 $short_code['view'] = 'galleries';
985 }
986 else if ($display_type == 'collection') {
987 $short_code['view'] = 'collections';
988 $short_code['collection_id'] = sanitize_text_field($_POST['selected_data']);
989 }
990 else if ($display_type == 'collections') {
991 $short_code['view'] = 'collections';
992 }
993 break;
994
995 case 'smugmug':
996 if ($display_type == 'album-photo') {
997 $short_code['view'] = 'album';
998 $short_code['album'] = sanitize_text_field($_POST['selected_data']);
999 }
1000 else if ($display_type == 'folder-photo') {
1001 $short_code['view'] = 'images';
1002 $short_code['folder'] = sanitize_text_field($_POST['selected_data']);
1003 }
1004 else if ($display_type == 'user-photo') {
1005 $short_code['view'] = 'images';
1006 }
1007 else if ($display_type == 'multi-album') {
1008 $short_code['view'] = 'albums';
1009 }
1010 else if ($display_type == 'tree') {
1011 $short_code['view'] = 'tree';
1012 }
1013 else if ($display_type == 'folder') {
1014 $short_code['view'] = 'folder';
1015 $short_code['folder'] = sanitize_text_field($_POST['selected_data']);
1016 }
1017 break;
1018
1019 case 'google':
1020 if ($display_type == 'album-photo') {
1021 $short_code['view'] = 'photos';
1022 $short_code['album_id'] = sanitize_text_field($_POST['selected_data']);
1023 }
1024 else if ($display_type == 'multi-photo') {
1025 $short_code['view'] = 'photos';
1026 $date_filters = !empty($_POST['date_filters']) ? sanitize_text_field($_POST['date_filters']) : '';
1027 $date_range_filters = !empty($_POST['date_range_filters']) ? sanitize_text_field($_POST['date_range_filters']) : '';
1028 $short_code['date_filters'] = trim($date_filters.','.$date_range_filters, ',');
1029 }
1030 else if ($display_type == 'multi-album') {
1031 $short_code['view'] = 'albums';
1032 }
1033 break;
1034
1035 case 'zenfolio':
1036 if ($display_type == 'multi-photo') {
1037 $short_code['view'] = 'photos';
1038 }
1039 else if ($display_type == 'single-photo') {
1040 $short_code['view'] = 'photos';
1041 $short_code['object_id'] = sanitize_text_field($_POST['selected_data']);
1042 }
1043 else if ($display_type == 'gallery-photo'/* || $display_type == 'collection-photo'*/) {
1044 $short_code['view'] = 'photosets';
1045 $short_code['object_id'] = sanitize_text_field($_POST['selected_data']);
1046 }
1047 else if ($display_type == 'multi-gallery' || $display_type == 'multi-collection' || $display_type == 'multi-gallery-collection') {
1048 $short_code['view'] = 'photosets';
1049 if ($display_type == 'multi-gallery') {
1050 $short_code['photoset_type'] = 'Gallery';
1051 }
1052 else if ($display_type == 'multi-collection') {
1053 $short_code['photoset_type'] = 'Collection';
1054 }
1055 }
1056 else if ($display_type == 'group') {
1057 $short_code['view'] = 'group';
1058 $short_code['object_id'] = sanitize_text_field($_POST['selected_data']);
1059 }
1060 else if ($display_type == 'group-hierarchy') {
1061 $short_code['view'] = 'hierarchy';
1062 }
1063 break;
1064
1065 case 'instagram':
1066 if ($display_type == 'single-photo') {
1067 $short_code['media_id'] = sanitize_text_field($_POST['selected_data']);
1068 }
1069 break;
1070
1071 default:
1072 if ($display_type == 'current-post' && !empty($_POST['post_id'])) {
1073 $short_code['id'] = sanitize_text_field($_POST['post_id']);
1074 }
1075 else if (!empty($_POST['selected_data'])) {
1076 $short_code['ids'] = sanitize_text_field($_POST['selected_data']);
1077 }
1078 break;
1079 }
1080
1081 if (!empty($_POST['selection'])) {
1082 if ($_POST['selection'] != 'all') {
1083 $short_code['filter'] = sanitize_text_field($_POST['selected_data']);
1084 }
1085 if ($_POST['selection'] == 'not-selected') {
1086 $short_code['filter_type'] = 'exclude';
1087 }
1088 }
1089
1090 if (isset($_POST['headers']) && trim($_POST['headers'] !== '')) {
1091 if (trim($_POST['headers']) == 'none') {
1092 $short_code['headers'] = '';
1093 }
1094 else {
1095 $short_code['headers'] = sanitize_text_field($_POST['headers']);
1096 }
1097 }
1098
1099 $additional_attrs = array_merge(
1100 $this->shortcode_attributes[$provider],
1101 $this->shortcode_attributes['common']
1102 );
1103 foreach ($additional_attrs as $attr) {
1104 if (!empty($_POST[$attr]) && is_array($_POST[$attr])) {
1105 $short_code[$attr] = implode(',', $_POST[$attr]);
1106 }
1107 else if (!empty($_POST[$attr])) {
1108 $short_code[$attr] = $_POST[$attr];
1109 }
1110 }
1111
1112 if (!empty($_POST['layout'])) {
1113 $key = $provider == 'wp' ? 'style' : 'layout';
1114 if ($_POST['layout'] != 'slideshow') {
1115 $short_code[$key] = sanitize_text_field($_POST['layout']);
1116 }
1117 else {
1118 $short_code[$key] = sanitize_text_field($_POST['slideshow-style']);
1119 }
1120 }
1121 // layout
1122
1123 $raw_shortcode = !empty($_POST['photonic-editor-shortcode-raw']) ? sanitize_text_field($_POST['photonic-editor-shortcode-raw']) : '';
1124 if (!empty($raw_shortcode)) {
1125 $input = base64_decode($raw_shortcode);
1126 $input = json_decode($input);
1127 if (!empty($input->shortcode) && !empty($input->shortcode->attrs) && !empty($input->shortcode->attrs->named)) {
1128 $input = $input->shortcode->attrs->named;
1129 }
1130 }
1131 else {
1132 $raw_shortcode = !empty($_POST['photonic-editor-json']) ? stripslashes_deep($_POST['photonic-editor-json']) : '';
1133 $input = json_decode($raw_shortcode);
1134 }
1135
1136 if (!empty($input)) {
1137 $attr_array = (array)$input;
1138
1139 // If the type changes, regardless of everything else blank out the attributes
1140 if (!(empty($short_code['type']) && (empty($attr_array['type']) || in_array($attr_array['type'], array('wp', 'default')))) &&
1141 ($attr_array['type'] != $short_code['type'])) {
1142 $attr_array = array();
1143 }
1144 else {
1145 foreach ($short_code as $key => $value) {
1146 unset($attr_array[$key]);
1147 }
1148 }
1149
1150 if (!empty($this->aka_attributes[$provider])) {
1151 $aka = $this->aka_attributes[$provider];
1152 foreach ($aka as $key => $value) {
1153 if (isset($short_code[$key]) || isset($short_code[$value])) unset($attr_array[$key]);
1154 }
1155 }
1156
1157 // Others ...
1158 if (!empty($short_code['type']) && $short_code['type'] == 'instagram') {
1159 if (!empty($short_code['media_id'])) {
1160 unset($attr_array['view']);
1161 unset($attr_array['media']);
1162 }
1163 else if (!empty($short_code['media'])) {
1164 unset($attr_array['media_id']);
1165 }
1166 }
1167 $short_code = array_merge($short_code, $attr_array);
1168 }
1169
1170 if (!$this->is_gutenberg) {
1171 $output = '<h1>' . esc_html__('Your shortcode', 'photonic') . '</h1>';
1172 $output .= "<code id='photonic_shortcode'>[" . (empty($photonic_alternative_shortcode) ? 'gallery' : $photonic_alternative_shortcode).' ';
1173 $shortcode_attrs = array();
1174 foreach ($short_code as $attr => $value) {
1175 $shortcode_attrs[] = $attr . "='" . esc_attr($value) . "'";
1176 }
1177
1178 $output .= implode(' ', $shortcode_attrs);
1179 $output .= ']</code>';
1180 $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>';
1181 }
1182 else {
1183 $output = '<h1>' . esc_html__('Your Gallery', 'photonic') . '</h1>';
1184 $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>';
1185 $shortcode_attrs = array();
1186 foreach ($short_code as $attr => $value) {
1187 $shortcode_attrs[] = '<code>'.$attr . ": " . esc_attr($value).'</code>';
1188 }
1189 $output .= implode("<br/>\n", $shortcode_attrs);
1190
1191 $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>';
1192 $output .= "<code>[" . (empty($photonic_alternative_shortcode) ? 'gallery' : $photonic_alternative_shortcode).' ';
1193 $shortcode_attrs = array();
1194 foreach ($short_code as $attr => $value) {
1195 $shortcode_attrs[] = $attr . "='" . esc_attr($value) . "'";
1196 }
1197
1198 $output .= implode(' ', $shortcode_attrs);
1199 $output .= ']</code>';
1200
1201 $output .= "<input id='photonic_shortcode' name='photonic_shortcode' type='hidden' value='".esc_attr(json_encode($short_code))."' />\n";
1202 }
1203
1204 if ($this->force_next_screen > -1) {
1205 $output .= "\n<input type='hidden' name='force_next_screen' value='{$this->force_next_screen}'/>\n";
1206 }
1207 if ($this->force_previous_screen > -1) {
1208 $output .= "\n<input type='hidden' name='force_previous_screen' value='{$this->force_previous_screen}'/>\n";
1209 }
1210 return $output;
1211 }
1212
1213 /**
1214 * This is the inverse of the <code>construct_shortcode</code> method. If the Editor has a shortcode selected, this method
1215 * splits it out into the relevant screens.
1216 *
1217 * @param $input
1218 * @return array
1219 */
1220 private function deconstruct_shortcode($input) {
1221 $deconstructed = array();
1222 if (!empty($input)) {
1223 if ((!empty($input->type) && in_array($input->type, array('wp', 'default', 'flickr', 'smugmug', 'picasa', 'google', 'zenfolio', 'instagram'))) ||
1224 (empty($input->type) && !empty($input->style)) && in_array($input->style, array('square', 'circle', 'random', 'masonry', 'mosaic', 'strip-above', 'strip-below', 'strip-right', 'no-strip'))
1225 ) {
1226 $deconstructed['provider'] = !empty($input->type) ? $input->type : 'wp';
1227
1228 switch ($deconstructed['provider']) {
1229 case 'flickr':
1230 // Potential for an existing (old) shortcode to not have a user_id. E.g. Just defining photoset_id.
1231 // Rather than defaulting to the default user, we will make the user put it in this time.
1232 if (!empty($input->user_id)) {
1233 $deconstructed['for'] = 'other';
1234 $deconstructed['login'] = $input->user_id;
1235 }
1236 else if (!empty($input->group_id)) {
1237 $deconstructed['for'] = 'group';
1238 $deconstructed['group'] = $input->group_id;
1239 }
1240
1241 if (!empty($input->view)) {
1242 if ($input->view == 'collections' && empty($input->collection_id)) {
1243 $deconstructed['display_type'] = 'collections';
1244 }
1245 else if ($input->view == 'galleries' && empty($input->gallery_id)) {
1246 $deconstructed['display_type'] = 'multi-gallery';
1247 }
1248 else if ($input->view == 'photosets' && empty($input->photoset_id)) {
1249 $deconstructed['display_type'] = 'multi-album';
1250 }
1251 else if ($input->view == 'photos') {
1252 $deconstructed['display_type'] = 'multi-photo';
1253 }
1254 }
1255
1256 if (!empty($input->collection_id)) {
1257 $deconstructed['display_type'] = 'collection';
1258 $deconstructed['selected_data'] = $input->collection_id;
1259 }
1260 else if (!empty($input->gallery_id)) {
1261 $deconstructed['display_type'] = 'gallery-photo';
1262 $deconstructed['selected_data'] = $input->gallery_id;
1263 }
1264 else if (!empty($input->photoset_id)) {
1265 $deconstructed['display_type'] = 'album-photo';
1266 $deconstructed['selected_data'] = $input->photoset_id;
1267 }
1268 else if (!empty($input->photo_id)) {
1269 $deconstructed['display_type'] = 'single-photo';
1270 $deconstructed['selected_data'] = $input->photo_id;
1271 }
1272
1273 break;
1274
1275 case 'smugmug':
1276 if (!empty($input->view)) {
1277 if ($input->view == 'tree') {
1278 $deconstructed['display_type'] = 'tree';
1279 }
1280 else if ($input->view == 'folder') {
1281 $deconstructed['display_type'] = 'folder';
1282 }
1283 else if ($input->view == 'albums') {
1284 $deconstructed['display_type'] = 'multi-album';
1285 }
1286 else if ($input->view == 'images' || $input->view == 'album') {
1287 if (!empty($input->album) || !empty($input->album_key) || !empty($input->album_id)) {
1288 $deconstructed['display_type'] = 'album-photo';
1289 }
1290 else if (!empty($input->folder)) {
1291 $deconstructed['display_type'] = 'folder-photo';
1292 }
1293 else if (!empty($input->nick_name)) {
1294 $deconstructed['display_type'] = 'user-photo';
1295 }
1296 }
1297
1298 if (!empty($input->folder)) {
1299 $deconstructed['selected_data'] = trim($input->folder);
1300 }
1301 else if (!empty($input->album_key)) { // old syntax
1302 $deconstructed['selected_data'] = $input->album_key;
1303 }
1304 else if (!empty($input->album)) {
1305 $album = explode('_', $input->album);
1306 if (count($album) == 2) {
1307 $deconstructed['selected_data'] = $album[1];
1308 }
1309 else if (count($album) == 1) {
1310 $deconstructed['selected_data'] = $album[0];
1311 }
1312 }
1313
1314 if (!empty($input->nick_name)) {
1315 $deconstructed['for'] = 'other';
1316 $deconstructed['user'] = $input->nick_name;
1317 }
1318 }
1319 break;
1320
1321 case 'google':
1322 if (!empty($input->view)) {
1323 if ($input->view == 'photos') {
1324 if (!empty($input->album_id)) {
1325 $deconstructed['display_type'] = 'album-photo';
1326 $deconstructed['selected_data'] = sanitize_text_field($input->album_id);
1327 }
1328 else {
1329 $deconstructed['display_type'] = 'multi-photo';
1330 if (!empty($input->date_filters)) {
1331 $filters = sanitize_text_field($input->date_filters);
1332 $filters = explode(',', $filters);
1333 $date_filters = array();
1334 $date_range_filters = array();
1335 foreach ($filters as $filter) {
1336 $maybe_range = explode('-', $filter);
1337 if (count($maybe_range) == 2) {
1338 $date_range_filters[] = $filter;
1339 }
1340 else if (count($maybe_range) == 1) {
1341 $date_filters[] = $filter;
1342 }
1343 }
1344 if (count($date_range_filters) > 5) {
1345 $date_range_filters = array_slice($date_range_filters, 0, 5);
1346 }
1347 if (count($date_filters) > 5) {
1348 $date_filters = array_slice($date_filters, 0, 5);
1349 }
1350 $date_range_filters = implode(',', $date_range_filters);
1351 $date_filters = implode(',', $date_filters);
1352 if (!empty($date_filters)) {
1353 $deconstructed['date_filters'] = $date_filters;
1354 }
1355 if (!empty($date_range_filters)) {
1356 $deconstructed['date_range_filters'] = $date_range_filters;
1357 }
1358 }
1359 }
1360 }
1361 else if ($input->view == 'albums') {
1362 $deconstructed['display_type'] = 'multi-album';
1363 }
1364 }
1365 break;
1366
1367 case 'zenfolio':
1368 if (!empty($input->view)) {
1369 if ($input->view == 'photos' && empty($input->object_id)) {
1370 $deconstructed['display_type'] = 'multi-photo';
1371 }
1372 else if ($input->view == 'photos' && !empty($input->object_id)) {
1373 $deconstructed['display_type'] = 'single-photo';
1374 $deconstructed['selected_data'] = $input->object_id;
1375 }
1376 else if ($input->view == 'photosets') {
1377 if (!empty($input->object_id)) {
1378 $deconstructed['display_type'] = 'gallery-photo';
1379 $deconstructed['selected_data'] = $input->object_id;
1380 }
1381 else if (empty($input->photoset_type)) {
1382 $deconstructed['display_type'] = 'multi-gallery-collection';
1383 }
1384 else if (strtolower($input->photoset_type) == 'collection') {
1385 $deconstructed['display_type'] = 'multi-collection';
1386 }
1387 else if (strtolower($input->photoset_type) == 'gallery') {
1388 $deconstructed['display_type'] = 'multi-gallery';
1389 }
1390 }
1391 else if ($input->view == 'hierarchy') {
1392 $deconstructed['display_type'] = 'group-hierarchy';
1393 }
1394 else if ($input->view == 'group') {
1395 $deconstructed['display_type'] = 'group';
1396 if (!empty($input->object_id)) {
1397 $deconstructed['selected_data'] = $input->object_id;
1398 }
1399 }
1400
1401 if ($input->view == 'photosets' || $input->view == 'hierarchy' || $input->view == 'group' ||
1402 $deconstructed['display_type'] == 'single-photo') {
1403 global $photonic_zenfolio_default_user;
1404 if (!isset($input->login_name) && !empty($photonic_zenfolio_default_user)) {
1405 $deconstructed['for'] = 'current';
1406 }
1407 else if (!empty($input->login_name)) {
1408 $deconstructed['login_name'] = $input->login_name;
1409 $deconstructed['for'] = 'other';
1410 }
1411 }
1412 }
1413 break;
1414
1415 case 'instagram':
1416 if (!empty($input->media_id)) {
1417 $deconstructed['display_type'] = 'single-photo';
1418 $deconstructed['selected_data'] = $input->media_id;
1419 }
1420 else {
1421 $deconstructed['display_type'] = 'multi-photo';
1422 }
1423 break;
1424
1425 default:
1426 case 'wp':
1427 case 'default':
1428 if (empty($input->id) && empty($input->ids) && empty($input->include)) {
1429 $deconstructed['display_type'] = 'current-post';
1430 }
1431 else if (!empty($input->ids) || !empty($input->include)) {
1432 $deconstructed['display_type'] = 'multi-photo';
1433 $deconstructed['selected_data'] = !empty($input->ids) ? $input->ids : $input->include;
1434 }
1435 break;
1436 }
1437
1438 if (!empty($input->filter)) {
1439 $deconstructed['selected_data'] = $input->filter;
1440 if (empty($input->filter_type) && in_array($input->filter_type, array('include', 'exclude'))) {
1441 $deconstructed['selection'] = $input->filter_type;
1442 }
1443 else {
1444 $deconstructed['selection'] = 'selected';
1445 }
1446 }
1447 else {
1448 $deconstructed['selection'] = 'all';
1449 }
1450
1451 if (isset($this->aka_attributes[$deconstructed['provider']])) {
1452 $aka_attributes = $this->aka_attributes[$deconstructed['provider']];
1453 foreach ($aka_attributes as $attr => $aka) {
1454 if (isset($input->{$attr}) && !isset($deconstructed[$aka])) {
1455 $deconstructed[$aka] = sanitize_text_field($input->{$attr});
1456 }
1457 }
1458 }
1459
1460 $layout = empty($input->layout) ? (empty($input->style) ? '' : $input->style) : $input->layout;
1461 if (!empty($layout)) {
1462 if (in_array($layout, array('square', 'circle', 'random', 'masonry', 'mosaic',))) {
1463 $deconstructed['layout'] = $layout;
1464 }
1465 else if (in_array($layout, array('strip-above', 'strip-below', 'strip-right', 'no-strip'))) {
1466 $deconstructed['layout'] = 'slideshow';
1467 $deconstructed['slideshow-style'] = $layout;
1468 }
1469 }
1470
1471 $same_name_attrs = array_merge($this->shortcode_attributes['common'], $this->shortcode_attributes[$deconstructed['provider']]);
1472 foreach ($same_name_attrs as $attr) {
1473 if (isset($input->{$attr}) && !isset($deconstructed[$attr])) {
1474 $deconstructed[$attr] = $input->{$attr};
1475 }
1476 }
1477 }
1478 }
1479 return $deconstructed;
1480 }
1481
1482 /**
1483 * Displays an array of L1, L2 or L3 objects as a series of selectable thumbnails. Titles are deliberately not displayed because they mess
1484 * with the layout if they are too long.
1485 *
1486 * @param array $objects
1487 * @param bool $provider
1488 * @param array $existing
1489 * @param array $present
1490 * @param bool $more
1491 * @return string
1492 */
1493 function get_thumbnail_display($objects, $provider = false, $existing = array(), &$present = array(), $more = false) {
1494 $output = '';
1495 $selected_data = empty($existing['selected_data']) ? array() : explode(',', $existing['selected_data']);
1496 foreach ($objects as $object) {
1497 if (!is_array($object)) {
1498 $output .= '<h4>'.$object."</h4>\n";
1499 continue;
1500 }
1501 if (!isset($object['id'])) {
1502 continue;
1503 }
1504 $selected = '';
1505 if (in_array($object['id'], $selected_data) || (!empty($object['alt_id']) && in_array($object['alt_id'], $selected_data))
1506 || (!empty($object['alt_id2']) && in_array($object['alt_id2'], $selected_data))) {
1507 $selected = 'selected';
1508 $present[] = $object['id'];
1509 }
1510 $passworded = !empty($object['passworded']) ? 'passworded' : '';
1511
1512 $title = !empty($object['title']) ? esc_attr($object['title']) : '';
1513 $counts = !empty($object['counters']) ? ' (' . esc_attr(implode(', ', $object['counters'])) . ')' : '';
1514 $alt = !empty($object['alt_id']) ? "data-photonic-selection-alt-id='{$object['alt_id']}'" : '';
1515 $alt_2 = !empty($object['alt_id2']) ? "data-photonic-selection-alt-id-2='{$object['alt_id2']}'" : '';
1516
1517 $output .= "<div class='photonic-flow-selector $provider $selected $passworded'>\n";
1518 $output .= "\t<div class='photonic-flow-selector-inner' data-photonic-selection-id='{$object['id']}' $alt $alt_2>\n";
1519 $output .= "\t\t<img src='{$object['thumbnail']}' alt='$title$counts' title='$title$counts' />\n";
1520 $output .= "\t</div>\n";
1521 $output .= "</div>\n";
1522 }
1523 if ($more) {
1524 return $output;
1525 }
1526
1527 $output .= "<input type='hidden' name='existing_selection' id='existing_selection' value='" . (!empty($present) ? implode(',', $present) : '') . "'/>\n";
1528 return $output;
1529 }
1530
1531 /**
1532 * A recursive call to traverse a SmugMug node and generate a list of objects, with each object corresponding to a folder.
1533 * Actually a node is used instead of the folder because the folder object is deprecated by SmugMug, but nodes are only being
1534 * used for folders in Photonic.
1535 *
1536 * @param array $objects
1537 * @param $node
1538 */
1539 private function get_smugmug_folders(&$objects, $node) {
1540 $object = array();
1541 if ($node->Type == 'Folder') {
1542 $object['id'] = $node->NodeID;
1543 $object['title'] = !empty($node->Name) ? esc_attr($node->Name) : '';
1544
1545 if (isset($node->Uris->NodeCoverImage->Image->Uris->ImageSizes->ImageSizes->ThumbImageUrl)) {
1546 $object['thumbnail'] = $node->Uris->NodeCoverImage->Image->Uris->ImageSizes->ImageSizes->ThumbImageUrl;
1547 }
1548 else {
1549 $object['thumbnail'] = trailingslashit(PHOTONIC_URL) . 'include/images/placeholder-Th.png';
1550 }
1551
1552 if (isset($node->Uris->ChildNodes->Node)) {
1553 $child_nodes = $node->Uris->ChildNodes->Node;
1554 if (is_array($child_nodes)) {
1555 foreach ($child_nodes as $child_node) {
1556 $this->get_smugmug_folders($objects, $child_node);
1557 }
1558 }
1559 }
1560 $objects[] = $object;
1561 }
1562 }
1563
1564 /**
1565 * A recursive call to traverse a Zenfolio group and generate a list of objects, with each object corresponding to a photoset.
1566 *
1567 * @param $objects
1568 * @param $elements
1569 * @param $display_type
1570 */
1571 private function get_zenfolio_groups(&$objects, $elements, $display_type) {
1572 if (!empty($elements->PhotoSet) && $display_type != 'group') {
1573 foreach ($elements->PhotoSet as $photoset) {
1574 if ((($display_type == 'gallery-photo' || $display_type == 'multi-gallery') && $photoset->Type == 'Gallery') ||
1575 (($display_type == 'collection-photo' || $display_type == 'multi-collection') && $photoset->Type == 'Collection') ||
1576 $display_type == 'multi-gallery-collection' || $display_type == 'group-hierarchy' || $display_type == 'single-photo'
1577 ) {
1578 $object = array();
1579 $page_url = parse_url($photoset->PageUrl);
1580 $page_url = $page_url['path'];
1581 $page_url = explode('/', $page_url);
1582 if (count($page_url) > 1) {
1583 $page_url = $page_url[1];
1584 }
1585
1586 if (!is_array($page_url) && preg_match('/^p\d{9}/', $page_url) === 0) {
1587 $page_url = array();
1588 }
1589
1590 $object['id'] = !is_array($page_url) ? $page_url : $photoset->Id;
1591 $object['alt_id'] = $photoset->Id;
1592 $object['title'] = esc_attr($photoset->Title);
1593 $object['counters'] = array(esc_html(sprintf(_n('%s media item', '%s media items', $photoset->PhotoCount, 'photonic'), $photoset->PhotoCount)));
1594
1595 $photo = $photoset->TitlePhoto;
1596 $object['thumbnail'] = 'https://' . $photo->UrlHost . $photo->UrlCore . '-1.jpg';
1597
1598 if (!empty($photoset->AccessDescriptor) && !empty($photoset->AccessDescriptor->AccessType) && $photoset->AccessDescriptor->AccessType == 'Password') {
1599 $object['passworded'] = 1;
1600 }
1601
1602 $objects[] = $object;
1603 }
1604 }
1605 }
1606
1607 if (!empty($elements->Group)) {
1608 foreach ($elements->Group as $group) {
1609 if ($display_type == 'group' || $display_type == 'group-hierarchy') {
1610 $object = array();
1611 $object['id'] = $group->Id;
1612 $object['title'] = $group->Title;
1613 $object['thumbnail'] = trailingslashit(PHOTONIC_URL) . 'include/images/placeholder-Ti.png';
1614 $objects[] = $object;
1615 }
1616
1617 if ($display_type != 'group-hierarchy' && !empty($group->Elements)) {
1618 $this->get_zenfolio_groups($objects, $group->Elements, $display_type);
1619 }
1620 }
1621 }
1622 }
1623
1624 /**
1625 * A wrapper function that invokes the individual <code>process_response</code> calls by provider.
1626 * - If the call is successful the results are passed as a thumbnail grid in an array with a "success" key
1627 * - If the call is unsuccessful an error message is passed in an array with an "error" key
1628 *
1629 * @param $response
1630 * @param $provider
1631 * @param null $display_type
1632 * @param array $form_parameters
1633 * @param array $existing
1634 * @param null $url
1635 * @param bool $more
1636 * @return array
1637 */
1638 function process_response($response, $provider, $display_type = null, $form_parameters = array(), $existing = array(), $url = null, $more = false) {
1639 if (!is_wp_error($response)) {
1640 if (isset($response['response']) && isset($response['response']['code'])) {
1641 if ($response['response']['code'] == 200) {
1642 $pagination = array();
1643 if ($provider == 'flickr') {
1644 $objects = $this->process_flickr_response($response, $display_type, $url, $pagination);
1645 }
1646 else if ($provider == 'smugmug') {
1647 $objects = $this->process_smugmug_response($response, $display_type, $url, $pagination);
1648 }
1649 else if ($provider == 'google') {
1650 $objects = $this->process_google_response($response, $url, $pagination);
1651 }
1652 else if ($provider == 'zenfolio') {
1653 $objects = $this->process_zenfolio_response($response, $display_type);
1654 }
1655 else if ($provider == 'instagram') {
1656 $objects = $this->process_instagram_response($response, $display_type, $url, $pagination);
1657 }
1658
1659 if (empty($objects)) {
1660 return array('error' => $this->error_no_data_returned);
1661 }
1662 else if (!empty($objects['error']) || !empty($objects['success'])) {
1663 // Happens for "Find user" kind of calls
1664 return $objects;
1665 }
1666
1667 $present = array();
1668 $output = $this->get_thumbnail_display($objects, $provider, $existing, $present, $more);
1669
1670 $selected_data = empty($existing['selected_data']) ? array() : explode(',', $existing['selected_data']);
1671 $missing = array_diff($selected_data, $present);
1672 if (!empty($missing)) {
1673 //
1674 }
1675
1676 if (!empty($pagination['url'])) {
1677 $data_display_type = empty($display_type) ? '' : $display_type;
1678 $output .= "<div class='photonic-more-wrapper'>\n".
1679 "\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".
1680 "</div>";
1681 }
1682
1683 if (!$more) {
1684 foreach ($form_parameters as $id => $value) {
1685 $output .= "<input type='hidden' name='$id' value='" . esc_attr($value) . "' />\n";
1686 }
1687 }
1688 return array('success' => $output);
1689 }
1690 else {
1691 Photonic::log($response['response']);
1692 return array('error' => sprintf(esc_html__('No data returned. Error code %s', 'photonic'), $response['response']['code']));
1693 }
1694 }
1695 else {
1696 Photonic::log($response);
1697 return array('error' => esc_html__('No data returned. Empty response, or empty error code.', 'photonic'));
1698 }
1699 }
1700 else {
1701 return array('error' => $response->get_error_message());
1702 }
1703 }
1704
1705 /**
1706 * Processes a response from Flickr to build it out into a gallery of thumbnails. Flickr has L1, L2 and L3 displays in the flow.
1707 *
1708 * @param $response
1709 * @param $display_type
1710 * @param null $url
1711 * @param array $pagination
1712 * @return array
1713 */
1714 private function process_flickr_response($response, $display_type, $url = null, &$pagination = array()) {
1715 $objects = array();
1716 $body = json_decode(wp_remote_retrieve_body($response));
1717
1718 if (isset($body->stat) && $body->stat == 'fail') {
1719 Photonic::log($response);
1720 return array('error' => $body->message);
1721 }
1722
1723 if (isset($body->photosets) && isset($body->photosets->photoset)) {
1724 // $page = (int)($body->photosets->page); // Not needed because apparently the photosets.getList call returns all photosets for now
1725 // $pages = (int)($body->photosets->pages); // Not needed because apparently the photosets.getList call returns all photosets for now
1726 $photosets = $body->photosets->photoset;
1727 foreach ($photosets as $flickr_object) {
1728 $object = array();
1729 $object['id'] = $flickr_object->id;
1730 $object['title'] = esc_attr($flickr_object->title->_content);
1731 $object['counters'] = array();
1732 if (!empty($flickr_object->photos)) $object['counters'][] = esc_html(sprintf(_n('%s photo', '%s photos', $flickr_object->photos, 'photonic'), $flickr_object->photos));
1733 if (!empty($flickr_object->videos)) $object['counters'][] = esc_html(sprintf(_n('%s video', '%s videos', $flickr_object->videos, 'photonic'), $flickr_object->videos));
1734 $object['thumbnail'] = "https://farm" . $flickr_object->farm . ".static.flickr.com/" . $flickr_object->server . "/" . $flickr_object->primary . "_" . $flickr_object->secret . "_q.jpg";
1735 $objects[] = $object;
1736 }
1737 }
1738 else if (isset($body->galleries) && isset($body->galleries->gallery)) {
1739 $page = (int)($body->galleries->page);
1740 $pages = (int)($body->galleries->pages);
1741 $galleries = $body->galleries->gallery;
1742 foreach ($galleries as $flickr_object) {
1743 $object = array();
1744 $object['id'] = $flickr_object->id;
1745 $object['title'] = esc_attr($flickr_object->title->_content);
1746 $object['counters'] = array();
1747 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));
1748 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));
1749 $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";
1750 $objects[] = $object;
1751 }
1752 }
1753 else if (isset($body->photos) && isset($body->photos->photo)) {
1754 if ($display_type == 'single-photo') {
1755 $page = (int)($body->photos->page);
1756 $pages = (int)($body->photos->pages);
1757 if ($page < $pages && !empty($url)) {
1758 $url = remove_query_arg('page', $url);
1759 $pagination['url'] = add_query_arg(array('page' => $page + 1), $url);
1760 }
1761 }
1762 $photos = $body->photos->photo;
1763 foreach ($photos as $flickr_object) {
1764 $object = array();
1765 $object['id'] = $flickr_object->id;
1766 $object['title'] = esc_attr($flickr_object->title);
1767 $object['thumbnail'] = 'https://farm' . $flickr_object->farm . '.static.flickr.com/' . $flickr_object->server . '/' . $flickr_object->id . '_' . $flickr_object->secret . '_q.jpg';
1768 $objects[] = $object;
1769 }
1770 }
1771 else if (isset($body->collections) && isset($body->collections->collection)) {
1772 $collections = $body->collections->collection;
1773 foreach ($collections as $flickr_object) {
1774 $object = array();
1775 $object['id'] = $flickr_object->id;
1776 $object['title'] = esc_attr($flickr_object->title);
1777 $object['counters'] = array();
1778 if (!empty($flickr_object->set)) $object['counters'][] = esc_html(sprintf(_n('%s album', '%s albums', count($flickr_object->set), 'photonic'), count($flickr_object->set)));
1779 $object['thumbnail'] = $flickr_object->iconlarge;
1780 $objects[] = $object;
1781 }
1782 }
1783 else if (isset($body->user)) {
1784 return array('success' => array('user_id' => $body->user->id));
1785 }
1786 else if (isset($body->group)) {
1787 return array('success' => array('group_id' => $body->group->id));
1788 }
1789
1790 if (!empty($page) && !empty($pages) && $page < $pages && !empty($url)) {
1791 $url = remove_query_arg('page', $url);
1792 $pagination['url'] = add_query_arg(array('page' => $page + 1), $url);
1793 }
1794 return $objects;
1795 }
1796
1797 /**
1798 * Processes a response from SmugMug to build it out into a gallery of thumbnails. SmugMug has L1, L2 and L3 displays in the flow.
1799 *
1800 * @param $response
1801 * @param $display_type
1802 * @param null $url
1803 * @param array $pagination
1804 * @return array
1805 */
1806 private function process_smugmug_response($response, $display_type, $url = null, &$pagination = array()) {
1807 $objects = array();
1808 $body = json_decode(wp_remote_retrieve_body($response));
1809 if (isset($body->Response) && isset($body->Response->Album)) {
1810 $albums = $body->Response->Album;
1811 foreach ($albums as $album) {
1812 $object = array();
1813 if (isset($album->AlbumKey)) {
1814 $object['id'] = $album->AlbumKey;
1815 }
1816 else {
1817 $uri = $album->Uris->Album->Uri;
1818 $uri = explode('/', $uri);
1819 $object['id'] = $uri[count($uri) - 1];
1820 }
1821 $object['title'] = !empty($album->Name) ? esc_attr($album->Name) : '';
1822 if (isset($album->ImageCount)) {
1823 $object['counters'] = array(esc_html(sprintf(_n('%s media item', '%s media items', $album->ImageCount, 'photonic'), $album->ImageCount)));
1824 }
1825
1826 $highlight = $album->Uris->HighlightImage;
1827 if (isset($highlight->Image)) {
1828 $thumbURL = $highlight->Image->Uris->ImageSizes->ImageSizes->ThumbImageUrl;
1829 }
1830 else {
1831 $thumbURL = trailingslashit(PHOTONIC_URL) . 'include/images/placeholder-Th.png';
1832 }
1833
1834 $object['thumbnail'] = $thumbURL;
1835 if (isset($album->SecurityType) && $album->SecurityType == 'Password') {
1836 $object['passworded'] = 1;
1837 }
1838
1839 $objects[] = $object;
1840 }
1841
1842 if (isset($body->Response->Pages)) {
1843 $pages = $body->Response->Pages;
1844 if ($pages->Start + $pages->Count - 1 < $pages->Total) {
1845 $pagination['url'] = add_query_arg(array('start' => $pages->Start + $pages->Count), remove_query_arg(array('start'), $url));
1846 }
1847 }
1848 }
1849 else if (isset($body->Response) && isset($body->Response->Node)) {
1850 $nodes = $body->Response->Node;
1851 foreach ($nodes as $node) {
1852 if ($display_type == 'folder' && $node->Type == 'Album') {
1853 continue;
1854 }
1855 else if ($display_type == 'folder') {
1856 $this->get_smugmug_folders($objects, $node);
1857 }
1858 else {
1859 $object = array();
1860 if ($node->Type == 'Album') {
1861 $uri = $node->Uris->Album->Uri;
1862 $uri = explode('/', $uri);
1863 $uri = $uri[count($uri) - 1];
1864 $object['id'] = $uri;
1865 }
1866 else if ($node->Type == 'Folder') {
1867 $object['id'] = $node->NodeID;
1868 }
1869 $object['title'] = !empty($node->Name) ? esc_attr($node->Name) : '';
1870 if ($display_type == 'tree') {
1871 $object['title'] .= " ({$node->Type})";
1872 }
1873 if (isset($node->Uris->NodeCoverImage->Image->Uris->ImageSizes->ImageSizes->ThumbImageUrl)) {
1874 $object['thumbnail'] = $node->Uris->NodeCoverImage->Image->Uris->ImageSizes->ImageSizes->ThumbImageUrl;
1875 }
1876 else {
1877 $object['thumbnail'] = trailingslashit(PHOTONIC_URL) . 'include/images/placeholder-Th.png';
1878 }
1879 $objects[] = $object;
1880 }
1881 }
1882 }
1883 else if (isset($body->Response) && isset($body->Response->User)) {
1884 $user = $body->Response->User;
1885 return array('success' => $user->Uris->Node->Uri);
1886 }
1887 else {
1888 Photonic::log($body);
1889 return array('error' => $this->error_not_found);
1890 }
1891
1892 return $objects;
1893 }
1894
1895 /**
1896 * Processes a response from Google to build it out into a gallery of thumbnails. Google has both, L1 and L2 displays in the flow.
1897 *
1898 * @param $response
1899 * @param $url
1900 * @param array $pagination
1901 * @return array
1902 */
1903 function process_google_response($response, $url, &$pagination = array()) {
1904 $objects = array();
1905 $body = json_decode(wp_remote_retrieve_body($response));
1906 if (isset($body->albums)) {
1907 $albums = $body->albums;
1908 foreach ($albums as $album) {
1909 $object = array();
1910 $object['id'] = $album->id;
1911 $object['title'] = !empty($album->title) ? esc_attr($album->title) : '';
1912 $object['counters'] = array(esc_html(sprintf(_n('%s media item', '%s media items', $album->mediaItemsCount, 'photonic'), $album->mediaItemsCount)));
1913 $object['thumbnail'] = $album->coverPhotoBaseUrl . "=w150-h150-c";
1914 $objects[] = $object;
1915 }
1916 if (!empty($body->nextPageToken)) {
1917 $pagination['url'] = add_query_arg(array('pageToken' => $body->nextPageToken), remove_query_arg(array('pageToken'), $url));
1918 }
1919 }
1920 else if (isset($body->mediaItems)) {
1921 $photos = $body->mediaItems;
1922 foreach ($photos as $photo) {
1923 $object = array();
1924 $object['id'] = $photo->id;
1925 $object['title'] = !empty($photo->description) ? esc_attr($photo->description) : '';
1926 $object['thumbnail'] = $photo->baseUrl . "=w150-h150-c";
1927 $objects[] = $object;
1928 }
1929 }
1930 else if (isset($body->error)) {
1931 $objects['error'] = $body->error->message;
1932 }
1933 return $objects;
1934 }
1935
1936 /**
1937 * Processes a response from Zenfolio to build it out into a gallery of thumbnails. Zenfolio has both, L1 and L2 displays in the flow.
1938 *
1939 * @param $response
1940 * @param $display_type
1941 * @return array
1942 */
1943 function process_zenfolio_response($response, $display_type) {
1944 $body = wp_remote_retrieve_body($response);
1945 $body = preg_replace('/"Id":(\d+)/', '"Id":"$1"', $body);
1946 $body = simplexml_load_string($body);
1947 $objects = array();
1948 if ($display_type != 'multi-photo') {
1949 if (!empty($body->Elements)) {
1950 $elements = $body->Elements;
1951 $this->get_zenfolio_groups($objects, $elements, $display_type);
1952 }
1953 else if (!empty($body->PhotoSets)) {
1954 $photosets = $body->PhotoSets;
1955 $this->get_zenfolio_groups($objects, $photosets, $display_type);
1956 }
1957
1958 if ($display_type == 'single-photo') {
1959 $photo_array = array();
1960 global $photonic_zenfolio_gallery;
1961 if (!isset($photonic_zenfolio_gallery)) {
1962 $photonic_zenfolio_gallery = new Photonic_Zenfolio_Processor();
1963 }
1964 $requests = array();
1965 foreach ($objects as $object) {
1966 $parameters = array();
1967 $parameters['photoSetId'] = substr($object['id'], 1);
1968 $parameters['level'] = 'Level1';
1969 $parameters['includePhotos'] = 'true';
1970
1971 $request = $photonic_zenfolio_gallery->prepare_request('LoadPhotoSet', $parameters);
1972 $requests[] = array(
1973 'url' => 'https://api.zenfolio.com/api/1.8/zfapi.asmx',
1974 'type' => 'POST',
1975 'headers' => $request['headers'],
1976 'data' => $request['body'],
1977 );
1978 }
1979 $responses = Requests::request_multiple($requests);
1980 if (!empty($responses)) {
1981 foreach ($responses as $ps_response) {
1982 if (is_a($ps_response, 'Requests_Response')) {
1983 $ps_response = json_decode($ps_response->body);
1984 if (!empty($ps_response->result)) {
1985 $ps_response = $ps_response->result;
1986 if (!empty($ps_response->Photos)) {
1987 $photo_array['psid'.$ps_response->Id] = $ps_response->Title;
1988 foreach ($ps_response->Photos as $ps_photo) {
1989 if (array_key_exists($ps_photo->Id, $photo_array)) {
1990 continue;
1991 }
1992 $photo = array();
1993 $photo['id'] = $ps_photo->Id;
1994 $url_parts = explode('/', $ps_photo->UrlCore);
1995 $photo['alt_id'] = 'h'.dechex((int)substr($url_parts[count($url_parts) - 1], 1));
1996 $photo['alt_id2'] = $url_parts[count($url_parts) - 1];
1997 $photo['title'] = esc_attr($ps_photo->Title);
1998 $photo['thumbnail'] = 'https://' . $ps_photo->UrlHost . $ps_photo->UrlCore . '-1.jpg';
1999 $photo_array[$ps_photo->Id] = $photo;
2000 }
2001 }
2002 }
2003 }
2004 }
2005 }
2006 $objects = array_values($photo_array);
2007 }
2008 return $objects;
2009 }
2010 else {
2011 $photos = $body->Photos;
2012 foreach ($photos->Photo as $photo) {
2013 $object = array();
2014 $object['id'] = $photo->Id;
2015 $object['title'] = esc_attr($photo->Title);
2016 $object['thumbnail'] = 'https://' . $photo->UrlHost . $photo->UrlCore . '-1.jpg';
2017 $objects[] = $object;
2018 }
2019 return $objects;
2020 }
2021 }
2022
2023 /**
2024 * Processes a response from Instagram to build it out into a gallery of thumbnails. Instagram only has L1 displays.
2025 *
2026 * @param $response
2027 * @param $display_type
2028 * @param $url
2029 * @param array $pagination
2030 * @return array
2031 */
2032 function process_instagram_response($response, $display_type, $url, &$pagination = array()) {
2033 $objects = array();
2034 $body = json_decode($response['body']);
2035 if (isset($body->data)) {
2036 $data = $body->data;
2037 foreach ($data as $photo) {
2038 if (isset($photo->type) && ($photo->type == 'image' || $photo->type == 'carousel' || $photo->type == 'video') && isset($photo->images)) {
2039 $object = array();
2040 $link = $photo->link;
2041 $link = explode('/', $link);
2042 if (empty($link[count($link) - 1])) {
2043 $link = $link[count($link) - 2];
2044 }
2045 else {
2046 $link = $link[count($link) - 1];
2047 }
2048 $object['id'] = $link;
2049 if (isset($photo->caption) && isset($photo->caption->text)) {
2050 $object['title'] = esc_attr($photo->caption->text);
2051 }
2052 else {
2053 $object['title'] = '';
2054 }
2055 $object['thumbnail'] = $photo->images->thumbnail->url;
2056 $objects[] = $object;
2057 }
2058 }
2059 if (isset($body->pagination) && isset($body->pagination->next_max_id) && $display_type == 'single-photo') {
2060 $pagination['url'] = add_query_arg(array('max_id' => $body->pagination->next_max_id), remove_query_arg(array('max_id'), $url));
2061 }
2062 }
2063 return $objects;
2064 }
2065 }
2066