Advertisement
Guest User

Untitled

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