HEX
Server: nginx/1.24.0
System: Linux server 6.12.74+deb13+1-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.74-2 (2026-03-08) x86_64
User: www (1001)
PHP: 8.5.2
Disabled: passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv
Upload Files
File: /www/wwwroot/claudiayancor.duckdns.org/wp-content/plugins/photonic/Core/Photonic.php
<?php

namespace Photonic_Plugin\Core;

use Photonic_Plugin\Admin\Admin_Menu;
use Photonic_Plugin\Admin\Authentication;
use Photonic_Plugin\Admin\Helper;
use Photonic_Plugin\Options\Defaults;
use Photonic_Plugin\Options\Options;
use WP_Error;

class Photonic {
	public $defaults;
	public $localized;
	public $provider_map;
	public $admin_menu;
	public static $library;
	public static $lightbox_replacements;
	public static $safe_tags;

	public function __construct() {
		// $start = microtime(true);
		global $photonic_options;

		self::$lightbox_replacements = [
			'fancybox'    => 'fancybox3',
			'magnific'    => 'venobox',
			'prettyphoto' => 'spotlight',
		];

		self::$safe_tags = array_merge_recursive(
			[
				// Videos
				'video' => [
					'width'    => true,
					'height'   => true,
					'class'    => true,
					'controls' => true,
					'preload'  => true,
					'poster'   => true,
				],

				'source' => [
					'src'  => true,
					'type' => true,
				],

				// Password prompters
				'input'  => [
					'type' => true,
					'name' => true,
				],

				'button' => [
					'class' => true,
				],

				// Custom data attributes
				'div' => [
					'data-photonic-platform' => true,
					'data-photonic-query' => true,
					'data-photonic-gallery-columns' => true,
					'data-photonic-prompt' => true,

					// Slideshow
					'data-splide' => true,
				],

				'ul' => [
					// Slideshows
					'data-photonic-columns' => true,
					'data-photonic-controls' => true,
					'data-photonic-fx' => true,
					'data-photonic-layout' => true,
					'data-photonic-pause' => true,
					'data-photonic-speed' => true,
					'data-photonic-strip-style' => true,
					'data-photonic-timeout' => true,
				],

				'img' => [
					// Tooltips
					'data-photonic-tooltip' => true,
				],

				'a' => [
					// Non-photonic
					'data-html5-href' => true,
					'data-title' => true,
					'data-type' => true,

					// Non-Photonic - BaguetteBox
					'data-content-type' => true,

					// Non-Photonic - BigPicture
					'data-bp' => true,
					'data-bp-type' => true,

					// Non-Photonic - Fancybox
					'data-fancybox' => true,

					// Non-Photonic - Featherlight
					'data-featherlight' => true,
					'data-featherlight-type' => true,

					// Non-Photonic - GLightbox
					'data-format' => true,

					// Non-Photonic - Lightcase
					'data-lc-options' => true,

					// Non-Photonic - LightGallery
					'data-download-url' => true,
					'data-video' => true,
					'data-sub-html' => true,

					// Non-Photonic - PhotoSwipe5
					'data-pswp-height' => true,
					'data-pswp-width' => true,

					// Non-Photonic - Spotlight
					'data-media' => true,
					'data-src-mp4' => true,
					'data-poster' => true,

					// Non-Photonic - Strip
					'data-strip-group' => true,
					'data-strip-group-options' => true,
					'data-strip-caption' => true,

					// Non-Photonic - VenoBox
					'data-gall' => true,
					'data-vbtype' => true,

					// Tooltips
					'data-photonic-tooltip' => true,

					// Level 3
					'data-photonic-layout' => true,
					'data-photonic-level-3' => true,

					// Photonic photos
					'data-photonic-deep' => true,
					'data-photonic-media-type' => true,
					'data-rel' => true,

					// Photonic albums, common
					'data-photonic-overlay-size' => true,
					'data-photonic-overlay-video-size' => true,
					'data-photonic-photo-count' => true,
					'data-photonic-photo-more' => true,
					'data-photonic-platform' => true,
					'data-photonic-popup' => true,
					'data-photonic-singular' => true,

					// Albums, Google
					'data-photonic-overlay-crop' => true,
					'data-photonic-thumb-size' => true,

					// Albums, SmugMug
					'data-photonic-buy' => true,

					// Albums, Zenfolio
					'data-photonic-realm' => true,
				]
			],
			wp_kses_allowed_html('post')
		);

		require_once PHOTONIC_PATH . "/Options/Options.php";
		add_action('admin_init', [Options::get_instance(), 'prepare_options'], 20); // Setting to 20 so that CPTs can be picked up - Utilities are loaded with a priority 10

		$this->localized = false;
		$this->provider_map = [
			'flickr'    => 'Flickr',
			'smug'      => 'SmugMug',
			'smugmug'   => 'SmugMug',
			'google'    => 'Google',
			'zenfolio'  => 'Zenfolio',
			'instagram' => 'Instagram',
		];

		add_action('admin_menu', [&$this, 'add_admin_menu']);
		add_action('admin_init', [&$this, 'admin_init']);

		$photonic_options = get_option('photonic_options');
		$set_options = isset($photonic_options) && is_array($photonic_options) ? $photonic_options : [];

		$defaults = Defaults::get_options();
		$all_options = array_merge($defaults, $set_options);

		foreach ($all_options as $key => $value) {
			$mod_key = 'photonic_' . $key;
			global ${$mod_key};
			${$mod_key} = $value;
		}

		define('PHOTONIC_SSL_VERIFY', empty($photonic_ssl_verify_off));
		define('PHOTONIC_DEBUG', !empty($photonic_debug_on));

		if (!empty($photonic_script_dev_mode)) {
			define('PHOTONIC_DEV_MODE', '');
		}
		else {
			define('PHOTONIC_DEV_MODE', '.min');
		}

		if (!empty($photonic_curl_timeout) && is_numeric($photonic_curl_timeout)) {
			define('PHOTONIC_CURL_TIMEOUT', $photonic_curl_timeout);
		}
		else {
			define('PHOTONIC_CURL_TIMEOUT', 30);
		}

		global $photonic_slideshow_library, $photonic_custom_lightbox;
		if ('custom' !== $photonic_slideshow_library) {
			self::$library = esc_attr($photonic_slideshow_library);
			self::$library = empty(self::$lightbox_replacements[self::$library]) ? self::$library : self::$lightbox_replacements[self::$library];
		}
		elseif (empty($photonic_slideshow_library)) {
			self::$library = 'baguettebox';
		}
		else {
			self::$library = esc_attr($photonic_custom_lightbox);
		}

		// Gallery
		if (!empty($photonic_alternative_shortcode)) {
			add_shortcode($photonic_alternative_shortcode, [&$this, 'modify_gallery']);
			add_filter('shortcode_atts_' . $photonic_alternative_shortcode, [&$this, 'native_gallery_attributes'], 10, 3);
		}
		else {
			add_filter('post_gallery', [&$this, 'modify_gallery'], 20, 2);
			add_filter('shortcode_atts_gallery', [&$this, 'native_gallery_attributes'], 10, 3);
		}

		add_shortcode('photonic_helper', [&$this, 'helper_shortcode']);

		add_filter('script_loader_tag', [&$this, 'add_script_type'], 10, 3);

		add_action('wp_enqueue_scripts', [&$this, 'always_add_styles'], 20);
		if (!empty($photonic_always_load_scripts)) {
			add_action('wp_enqueue_scripts', [&$this, 'conditionally_add_scripts'], 20, 0);
		}

		add_action('wp_ajax_photonic_display_level_2_contents', [&$this, 'display_level_2_contents']);
		add_action('wp_ajax_nopriv_photonic_display_level_2_contents', [&$this, 'display_level_2_contents']);

		add_action('wp_ajax_photonic_display_level_3_contents', [&$this, 'display_level_3_contents']);
		add_action('wp_ajax_nopriv_photonic_display_level_3_contents', [&$this, 'display_level_3_contents']);

		add_action('wp_ajax_photonic_load_more', [&$this, 'load_more']);
		add_action('wp_ajax_nopriv_photonic_load_more', [&$this, 'load_more']);

		add_action('wp_ajax_photonic_lazy_load', [&$this, 'lazy_load']);
		add_action('wp_ajax_nopriv_photonic_lazy_load', [&$this, 'lazy_load']);

		add_action('wp_ajax_photonic_helper_shortcode_more', [&$this, 'helper_shortcode_more']);
		add_action('wp_ajax_nopriv_photonic_helper_shortcode_more', [&$this, 'helper_shortcode_more']);

		add_action('wp_ajax_photonic_invoke_helper', [&$this, 'invoke_helper']);
		add_action('wp_ajax_photonic_obtain_token', [&$this, 'obtain_token']);
		add_action('wp_ajax_photonic_save_token', [&$this, 'save_token_in_options']);
		add_action('wp_ajax_photonic_delete_token', [&$this, 'delete_token_from_options']);

		add_action('photonic_token_monitor', [&$this, 'monitor_token_validity']);
		if (!wp_next_scheduled('photonic_token_monitor')) {
			wp_schedule_event(time(), 'hourly', 'photonic_token_monitor');
		}

		$this->add_extensions();
		$this->add_gutenberg_support();

		add_action('wp_ajax_photonic_dismiss_warning', [&$this, 'dismiss_warning']);

		add_action('http_api_curl', [&$this, 'curl_timeout'], 100, 1);

		add_action('plugins_loaded', [&$this, 'enable_translations']);

		add_filter('body_class', [&$this, 'body_class']);

		add_filter('safe_style_css', [&$this, 'safe_styles']);

		// $end = microtime(true);
		// print_r("<!-- Photonic initialization: ".($end - $start)." -->\n");

		add_action('widgets_init', [&$this, 'load_widget']);

		// add_action('elementor/common/after_register_scripts', [&$this, 'enqueue_widget_scripts']);
		add_action('elementor/editor/before_enqueue_scripts', [&$this, 'enqueue_widget_scripts']);

		require_once PHOTONIC_PATH . "/Core/Template.php";
	}

	/**
	 * @param string $provider
	 * @param array $auth_token
	 */
	private static function save_provider_authentication($provider, $auth_token) {
		if (current_user_can('edit_theme_options')) { // Method is private, and is only called from save_token_in_options, where there is a nonce check
			$photonic_authentication = get_option('photonic_authentication');
			if (empty($photonic_authentication)) {
				$photonic_authentication = [];
			}
			$photonic_authentication[$provider] = $auth_token;
			update_option('photonic_authentication', $photonic_authentication);
			set_transient('photonic_' . $provider . '_token', $auth_token);
		}
	}

	/**
	 * Adds a menu item to the "Settings" section of the admin page.
	 *
	 * @return void
	 */
	public function add_admin_menu() {
		if (current_user_can('edit_theme_options')) {
			$parent_slug = 'photonic-options-manager';
		}
		elseif (current_user_can('edit_posts')) {
			$parent_slug = 'photonic-getting-started';
		}

		if (!empty($parent_slug)) {
			add_menu_page('Photonic', 'Photonic', 'edit_posts', $parent_slug, [&$this->admin_menu, 'settings'], PHOTONIC_URL . 'include/images/Photonic-20-gr.png');
			add_submenu_page($parent_slug, esc_html__('Settings', 'photonic'), esc_html__('Settings', 'photonic'), 'edit_theme_options', 'photonic-options-manager', [&$this->admin_menu, 'settings']);
			add_submenu_page($parent_slug, 'Getting Started', 'Getting Started', 'edit_posts', 'photonic-getting-started', [&$this->admin_menu, 'getting_started']);
			add_submenu_page($parent_slug, 'Authentication', 'Authentication', 'edit_theme_options', 'photonic-auth', [&$this->admin_menu, 'authentication']);
			add_submenu_page($parent_slug, esc_html__('Shortcode Replacement', 'photonic'), esc_html__('Shortcode Replacement', 'photonic'), 'edit_posts', 'photonic-shortcode-replace', [&$this->admin_menu, 'shortcode']);
			add_submenu_page($parent_slug, 'Helpers', 'Helpers', 'edit_posts', 'photonic-helpers', [&$this->admin_menu, 'helpers']);
		}
	}

	/**
	 * Adds all scripts and their dependencies to the end of the <body> element only on pages using Photonic.
	 *
	 * @return void
	 */
	public function conditionally_add_scripts() {
		global $photonic_slideshow_library, $photonic_custom_lightbox_js, $photonic_custom_lightbox, $photonic_always_load_scripts,
			   $photonic_disable_photonic_lightbox_scripts, $photonic_disable_photonic_slider_scripts, $photonic_js_in_header, $photonic_js_type;

		$library_versions = [
			'baguettebox'   => '1.11.1',
			'bigpicture'    => '2022-03-31',
			'colorbox'      => '1.6.4',
			'fancybox3'     => '3.5.7',
			'featherlight'  => '1.7.14',
			'glightbox'     => '2022-03-12',
			'imagelightbox' => '2018-08-17',
			'lightcase'     => '2.5.0',
			'lightgallery'  => '2.4.0',
			'photoswipe'    => '4.1.3',
			'photoswipe5'   => '5.2.8',
			'prettyphoto'   => '3.1.6',
			'spotlight'     => '0.7.8',
			'splide'        => '4.0.1',
			'strip'         => '1.8.0',
			'swipebox'      => '1.5.2',
			'venobox'       => '2.0.4',
		];

		$requires_jq = [
			'colorbox',
			'fancybox',
			'fancybox2',
			'fancybox3',
			'featherlight',
			'imagelightbox',
			'lightcase',
			'magnific',
			'prettyphoto',
			'strip',
			'swipebox',
		];

		$photonic_dependencies = [];
		$lb_deps = [];
		if (in_array(self::$library, $requires_jq, true)) {
			$photonic_dependencies[] = 'jquery';
			$lb_deps[] = 'jquery';
		}

		if (in_array(self::$library, ['colorbox', 'fancybox', 'fancybox2', 'prettyphoto'], true)) {
			wp_enqueue_script('jquery-detect-swipe', PHOTONIC_URL . 'include/ext/jquery.detect_swipe.js', ['jquery'], $this->get_version(PHOTONIC_PATH . '/include/ext/jquery.detect_swipe.js'), !($photonic_always_load_scripts && $photonic_js_in_header));
			$photonic_dependencies[] = 'jquery-detect-swipe';
		}

		if ('thickbox' === self::$library) {
			wp_enqueue_script('thickbox');
			$photonic_dependencies[] = 'thickbox';
		}
		elseif ('custom' === $photonic_slideshow_library && 'strip' !== $photonic_custom_lightbox) {
			$counter = 1;
			$dependencies = ['jquery'];
			foreach (preg_split("/((\r?\n)|(\r\n?))/", $photonic_custom_lightbox_js) as $line) {
				wp_enqueue_script('photonic-lightbox-' . $counter, trim($line), $dependencies, PHOTONIC_VERSION, !($photonic_always_load_scripts && $photonic_js_in_header));
				$photonic_dependencies[] = 'photonic-lightbox-' . $counter;
				$counter++;
			}
		}
		elseif ('none' !== self::$library) {
			if (empty($photonic_disable_photonic_lightbox_scripts)) {
				$photonic_dependencies[] = self::$library;
			}

			if (empty($photonic_disable_photonic_lightbox_scripts)) {
				if ('lightgallery' === self::$library) {
					global $photonic_enable_lg_zoom, $photonic_enable_lg_thumbnail, $photonic_enable_lg_fullscreen, $photonic_enable_lg_autoplay;
					$lightgallery_plugins = [];
					if (!empty($photonic_enable_lg_autoplay)) {
						$lightgallery_plugins[] = 'autoplay';
					}
					if (!empty($photonic_enable_lg_fullscreen)) {
						$lightgallery_plugins[] = 'fullscreen';
					}
					if (!empty($photonic_enable_lg_thumbnail)) {
						$lightgallery_plugins[] = 'thumbnail';
					}
					if (!empty($photonic_enable_lg_zoom)) {
						$lightgallery_plugins[] = 'zoom';
					}
					if (!empty($lightgallery_plugins)) {
						wp_enqueue_script('lightgallery', PHOTONIC_URL . 'include/ext/' . self::$library . '/' . self::$library . PHOTONIC_DEV_MODE . '.js', $lb_deps, $this->get_version(PHOTONIC_PATH . '/include/ext/' . self::$library . '/' . self::$library . PHOTONIC_DEV_MODE . '.js'), !($photonic_always_load_scripts && $photonic_js_in_header));
						$photonic_dependencies[] = 'lightgallery';
					}
					if (!empty(PHOTONIC_DEV_MODE)) {
						foreach ($lightgallery_plugins as $plugin) {
							wp_enqueue_script('photonic-lightbox-' . $plugin, PHOTONIC_URL . 'include/ext/' . self::$library . '/lg-plugin-' . $plugin . '.min.js', ['lightgallery'], $this->get_version(PHOTONIC_PATH . '/include/ext/' . self::$library . '/lg-plugin-' . $plugin . '.min.js'), !($photonic_always_load_scripts && $photonic_js_in_header));
						}
					}
					else {
						wp_enqueue_script('photonic-lightbox-plugins', PHOTONIC_URL . 'include/ext/' . self::$library . '/lightgallery-plugins.js', ['lightgallery'], $this->get_version(PHOTONIC_PATH . '/include/ext/' . self::$library . '/lightgallery-plugins.js'), !($photonic_always_load_scripts && $photonic_js_in_header));
					}
				}
				elseif ('photoswipe5' === self::$library) {
					wp_deregister_script('photoswipe'); // Remove any older version of PhotoSwipe, since the new version is not compatible with it
				}
			}
		}

		if (empty($photonic_disable_photonic_slider_scripts)) {
			wp_enqueue_script('splide', PHOTONIC_URL . 'include/ext/splide/splide' . PHOTONIC_DEV_MODE . '.js', [], $library_versions['splide'] . '-' . $this->get_version(PHOTONIC_PATH . '/include/ext/splide/splide' . PHOTONIC_DEV_MODE . '.js'), !($photonic_always_load_scripts && $photonic_js_in_header));
			$photonic_dependencies[] = 'splide';
		}

		$slideshow_library = self::$library;

		if (empty($photonic_disable_photonic_lightbox_scripts) && 'none' !== $slideshow_library && 'thickbox' !== $slideshow_library && $slideshow_library !== $photonic_custom_lightbox/* && 'photoswipe5' !== $slideshow_library*/) {
			wp_enqueue_script($slideshow_library, PHOTONIC_URL . 'include/ext/' . $slideshow_library . '/' . $slideshow_library . PHOTONIC_DEV_MODE . '.js', $lb_deps, (empty($library_versions[$slideshow_library]) ? $this->get_version(PHOTONIC_PATH . "/include/ext/$slideshow_library/$slideshow_library" . PHOTONIC_DEV_MODE . ".js") : $library_versions[$slideshow_library]), !($photonic_always_load_scripts && $photonic_js_in_header));
		}

		// $photonic_js_type = 'raw';
		$nomodule = "include/js/front-end/nomodule/photonic-" . $slideshow_library . PHOTONIC_DEV_MODE . '.js';
		$module = "include/js/front-end/module/photonic-" . $slideshow_library . PHOTONIC_DEV_MODE . '.js';

		if (in_array($photonic_js_type, ['transpiled', 'all'], true)) {
			wp_enqueue_script('photonic', PHOTONIC_URL . $nomodule, $photonic_dependencies, $this->get_version(PHOTONIC_PATH . "/" . $nomodule), !($photonic_always_load_scripts && $photonic_js_in_header));
		}

		if ('modules' === $photonic_js_type) {
			wp_enqueue_script('photonic', PHOTONIC_URL . $module, $photonic_dependencies, $this->get_version(PHOTONIC_PATH . "/" . $module), !($photonic_always_load_scripts && $photonic_js_in_header));
		}
		elseif ('all' === $photonic_js_type) {
			wp_enqueue_script('photonic-esm', PHOTONIC_URL . $module, $photonic_dependencies, $this->get_version(PHOTONIC_PATH . "/" . $module), !($photonic_always_load_scripts && $photonic_js_in_header));
		}

		if ('raw' === $photonic_js_type) {
			$proper_case = 'BaguetteBox';
			$lower_case = strtolower($proper_case);
			wp_enqueue_script('photonic-slider', PHOTONIC_URL . 'include/ext/splide/splide.min.js', [], $this->get_version(PHOTONIC_PATH . '/include/ext/splide/splide.min.js'), !($photonic_always_load_scripts && $photonic_js_in_header));
			wp_enqueue_script('photonic-lightbox', PHOTONIC_URL . "include/ext/$lower_case/$lower_case" . PHOTONIC_DEV_MODE . ".js", $lb_deps, $this->get_version(PHOTONIC_PATH . "/include/ext/$lower_case/$lower_case" . PHOTONIC_DEV_MODE . ".js"), !($photonic_always_load_scripts && $photonic_js_in_header));
			wp_enqueue_script('photonic', PHOTONIC_URL . "include/js/front-end/src/Entries/$proper_case.js", ['photonic-slider', 'photonic-lightbox'], $this->get_version(PHOTONIC_PATH . "/include/js/front-end/src/Entries/$proper_case.js"), !($photonic_always_load_scripts && $photonic_js_in_header));
		}

		$this->localize_variables_once();
	}

	public function localize_variables_once() {
		if ($this->localized) {
			return;
		}
		// Technically JS, but needs to happen here, otherwise the script is repeated multiple times, once for each time
		// <code>conditionally_add_scripts</code> is called.
		$js_array = $this->get_localized_js_variables();
		wp_localize_script('photonic', 'Photonic_JS', $js_array);
		$this->localized = true;
	}

	/**
	 * Adds all styles to all pages because styles, if not added in the header can cause issues.
	 *
	 * @return void
	 */
	public function always_add_styles() {
		global $photonic_slideshow_library, $photonic_custom_lightbox_css, $photonic_disable_photonic_lightbox_scripts, $photonic_disable_photonic_slider_scripts;

		if ('custom' === $photonic_slideshow_library) {
			$counter = 1;
			foreach (preg_split("/((\r?\n)|(\r\n?))/", $photonic_custom_lightbox_css) as $line) {
				wp_enqueue_style('photonic-lightbox-' . $counter, trim($line), [], PHOTONIC_VERSION);
				$counter++;
			}
		}

		$slideshow_library = esc_attr(!empty($photonic_disable_photonic_lightbox_scripts) ? 'none' : self::$library);

		$this->enqueue_lightbox_styles($slideshow_library, empty($photonic_disable_photonic_slider_scripts));

		global $photonic_css_in_file;
		$file = trailingslashit(PHOTONIC_UPLOAD_DIR) . 'custom-styles.css';
		if (@file_exists($file) && !empty($photonic_css_in_file)) {
			wp_enqueue_style('photonic-custom', trailingslashit(PHOTONIC_UPLOAD_URL) . 'custom-styles.css', ['photonic'], $this->get_version($file));
		}
		else {
			wp_add_inline_style('photonic', $this->generate_css());
		}

		if (class_exists('\FLBuilderModel') && \FLBuilderModel::is_builder_active()) {
			$this->enqueue_widget_scripts();
		}
	}

	public function enqueue_lightbox_styles($slideshow_library = 'swipebox', $include_slider = true) {
		if ($include_slider) {
			wp_enqueue_style('photonic-slider', PHOTONIC_URL . 'include/ext/splide/splide' . PHOTONIC_DEV_MODE . '.css', [], $this->get_version(PHOTONIC_PATH . '/include/ext/splide/splide' . PHOTONIC_DEV_MODE . '.css'));
		}

		$no_css = [
			'bigpicture',

			'none',
			'fancybox',
			'fancybox2',
			'fancybox4',
			'magnific',
			'prettyphoto',
		];

		if ('colorbox' === $slideshow_library) {
			global $photonic_cbox_theme;
			if ('theme' === $photonic_cbox_theme) {
				wp_enqueue_style('photonic-lightbox', PHOTONIC_URL . 'include/ext/colorbox/style-1/colorbox.css', [], $this->get_version(PHOTONIC_PATH . '/include/ext/colorbox/style-1/colorbox.css'));
			}
			else {
				wp_enqueue_style('photonic-lightbox', PHOTONIC_URL . 'include/ext/colorbox/style-' . $photonic_cbox_theme . '/colorbox.css', [], $this->get_version(PHOTONIC_PATH . '/include/ext/colorbox/style-' . $photonic_cbox_theme . '/colorbox.css'));
			}
		}
		elseif ('lightgallery' === $slideshow_library) {
			global $photonic_enable_lg_transitions;
			wp_enqueue_style('photonic-lightbox', PHOTONIC_URL . 'include/ext/lightgallery/lightgallery' . PHOTONIC_DEV_MODE . '.css', [], $this->get_version(PHOTONIC_PATH . '/include/ext/lightgallery/lightgallery' . PHOTONIC_DEV_MODE . '.css'));
			if (!empty($photonic_enable_lg_transitions)) {
				wp_enqueue_style('photonic-lightbox-lg-transitions', PHOTONIC_URL . 'include/ext/lightgallery/lightgallery-transitions.min.css', [], $this->get_version(PHOTONIC_PATH . '/include/ext/lightgallery/lightgallery-transitions.min.css'));
			}
		}
		elseif ('thickbox' === $slideshow_library) {
			wp_enqueue_style('thickbox');
		}
		elseif (!in_array($slideshow_library, $no_css, true)) {
			wp_enqueue_style('photonic-lightbox', PHOTONIC_URL . 'include/ext/' . $slideshow_library . '/' . $slideshow_library . PHOTONIC_DEV_MODE . '.css', [], $this->get_version(PHOTONIC_PATH . '/include/ext/' . $slideshow_library . '/' . $slideshow_library . PHOTONIC_DEV_MODE . '.css'));
		}

		wp_enqueue_style('photonic', PHOTONIC_URL . "include/css/front-end/core/photonic" . PHOTONIC_DEV_MODE . ".css", [], $this->get_version(PHOTONIC_PATH . "/include/css/front-end/core/photonic" . PHOTONIC_DEV_MODE . ".css"));
	}

	/**
	 * Prints the dynamically generated CSS based on option selections.
	 *
	 * @param bool $header
	 * @return string
	 */
	public function generate_css($header = true) {
		global $photonic_flickr_collection_set_constrain_by_padding, $photonic_flickr_photos_constrain_by_padding, $photonic_flickr_galleries_constrain_by_padding;
		global $photonic_smug_photos_constrain_by_padding, $photonic_smug_albums_album_constrain_by_padding, $photonic_instagram_photos_constrain_by_padding;
		global $photonic_zenfolio_photos_constrain_by_padding, $photonic_zenfolio_sets_constrain_by_padding, $photonic_tile_spacing, $photonic_masonry_tile_spacing, $photonic_mosaic_tile_spacing, $photonic_masonry_min_width;
		global $photonic_google_photos_constrain_by_padding;

		$css = '';

		$saved_css = get_option('photonic_css');

		if ($header && !empty($saved_css)) {
			$css .= "/* Retrieved from saved CSS */\n";
			$css .= $saved_css;
		}
		else {
			if ($header) {
				$css .= "/* Dynamically generated CSS */\n";
			}
			$css .= ".photonic-panel { " .
				$this->get_bg_css('photonic_flickr_gallery_panel_background') .
				$this->get_border_css('photonic_flickr_set_popup_thumb_border') .
				" }\n";

			$css .= ".photonic-flickr-stream .photonic-pad-photosets { margin: " . esc_attr($photonic_flickr_collection_set_constrain_by_padding) . "px; }\n";
			$css .= ".photonic-flickr-stream .photonic-pad-galleries { margin: " . esc_attr($photonic_flickr_galleries_constrain_by_padding) . "px; }\n";
			$css .= ".photonic-flickr-stream .photonic-pad-photos { padding: 5px " . esc_attr($photonic_flickr_photos_constrain_by_padding) . "px; }\n";

			$css .= ".photonic-google-stream .photonic-pad-photos { padding: 5px " . esc_attr($photonic_google_photos_constrain_by_padding) . "px; }\n";

			$css .= ".photonic-zenfolio-stream .photonic-pad-photos { padding: 5px " . esc_attr($photonic_zenfolio_photos_constrain_by_padding) . "px; }\n";
			$css .= ".photonic-zenfolio-stream .photonic-pad-photosets { margin: 5px " . esc_attr($photonic_zenfolio_sets_constrain_by_padding) . "px; }\n";

			$css .= ".photonic-smug-stream .photonic-pad-albums { margin: " . esc_attr($photonic_smug_albums_album_constrain_by_padding) . "px; }\n";
			$css .= ".photonic-smug-stream .photonic-pad-photos { padding: 5px " . esc_attr($photonic_smug_photos_constrain_by_padding) . "px; }\n";

			$css .= ".photonic-random-layout .photonic-thumb { padding: " . esc_attr($photonic_tile_spacing) . "px}\n";
			$css .= ".photonic-masonry-layout .photonic-thumb { padding: " . esc_attr($photonic_masonry_tile_spacing) . "px}\n";
			$css .= ".photonic-mosaic-layout .photonic-thumb { padding: " . esc_attr($photonic_mosaic_tile_spacing) . "px}\n";
		}

		return $css;
	}

	public static function get_version($file) {
		return date("Ymd-Gis", @filemtime($file));
	}

	public function admin_init() {
		require_once PHOTONIC_PATH . "/Admin/Admin.php";

		if (!empty($_REQUEST['page']) &&
			in_array($_REQUEST['page'], ['photonic-options-manager', 'photonic-options', 'photonic-helpers', 'photonic-getting-started', 'photonic-auth', 'photonic-shortcode-replace'], true)) {
			require_once PHOTONIC_PATH . "/Admin/Admin_Menu.php";
			$this->admin_menu = new Admin_Menu(__FILE__, $this);
		}
	}

	public function add_extensions() {
		require_once "Gallery.php";
		require_once PHOTONIC_PATH . "/Modules/Core.php";
		require_once PHOTONIC_PATH . '/Layouts/Core_Layout.php';
	}

	/**
	 * Overrides the native gallery short code, and does a lot more.
	 *
	 * @param $content
	 * @param array $attr
	 * @return string
	 */
	public function modify_gallery($content, $attr = []) {
		global $photonic_disable_on_home_page, $photonic_disable_on_archives;
		if ((is_archive() && !empty($photonic_disable_on_archives)) ||
			(is_home() && !empty($photonic_disable_on_home_page))) {
			return false;
		}

		global $photonic_alternative_shortcode;

		// If an alternative shortcode is used, then $content has the shortcode attributes
		if (!empty($photonic_alternative_shortcode)) {
			$attr = $content;
		}
		if (empty($attr)) {
			$attr = [];
		}

		$this->conditionally_add_scripts();
		$images = $this->get_gallery_images($attr);

		if (isset($images) && !is_array($images)) {
			return $images;
		}

		return $content;
	}

	/**
	 * Adds Photonic attributes to the native WP galleries. This cannot be called in <code>Photonic_Plugin\Modules\Native</code> because
	 * that class is not initialised until a gallery of the native type is encountered
	 *
	 * @param $out
	 * @param $pairs
	 * @param $attributes
	 * @return mixed
	 */
	public function native_gallery_attributes($out, $pairs, $attributes) {
		global $photonic_wp_title_caption, $photonic_enable_popup, $photonic_thumbnail_style, $photonic_alternative_shortcode;

		$bypass = empty($photonic_enable_popup) || 'off' === $photonic_enable_popup;
		$defaults = [
			'layout'      => esc_attr($photonic_thumbnail_style ?: 'square'),
			'more'        => '',
			'display'     => 'in-page',
			'panel'       => '',
			'filter'      => '',
			'filter_type' => 'include',
			'fx'          => 'slide',    // Splide effects: fade and slide
			'timeout'     => 4000,    // Time between slides in ms
			'speed'       => 3000,    // Time for each transition
			'pause'       => true,    // Pause on hover
			'strip-style' => 'thumbs',
			'controls'    => 'show',
			'popup'       => $bypass ? 'hide' : 'show',

			'custom_classes' => '',
			'alignment'      => '',

			'caption'          => esc_attr($photonic_wp_title_caption),
			'page'             => 1,
			'count'            => -1,
			'thumb_width'      => 75,
			'thumb_height'     => 75,
			'thumb_size'       => 'thumbnail',
			'slide_size'       => 'large',
			'slideshow_height' => 500,
		];

		$attributes = array_merge($defaults, $attributes);
		if (empty($attributes['style']) || ('default' === $attributes['style'] && !empty($photonic_alternative_shortcode) && 'gallery' !== $photonic_alternative_shortcode)) {
			$attributes['style'] = $attributes['layout'];
		}

		foreach ($attributes as $key => $value) {
			$out[$key] = $value;
		}
		return $out;
	}

	/**
	 * @param array $attr
	 * @return string
	 */
	public function helper_shortcode($attr = []) {
		if (empty($attr)) {
			$attr = [];
		}

		$this->conditionally_add_scripts();

		if (empty($attr['type']) || !in_array(strtolower($attr['type']), ['google', 'flickr', 'smugmug', 'zenfolio'], true)) {
			return sprintf(esc_html__('Please specify a value for %1$s. Accepted values are %2$s, %3$s, %4$s, %5$s', 'photonic'), '<code>type</code>', '<code>google</code>', '<code>flickr</code>', '<code>smugmug</code>', '<code>zenfolio</code>');
		}

		$gallery = new Gallery($attr);
		return $gallery->get_helper_contents();
	}

	/**
	 * @param $attr
	 * @return array|bool|string
	 */
	public function get_gallery_images($attr) {
		global $photonic_nested_shortcodes, $photonic_load_mode, $photonic_thumbnail_style;
		$attr = array_merge(
			[
				// Especially for Photonic
				'type'    => 'default',  // default, flickr, smugmug, google, zenfolio, instagram
				'style'   => 'default',  // default, strip-below, strip-above, strip-right, strip-left, no-strip, launch
				'display' => 'in-page',
			],
			$attr
		);

		if ($photonic_nested_shortcodes) {
			$attr = array_map('do_shortcode', $attr);
		}

		$type = strtolower($attr['type']);

		if ('picasa' === $type) {
			$message = esc_html__('Google has deprecated the Picasa API. Please consider switching over to Google Photos', 'photonic');
			return "<div class='photonic-error'>\n\t<span class='photonic-error-icon photonic-icon'>&nbsp;</span>\n\t<div class='photonic-message'>\n\t\t$message\n\t</div>\n</div>\n";
		}

		if ('500px' === $type) {
			$message = esc_html__('The API for 500px.com is no longer available for public use.', 'photonic');
			return "<div class='photonic-error'>\n\t<span class='photonic-error-icon photonic-icon'>&nbsp;</span>\n\t<div class='photonic-message'>\n\t\t$message\n\t</div>\n</div>\n";
		}

		$layout = ('default' === $type || 'wp' === $type)
			? $attr['style']
			: (!empty($attr['layout'])
				? $attr['layout']
				: $photonic_thumbnail_style);
		$layout = esc_attr($layout);

		$lazy_allowed = in_array($type, ['flickr', 'smugmug', 'google', 'zenfolio', 'instagram'], true)
			&& in_array($layout, ['square', 'circle', 'random', 'masonry', 'mosaic'], true);

		if (!empty($attr['show_gallery']) && $lazy_allowed) { // Lazy button not for WP galleries
			$images = $this->get_lazy_load_button($attr, 'show_gallery');
		}
		elseif ((('js' === $photonic_load_mode && (empty($attr['load_mode']) || 'js' === trim(esc_attr($attr['load_mode'])))) || ('php' === $photonic_load_mode && (!empty($attr['load_mode']) && 'js' === trim(sanitize_text_field($attr['load_mode'])))))
			&& $lazy_allowed) { // Lazy button not for WP galleries
			$attr['load_mode'] = 'js'; // Need to set this for cases where the shortcode didn't have the setting; get_lazy_load_button fails
			$images = $this->get_lazy_load_button($attr, 'js_load');
		}
		else {
			$gallery = new Gallery($attr);
			$images = $gallery->get_contents();
		}

		return $images;
	}

	/**
	 * Clicking on a level 2 object (i.e. an Album / Set / Gallery) triggers this. This will fetch the contents of the level 2 object and generate the markup for it.
	 * This is the hook for an AJAX-invoked call
	 *
	 * @return void
	 */
	public function display_level_2_contents() {
		// Cannot use a nonce here. Users often cache the gallery markup, which would cache the nonce. This would make it impossible to run this call after a certain amount of time.
		$panel = sanitize_text_field($_POST['panel_id']);
		$components = explode('-', $panel);

		if (count($components) <= 5) {
			die();
		}
		$panel = implode('-', array_slice($components, 4, 10, true));
		$query = sanitize_text_field($_POST['query']);
		$query = wp_parse_args($query);

		$args = [
			'display'    => 'popup',
			'layout'     => 'square',
			'panel'      => $panel,
			'password'   => !empty($_POST['password']) ? sanitize_text_field($_POST['password']) : '',
			'count'      => sanitize_text_field($_POST['photo_count']),
			'photo_more' => sanitize_text_field($_POST['photo_more']),
			'main_size'  => $query['main_size'],
			'type'       => $components[1]
		];

		$provider = $components[1];
		$type = $components[2];
		if (in_array($provider, ['smug', 'smugmug', 'zenfolio', 'google', 'flickr'], true)) {
			if ('smug' === $provider) {
				$args['view'] = 'album';
				$args['album_key'] = $components[4];
			}
			elseif ('zenfolio' === $provider) {
				$args['view'] = 'photosets';
				$args['object_id'] = $components[4];
				$args['thumb_size'] = sanitize_text_field($_POST['overlay_size']);
				$args['video_size'] = sanitize_text_field($_POST['overlay_video_size']);
				if (isset($_POST['realm_id'])) {
					$args['realm_id'] = sanitize_text_field($_POST['realm_id']);
				}
			}
			elseif ('google' === $provider) {
				$args['view'] = 'photos';
				$args['album_id'] = implode('-', array_slice($components, 4, (count($components) - 1) - 4));
				$args['thumb_size'] = sanitize_text_field($_POST['overlay_size']);
				$args['video_size'] = sanitize_text_field($_POST['overlay_video_size']);
				$args['crop_thumb'] = sanitize_text_field($_POST['overlay_crop']);
			}
			elseif ('flickr' === $provider) {
				if ('gallery' === $type) {
					$args['gallery_id'] = $components[4] . '-' . $components[5];
					$args['gallery_id_computed'] = true;
				}
				elseif ('set' === $type) {
					$args['photoset_id'] = $components[4];
				}
				$args['thumb_size'] = sanitize_text_field($_POST['overlay_size']);
				$args['video_size'] = sanitize_text_field($_POST['overlay_video_size']);
			}

			$gallery = new Gallery($args);
			echo wp_kses($gallery->get_contents(), self::$safe_tags);
		}
		die();
	}

	/**
	 * Clicking on the expander for a level 3 object (e.g. a Flickr Collection etc.) triggers this. This will fetch the nested level 2 objects and generate the corresponding markup.
	 * This is the hook for an AJAX-invoked call.
	 */
	public function display_level_3_contents() {
		// Cannot use a nonce here. Users often cache the gallery markup, which would cache the nonce. This would make it impossible to run this call after a certain amount of time.
		$node = sanitize_text_field($_POST['node']);
		$components = explode('-', $node);

		if (count($components) <= 3) {
			die();
		}

		$args = ['display' => 'in-page', 'headers' => '', 'layout' => sanitize_text_field($_POST['layout']), 'stream' => sanitize_text_field($_POST['stream'])];

		$provider = $components[0];
		if ('flickr' === $provider) {
			$args['collection_id'] = implode('-', array_slice($components, 2, 2, true));
			$args['user_id'] = $components[4];
			$args['type'] = 'flickr';
			$args['strip_top_level'] = 'remove';
			$gallery = new Gallery($args);
			echo wp_kses($gallery->get_contents(), self::$safe_tags);
		}
		die();
	}

	/**
	 * Constructs the CSS for a "background" option
	 *
	 * @param $option
	 * @return string
	 */
	private function get_bg_css($option) {
		global ${$option};
		$option_val = ${$option};
		if (!is_array($option_val)) {
			$val_array = [];
			$vals = explode(';', $option_val);
			foreach ($vals as $val) {
				if ('' === trim($val)) {
					continue;
				}
				$pair = explode('=', $val);
				$val_array[$pair[0]] = $pair[1];
			}
			$option_val = $val_array;
		}
		$bg_string = "";
		$bg_rgba_string = "";
		if (!isset($option_val['colortype']) || 'transparent' === $option_val['colortype']) {
			$bg_string .= " transparent ";
		}
		else {
			if (isset($option_val['color'])) {
				if ('#' === substr($option_val['color'], 0, 1)) {
					$color_string = sanitize_hex_color_no_hash(substr($option_val['color'], 1));
				}
				else {
					$color_string = sanitize_hex_color($option_val['color']);
				}
				$rgb_str_array = [];
				if (3 === strlen($color_string)) {
					$rgb_str_array[] = substr($color_string, 0, 1) . substr($color_string, 0, 1);
					$rgb_str_array[] = substr($color_string, 1, 1) . substr($color_string, 1, 1);
					$rgb_str_array[] = substr($color_string, 2, 1) . substr($color_string, 2, 1);
				}
				else {
					$rgb_str_array[] = substr($color_string, 0, 2);
					$rgb_str_array[] = substr($color_string, 2, 2);
					$rgb_str_array[] = substr($color_string, 4, 2);
				}
				$rgb_array = [];
				$rgb_array[] = hexdec($rgb_str_array[0]);
				$rgb_array[] = hexdec($rgb_str_array[1]);
				$rgb_array[] = hexdec($rgb_str_array[2]);
				$rgb_string = implode(',', $rgb_array);
				$rgb_string = ' rgb(' . $rgb_string . ') ';

				if (isset($option_val['trans'])) {
					$bg_rgba_string = $bg_string;
					$transparency = (int)$option_val['trans'];
					if (0 !== $transparency) {
						$trans_dec = $transparency / 100;
						$rgba_string = implode(',', $rgb_array);
						$rgba_string = ' rgba(' . $rgba_string . ',' . $trans_dec . ') ';
						$bg_rgba_string .= $rgba_string;
					}
				}

				$bg_string .= $rgb_string;
			}
		}
		if (isset($option_val['image']) && '' !== trim($option_val['image'])) {
			$bg_string .= " url(" . esc_url($option_val['image']) . ") ";
			$bg_string .= $option_val['position'] . " " . $option_val['repeat'];

			if ('' !== trim($bg_rgba_string)) {
				$bg_rgba_string .= " url(" . esc_url($option_val['image']) . ") ";
				$bg_rgba_string .= $option_val['position'] . " " . $option_val['repeat'];
			}
		}

		if ('' !== trim($bg_string)) {
			$bg_string = "background: " . $bg_string . " !important;\n";
			if ('' !== trim($bg_rgba_string)) {
				$bg_string .= "\tbackground: " . $bg_rgba_string . " !important;\n";
			}
		}
		return $bg_string;
	}

	/**
	 * Generates the CSS for borders. Each border, top, right, bottom and left is generated as a separate line.
	 *
	 * @param $option
	 * @return string
	 */
	private function get_border_css($option) {
		global ${$option};
		$option_val = ${$option};
		if (!is_array($option_val)) {
			$option_val = stripslashes($option_val);
			$edge_array = $this->build_edge_array($option_val);
			$option_val = $edge_array;
		}
		$border_string = '';
		foreach ($option_val as $edge => $selections) {
			$border_string .= "\tborder-$edge: ";
			if (!isset($selections['style'])) {
				$selections['style'] = 'none';
			}
			if ('none' === $selections['style']) {
				$border_string .= "none";
			}
			else {
				if (isset($selections['border-width'])) {
					$border_string .= $selections['border-width'];
				}
				if (isset($selections['border-width-type'])) {
					$border_string .= $selections['border-width-type'];
				}
				else {
					$border_string .= "px";
				}
				$border_string .= " " . $selections['style'] . " ";
				if ('transparent' === $selections['colortype']) {
					$border_string .= "transparent";
				}
				else {
					if ('#' === substr($selections['color'], 0, 1)) {
						$border_string .= $selections['color'];
					}
					else {
						$border_string .= '#' . $selections['color'];
					}
				}
			}
			$border_string .= ";\n";
		}
		return "\n" . $border_string;
	}

	public function build_edge_array($option_val) {
		$edge_array = [];
		$edges = explode('||', $option_val);
		foreach ($edges as $edge_val) {
			if ('' !== trim($edge_val)) {
				$edge_options = explode('::', trim($edge_val));
				if (is_array($edge_options) && count($edge_options) > 1) {
					$val_array = [];
					$vals = explode(';', $edge_options[1]);
					foreach ($vals as $val) {
						$pair = explode('=', $val);
						if (is_array($pair) && count($pair) > 1) {
							$val_array[$pair[0]] = $pair[1];
						}
					}
					$edge_array[$edge_options[0]] = $val_array;
				}
			}
		}
		return $edge_array;
	}

	/**
	 * Make an HTTP request
	 *
	 * @static
	 * @param $url
	 * @param string $method GET | POST | DELETE
	 * @param null $post_fields
	 * @param string $user_agent
	 * @param int $timeout
	 * @param bool $ssl_verify_peer
	 * @param array $headers
	 * @param array $cookies
	 * @return array|WP_Error
	 */
	public static function http($url, $method = 'POST', $post_fields = null, $user_agent = null, $timeout = 90, $ssl_verify_peer = false, $headers = [], $cookies = []) {
		$curl_args = [
			'user-agent' => $user_agent,
			'timeout'    => $timeout,
			'sslverify'  => $ssl_verify_peer,
			'headers'    => array_merge(['Expect:'], $headers),
			'method'     => $method,
			'body'       => $post_fields,
			'cookies'    => $cookies,
		];

		switch ($method) {
			case 'DELETE':
				if (!empty($post_fields)) {
					$url = "{$url}?{$post_fields}";
				}
				break;
		}

		$response = wp_remote_request($url, $curl_args);
		return $response;
	}

	public function obtain_token() {
		require_once PHOTONIC_PATH . "/Admin/Authentication.php";
		$auth = Authentication::get_instance();
		$auth->obtain_token();
		die();
	}

	/**
	 * Invoked via AJAX in the "Authentication" page, when the user clicks on "Save Token"
	 */
	public function save_token_in_options() {
		if (isset($_POST['provider']) && isset($_POST['token']) && check_ajax_referer($_POST['provider'] . '-save-token-' . $_POST['token']) && current_user_can('edit_theme_options')) {
			$provider = strtolower(sanitize_text_field($_POST['provider']));
			$token = sanitize_text_field($_POST['token']);
			$secret = sanitize_text_field($_POST['secret']);
			if (!empty($_POST['expires_in'])) {
				$expires_in = sanitize_text_field($_POST['expires_in']);
			}

			if (in_array($provider, ['flickr', 'smug', 'zenfolio', 'google', 'instagram'], true)) {
				$options = get_option('photonic_options');
				if (empty($options)) {
					$options = [];
				}
				$option_set = false;
				if (in_array($provider, ['flickr', 'smug', 'zenfolio'], true)) {
					$options[$provider . '_access_token'] = $token;
					$options[$provider . '_token_secret'] = $secret;
					$option_set = true;
				}
				elseif (in_array($provider, ['google'], true)) {
					$options[$provider . '_refresh_token'] = $token;
					$option_set = true;
				}
				elseif (in_array($provider, ['instagram'], true)) {
					$client_id = sanitize_text_field($_POST['client_id']);
					$user = sanitize_text_field($_POST['user']);

					$options[$provider . '_access_token'] = $token;

					$auth_token = [];
					$auth_token['oauth_token'] = $token;
					$auth_token['oauth_token_created'] = time();
					if (!empty($expires_in)) {
						$auth_token['oauth_token_expires'] = $expires_in;
					}
					$auth_token['client_id'] = $client_id;
					$auth_token['user'] = $user;

					self::save_provider_authentication($provider, $auth_token);

					$option_set = true;
				}

				if ($option_set) {
					update_option('photonic_options', $options);
					echo esc_url(admin_url('admin.php?page=photonic-options-manager')) . '&tab=' . esc_attr($this->provider_map[$provider]) . '.php';
				}
			}
		}
		die();
	}

	public function delete_token_from_options() {
		if (isset($_POST['provider']) && check_ajax_referer($_POST['provider'] . '-delete-token')) {
			$provider = strtolower(sanitize_text_field($_POST['provider']));
			if (in_array($provider, ['flickr', 'smug', 'zenfolio', 'google', 'instagram'], true) && current_user_can('edit_theme_options')) {
				$photonic_authentication = get_option('photonic_authentication');
				if ('zenfolio' === $provider) {
					if (isset($photonic_authentication[$provider])) {
						unset($photonic_authentication[$provider]);
					}
				}
				update_option('photonic_authentication', $photonic_authentication);
			}
		}
		die();
	}

	public function invoke_helper() {
		require_once PHOTONIC_PATH . "/Admin/Helper.php";
		$helper = new Helper();
		$helper->invoke_helper();
	}

	/**
	 * @return array
	 */
	public function get_localized_js_variables() {
		global $photonic_lightbox_no_loop, $photonic_slideshow_mode, $photonic_slideshow_interval,
			   $photonic_cb_transition_effect, $photonic_cb_transition_speed,
			   $photonic_fbox_title_position, $photonic_fb3_transition_effect, $photonic_fb3_transition_speed, $photonic_fb3_show_fullscreen, $photonic_enable_fb3_fullscreen,
			   $photonic_fb3_hide_thumbs, $photonic_enable_fb3_thumbnail, $photonic_fb3_disable_zoom, $photonic_fb3_disable_slideshow, $photonic_fb3_enable_download, $photonic_fb3_disable_right_click,
			   $photonic_lc_transition_effect, $photonic_lc_transition_speed_in, $photonic_lc_transition_speed_out, $photonic_lc_enable_shrink,
			   $photonic_lg_transition_effect, $photonic_lg_transition_speed, $photonic_disable_lg_download, $photonic_lg_hide_bars_delay,
			   $photonic_enable_lg_zoom, $photonic_enable_lg_thumbnail, $photonic_enable_lg_fullscreen, $photonic_enable_lg_autoplay,
			   $photonic_tile_spacing, $photonic_tile_min_height, $photonic_pphoto_theme, $photonic_pp_animation_speed,
			   $photonic_sp_download, $photonic_sp_hide_bars,
			   $photonic_enable_swipebox_mobile_bars, $photonic_sb_hide_mobile_close, $photonic_sb_hide_bars_delay,
			   $photonic_vb_display_vertical_scroll, $photonic_vb_title_position, $photonic_vb_title_style,
			   $photonic_lightbox_for_all, $photonic_lightbox_for_videos, $photonic_popup_panel_width, $photonic_deep_linking, $photonic_social_media,
			   $photonic_slideshow_prevent_autostart, $photonic_wp_slide_adjustment, $photonic_masonry_min_width, $photonic_mosaic_trigger_width,
			   $photonic_debug_on;

		global $photonic_js_variables;

		$lightgallery_plugins = [];
		if (!empty($photonic_enable_lg_autoplay)) {
			$lightgallery_plugins[] = 'autoplay';
		}
		if (!empty($photonic_enable_lg_fullscreen)) {
			$lightgallery_plugins[] = 'fullscreen';
		}
		if (!empty($photonic_enable_lg_thumbnail)) {
			$lightgallery_plugins[] = 'thumbnail';
		}
		if (!empty($photonic_enable_lg_zoom)) {
			$lightgallery_plugins[] = 'zoom';
		}
		$lightgallery_plugins = implode(',', $lightgallery_plugins);

		$slideshow_library = esc_js(self::$library);
		$js_array = [
			'ajaxurl'    => admin_url('admin-ajax.php'),
			'plugin_url' => PHOTONIC_URL,
			'debug_on'   => !empty($photonic_debug_on),

			'fbox_show_title'     => 'none' !== $photonic_fbox_title_position,
			'fbox_title_position' => 'none' === $photonic_fbox_title_position ? 'outside' : esc_js($photonic_fbox_title_position),

			'slide_adjustment' => esc_js($photonic_wp_slide_adjustment ?: 'adapt-height-width'),

			'deep_linking' => isset($photonic_deep_linking) ? esc_js($photonic_deep_linking) : 'none',
			'social_media' => isset($photonic_deep_linking) ? 'none' !== $photonic_deep_linking && empty($photonic_social_media) : '',

			'lightbox_library'     => $slideshow_library,
			'tile_spacing'         => (empty($photonic_tile_spacing) || !absint($photonic_tile_spacing)) ? 0 : absint($photonic_tile_spacing),
			'tile_min_height'      => (empty($photonic_tile_min_height) || !absint($photonic_tile_min_height)) ? 200 : absint($photonic_tile_min_height),
			'masonry_min_width'    => (empty($photonic_masonry_min_width) || !absint($photonic_masonry_min_width)) ? 200 : absint($photonic_masonry_min_width),
			'mosaic_trigger_width' => (empty($photonic_mosaic_trigger_width) || !absint($photonic_mosaic_trigger_width)) ? 200 : absint($photonic_mosaic_trigger_width),

			'slideshow_mode'     => (isset($photonic_slideshow_mode) && 'on' === $photonic_slideshow_mode) ? true : false,
			'slideshow_interval' => (isset($photonic_slideshow_interval) && absint($photonic_slideshow_interval)) ? absint($photonic_slideshow_interval) : 5000,
			'lightbox_loop'      => empty($photonic_lightbox_no_loop),

			'gallery_panel_width' => (empty($photonic_popup_panel_width) || !absint($photonic_popup_panel_width) || absint($photonic_popup_panel_width) > 100) ? 80 : absint($photonic_popup_panel_width),

			'cb_transition_effect' => esc_js($photonic_cb_transition_effect ?: 'elastic'),
			'cb_transition_speed'  => (isset($photonic_cb_transition_speed) && absint($photonic_cb_transition_speed)) ? absint($photonic_cb_transition_speed) : 350,

			'fb3_transition_effect'   => esc_js($photonic_fb3_transition_effect ?: 'zoom'),
			'fb3_transition_speed'    => (isset($photonic_fb3_transition_speed) && absint($photonic_fb3_transition_speed)) ? absint($photonic_fb3_transition_speed) : 366,
			'fb3_fullscreen_button'   => !empty($photonic_fb3_show_fullscreen),
			'fb3_fullscreen'          => isset($photonic_enable_fb3_fullscreen) && 'on' === $photonic_enable_fb3_fullscreen ? true : false,
			'fb3_thumbs_button'       => empty($photonic_fb3_hide_thumbs),
			'fb3_thumbs'              => isset($photonic_enable_fb3_thumbnail) && 'on' === $photonic_enable_fb3_thumbnail ? true : false,
			'fb3_zoom'                => empty($photonic_fb3_disable_zoom),
			'fb3_slideshow'           => empty($photonic_fb3_disable_slideshow),
			'fb3_download'            => !empty($photonic_fb3_enable_download),
			'fb3_disable_right_click' => !empty($photonic_fb3_disable_right_click),

			'lc_transition_effect'    => esc_js($photonic_lc_transition_effect ?: 'scrollHorizontal'),
			'lc_transition_speed_in'  => (isset($photonic_lc_transition_speed_in) && absint($photonic_lc_transition_speed_in)) ? absint($photonic_lc_transition_speed_in) : 350,
			'lc_transition_speed_out' => (isset($photonic_lc_transition_speed_out) && absint($photonic_lc_transition_speed_out)) ? absint($photonic_lc_transition_speed_out) : 250,
			'lc_disable_shrink'       => empty($photonic_lc_enable_shrink),

			'lg_plugins'           => $lightgallery_plugins,
			'lg_transition_effect' => esc_js($photonic_lg_transition_effect ?: 'lg-slide'),
			'lg_enable_download'   => empty($photonic_disable_lg_download),
			'lg_hide_bars_delay'   => (isset($photonic_lg_hide_bars_delay) && absint($photonic_lg_hide_bars_delay)) ? absint($photonic_lg_hide_bars_delay) : 6000,
			'lg_transition_speed'  => (isset($photonic_lg_transition_speed) && absint($photonic_lg_transition_speed)) ? absint($photonic_lg_transition_speed) : 600,

			'pphoto_theme'           => isset($photonic_pphoto_theme) ? esc_js($photonic_pphoto_theme) : 'pp_default',
			'pphoto_animation_speed' => esc_js($photonic_pp_animation_speed ?: 'fast'),

			'sp_download'  => !empty($photonic_sp_download),
			'sp_hide_bars' => $photonic_sp_hide_bars,

			'enable_swipebox_mobile_bars' => !empty($photonic_enable_swipebox_mobile_bars),
			'sb_hide_mobile_close'        => !empty($photonic_sb_hide_mobile_close),
			'sb_hide_bars_delay'          => (isset($photonic_sb_hide_bars_delay) && absint($photonic_sb_hide_bars_delay)) ? absint($photonic_sb_hide_bars_delay) : 0,

			'vb_disable_vertical_scroll' => empty($photonic_vb_display_vertical_scroll),
			'vb_title_position'          => esc_js($photonic_vb_title_position),
			'vb_title_style'             => esc_js($photonic_vb_title_style),

			'lightbox_for_all'    => !empty($photonic_lightbox_for_all),
			'lightbox_for_videos' => !empty($photonic_lightbox_for_videos),

			'slideshow_autostart' => !(isset($photonic_slideshow_prevent_autostart) && 'on' === $photonic_slideshow_prevent_autostart),

			'password_failed'    => esc_attr__('This album is password-protected. Please provide a valid password.', 'photonic'),
			'incorrect_password' => esc_attr__('Incorrect password.', 'photonic'),
			'maximize_panel'     => esc_attr__('Show', 'photonic'),
			'minimize_panel'     => esc_attr__('Hide', 'photonic'),
		];
		$photonic_js_variables = $js_array;
		return $js_array;
	}

	public function enable_translations() {
		load_plugin_textdomain('photonic', false, false);
	}

	public function load_more() {
		// Cannot use a nonce here. Users often cache the gallery markup, which would cache the nonce. This would make it impossible to run this call after a certain amount of time.
		$provider = sanitize_text_field($_POST['provider']);
		$query = sanitize_text_field($_POST['query']);
		$attr = wp_parse_args($query);

		$attr['type'] = $provider;
		if ('flickr' === $provider) {
			$attr['page'] = isset($attr['page']) ? $attr['page'] + 1 : 0;
		}
		elseif ('smug' === $provider) {
			$attr['start'] = $attr['start'] + $attr['count'];
		}
		elseif ('zenfolio' === $provider) {
			$attr['offset'] = $attr['offset'] + $attr['limit'];
		}
		elseif ('wp' === $provider) {
			$attr['page'] = $attr['page'] + 1;
		}
		elseif ('google' !== $provider && 'instagram' !== $provider) {
			unset($attr['type']);
		}

		if (!empty($attr['type'])) {
			$gallery = new Gallery($attr);
			echo wp_kses($gallery->get_contents(), self::$safe_tags);
		}
		die();
	}

	public function helper_shortcode_more() {
		if (!empty($_POST['provider'])) {
			$provider = sanitize_text_field($_POST['provider']);
			if (in_array($provider, ['google'], true)) {
				$attr = ['type' => $provider];
				if ('google' === $provider) {
					$attr['nextPageToken'] = sanitize_text_field($_POST['nextPageToken']);
					$attr['album_type'] = sanitize_text_field($_POST['access']);
					$gallery = new Gallery($attr);
					echo wp_kses($gallery->get_helper_contents(), self::$safe_tags);
				}
			}
		}
		die();
	}

	/**
	 * @param string|array $classes
	 * @return array
	 */
	public function body_class($classes = []) {
		if (!is_array($classes)) {
			$classes = explode(' ', $classes);
		}

		return $classes;
	}

	/**
	 * Used for handling the front-end for Gutenberg blocks
	 *
	 * @param $attributes
	 * @return array|bool|string
	 */
	public function render_block($attributes) {
		if (!empty($attributes['shortcode'])) {
			$shortcode = (array) (json_decode($attributes['shortcode']));

			if (!empty($attributes['align'])) {
				$shortcode['alignment'] = $attributes['align'];
			}
			if (!empty($attributes['className'])) {
				$shortcode['custom_classes'] = $attributes['className'];
			}

			$this->conditionally_add_scripts();
			return $this->get_gallery_images($shortcode);
		}
		return '';
	}

	private function add_gutenberg_support() {
		if (function_exists('register_block_type')) {
			register_block_type(
				'photonic/gallery',
				[
					'attributes'      => [
						'shortcode' => [
							'type' => 'string',
						],
					],
					'render_callback' => [&$this, 'render_block'],
				]
			);
		}
	}

	public function dismiss_warning() {
		$user_id = get_current_user_id();
		$response = [];
		if (!empty($_POST['dismissible']) && check_ajax_referer('dismiss-warning-' . $user_id)) {
			add_user_meta($user_id, "photonic_" . sanitize_text_field($_POST['dismissible']), 'true', true);
			$response[$_POST['dismissible']] = 'true';
		}
		echo wp_json_encode($response);
		die();
	}

	public function curl_timeout($handle) {
		curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, PHOTONIC_CURL_TIMEOUT);
		curl_setopt($handle, CURLOPT_TIMEOUT, PHOTONIC_CURL_TIMEOUT < 30 ? 30 : PHOTONIC_CURL_TIMEOUT);
	}

	public static function log($element) {
		if (PHOTONIC_DEBUG) {
			echo wp_kses($element, self::$safe_tags);
		}
	}

	public static function doc_link($link) {
		return ' ' . sprintf(esc_html__('See %1$shere%2$s for documentation.', 'photonic'), "<a href='$link'>", '</a>');
	}

	public function get_lazy_load_button($attr = [], $button_type = 'show_gallery') {
		$types = [
			'show_gallery' => 'show_gallery',
			'js_load'      => 'load_mode'
		];
		$type = $types[$button_type];
		$button = esc_attr($attr[$type]);
		$button_attr = [];

		if (!empty($attr["{$type}_button_type"]) && 'image' === $attr["{$type}_button_type"] && !empty($attr["{$type}_button_image"])) {
			$button_attr['type'] = 'image';
			$button_attr['alt'] = $button;
			$button_attr['src'] = esc_url($attr["{$type}_button_image"]);
		}
		else {
			$button_attr['type'] = 'button';
			$button_attr['value'] = $button;
		}

		$class = str_replace('_', '-', $button_type);
		$button_attr['class'] = "photonic-{$class}-button";

		unset($attr["{$type}"]);
		unset($attr["{$type}_button_type"]);
		unset($attr["{$type}_button_image"]);

		$attr['load_mode'] = 'php'; // doesn't matter what the $button_type is.

		$attr_str = http_build_query($attr);
		$button_attr['data-photonic-shortcode'] = $attr_str;

		$input_attr = [];
		foreach ($button_attr as $name => $value) {
			$input_attr[] = "$name='$value'";
		}
		$input_attr = implode(' ', $input_attr);

		return "<input $input_attr/>";
	}

	public function lazy_load() {
		// $_POST['shortcode'] only contains the parameters of a URL, to be passed to photonic after being broken down. Sanitization functions are killing
		// characters such as "@" (used in Flickr user ids) or its escaped form. So we use esc_url_raw.
		// However, esc_url_raw needs a domain, so we prepend a random one, sanitize it, then pull out only the 'query' part from it.
		$shortcode = esc_url_raw('http://randomurl.com?' . $_POST['shortcode']);
		$shortcode_parse = wp_parse_url($shortcode);
		parse_str($shortcode_parse['query'], $attr);

		$images = $this->get_gallery_images($attr);
		// echo $images;
		echo wp_kses($images, self::$safe_tags);
		die();
	}

	public function load_widget() {
		require_once PHOTONIC_PATH . '/Add_Ons/WP/Widget.php';
		register_widget("Photonic_Plugin\Add_Ons\WP\Widget");
	}

	public static function enqueue_widget_scripts() {
		global $photonic_alternative_shortcode;
		$js_array = [
			'ajaxurl'           => admin_url('admin-ajax.php'),
			'shortcode'         => esc_js($photonic_alternative_shortcode ?: 'gallery'),
			'current_shortcode' => esc_html__('Current shortcode', 'photonic'),
			'edit_message'      => esc_html__('Click on the icon to edit your gallery.', 'photonic'),
		];
		wp_enqueue_script('photonic-widget', PHOTONIC_URL . 'include/js/admin/widget.js', ['jquery'], self::get_version(PHOTONIC_PATH . '/include/js/admin/widget.js'), true);
		wp_localize_script('photonic-widget', 'Photonic_Widget_JS', $js_array);
		wp_enqueue_style('photonic-widget', PHOTONIC_URL . 'include/css/admin/widget.css', [], self::get_version(PHOTONIC_PATH . '/include/css/admin/widget.css'));
	}

	public function add_script_type($tag, $handle, $src) {
		if ('photonic' !== $handle && 'photonic-esm' !== $handle && 'photoswipe5' !== $handle && 'photoswipe5-lightbox' !== $handle) {
			return $tag;
		}

		if ('photonic' === $handle) {
			global $photonic_js_type;
			if ('photonic-esm' === $handle || 'modules' === $photonic_js_type || 'raw' === $photonic_js_type) {
				// change the script tag by adding type="module" and return it.
				$tag = "<script type='module' src='" . esc_url($src) . "'></script>\n";
			}
			elseif ('photonic' === $handle && 'all' === $photonic_js_type) {
				// change the script tag by adding "nomodule" and return it.
				$tag = "<script nomodule src='" . esc_url($src) . "'></script>\n";
			}
			elseif ('photonic' === $handle && 'transpiled' === $photonic_js_type) {
				// change the script tag by adding "nomodule" and return it.
				$tag = "<script src='" . esc_url($src) . "'></script>\n";
			}
		}
		else {
			// change the script tag by adding type="module" and return it.
			$tag = "<script type='module' src='" . esc_url($src) . "'></script>\n";
		}
		return $tag;
	}

	public function monitor_token_validity() {
		require_once PHOTONIC_PATH . '/Core/Cron.php';
		new Cron();
	}

	/**
	 * Custom style attributes, primarily required for the Justified Grid layout. Since <code>wp_kses_post</code> strips out tags that it doesn't recognize, we have this.
	 *
	 * @param $styles
	 * @return mixed
	 */
	public function safe_styles($styles) {
		$styles[] = '--dw';
		$styles[] = '--dh';
		$styles[] = '--tile-min-height';
		$styles[] = 'display';
		return $styles;
	}
}