Your IP : 216.73.216.162


Current Path : /home/x/b/o/xbodynamge/namtation/wp-content/
Upload File :
Current File : /home/x/b/o/xbodynamge/namtation/wp-content/image-cdn.tar

init.php000066600000030700151136221570006227 0ustar00<?php

/**
 * The Orbit Fox Image CDN Module.
 *
 * @link       https://themeisle.com
 * @since      1.0.0
 *
 * @package    Image_CDN_OBFX_Module
 */

/**
 * The class defines a new module to be used by Orbit Fox plugin.
 *
 * @package    Image_CDN_OBFX_Module
 * @author     Themeisle <friends@themeisle.com>
 */
class Image_CDN_OBFX_Module extends Orbit_Fox_Module_Abstract {
	/**
	 * Dashboard related data.
	 *
	 * @var null|array Dashboard related data.
	 */
	protected $connect_data = null;
	/**
	 * @var \OrbitFox\Connector $connector Orbitfox Api connector.
	 */
	private $connector;

	/**
	 * Image_CDN_OBFX_Module constructor.
	 *
	 * @since   1.0.0
	 * @access  public
	 */
	public function __construct() {
		parent::__construct();
		if ( isset( $_GET['loggedin'] ) && $_GET['loggedin'] == 'true' ) {
			$this->show = true;
		}
		$this->no_save     = true;
		$this->name        = sprintf( __( 'Image Optimization &amp; CDN Module <sup class="obfx-title-new">NEW</sup>', 'themeisle-companion' ) );
		$this->description = sprintf( __( 'Let us take care of your images sizes. With this feature we\'ll compress and resize every image on your website. <i>This service is powered by <b>%sOptimole%s</b></i>. <br/> <strong>* Requires account on orbitfox.com</strong>', 'themeisle-companion' ), '<a href="https://optimole.com" class="obfx-no-link" target="_blank">', '</a>' );
		add_action( 'obfx_module_tile_after', [ $this, 'tryout' ], 10, 2 );
	}

	public function tryout( $slug = '', $active = false ) {
		if ( $slug !== 'image-cdn' ) {
			return;
		}
		if ( $active ) {
			return;
		}
		echo sprintf( __( '%sTest your site%s','themeisle-companion' ), '<span class="obfx-tryout-message"><a href="' . esc_url( sprintf( 'https://speedtest.optimole.com/?url=%s', get_site_url() ) ) . '" target="_blank">', '</a></span>' );
	}

	/**
	 * Determine if module should be loaded.
	 *
	 * @since   1.0.0
	 * @access  public
	 * @return bool
	 */
	public function enable_module() {
		return true;
	}

	/**
	 * The loading logic for the module.
	 *
	 * @since   1.0.0
	 * @access  public
	 */
	public function load() {
	}

	/**
	 * Add top admin bar notice of traffic quota/usage.
	 *
	 * @param WP_Admin_Bar $wp_admin_bar Admin bar resource.
	 */
	public function add_traffic_node( $wp_admin_bar ) {
		if ( ! is_user_logged_in() ) {
			return;
		}
		$obfx_user_data = $this->get_api_data();
		$args           = array(
			'id'    => 'obfx_img_quota',
			'title' => 'OrbitFox' . __( ' Image Traffic', 'themeisle-companion' ) . ': ' . number_format( floatval( ( $obfx_user_data['image_cdn']['usage'] / 1000 ) ), 3 ) . ' / ' . number_format( floatval( ( $obfx_user_data['image_cdn']['quota'] / 1000 ) ), 0 ) . 'GB',
			'href'  => 'https://dashboard.orbitfox.com/',
			'meta'  => array( 'target' => '_blank' )
		);
		$wp_admin_bar->add_node( $args );

	}

	/**
	 * Return api data.
	 *
	 * @return mixed|string APi data.
	 */
	private function get_api_data() {

		if ( ! $this->get_is_active() ) {
			return '';
		}

		return class_exists( '\OrbitFox\Connector' ) ? get_option( \OrbitFox\Connector::API_DATA_KEY, '' ) : '';
	}

	/**
	 * Render data from dashboard of orbitfox.com.
	 */
	public function render_connect_data( $html ) {

		$obfx_user_data = $this->get_api_data();
		$class          = '';
		if ( ! empty( $obfx_user_data ) ) {
			$class = 'obfx-img-logged-in';
		}
		$display_name = isset( $obfx_user_data['display_name'] ) ? $obfx_user_data['display_name'] : '';
		$usage        = ( isset( $obfx_user_data['image_cdn'] ) && isset( $obfx_user_data['image_cdn']['usage'] ) ) ? $obfx_user_data['image_cdn']['usage'] : 0;
		$quota        = ( isset( $obfx_user_data['image_cdn'] ) && isset( $obfx_user_data['image_cdn']['quota'] ) ) ? $obfx_user_data['image_cdn']['quota'] : 0;
		$html         = '<div class="obfx-img-logged-in-data obfx-loggedin-show ' . $class . '" > ';
		$html         .= '<h5>' . __( 'Logged in as', 'themeisle-companion' ) . ' : <b id="obfx-img-display-name">' . esc_attr( $display_name ) . '</b></h5>';
		$html         .= '<p>' . __( 'Your private CDN url', 'themeisle-companion' ) . ' : <code id="obfx-img-cdn-url">' . $this->get_cdn_url() . '</code></p> ';
		$html         .= '<p>' . __( 'This month traffic usage', 'themeisle-companion' ) . ' : <code id="obfx-img-traffic-usage">' . number_format( floatval( ( $usage / 1000 ) ), 3 ) . ' GB</code>';
		$html         .= ' ' . __( 'Your traffic quota', 'themeisle-companion' ) . ' : <code class="obfx-img-traffic-quota">' . number_format( floatval( ( $quota / 1000 ) ), 3 ) . ' GB / month</code></p>';
		$html         .= '<p><i>' . __( 'You can use our image service and CDN in the limit of <span class="obfx-img-traffic-quota">', 'themeisle-companion' ) . number_format( floatval( ( $quota / 1000 ) ), 0 ) . '</span> per month.  </i></p>';
		$html         .= '</div>';

		return $html;
	}

	/**
	 * Get CDN private url.
	 *
	 * @return string Get CDN private url.
	 */
	private function get_cdn_url() {
		$obfx_user_data = $this->get_api_data();
		if ( empty( $obfx_user_data ) ) {
			return '';
		}
		if ( ! isset( $obfx_user_data['image_cdn']['key'] ) ) {
			return '';
		}

		return sprintf( '%s.i.optimole.com', strtolower( $obfx_user_data['image_cdn']['key'] ) );

	}

	/**
	 * Method that returns an array of scripts and styles to be loaded
	 * for the front end part.
	 *
	 * @since   1.0.0
	 * @access  public
	 * @return array
	 */
	public function public_enqueue() {
		return array();
	}

	/**
	 * Method that returns an array of scripts and styles to be loaded
	 * for the admin part.
	 *
	 * @since   1.0.0
	 * @access  public
	 * @return array|boolean
	 */
	public function admin_enqueue() {
		return array();
	}

	/**
	 * Options array for the Orbit Fox module.
	 *
	 * @return array
	 */
	public function options() {
		//Hack to allow binding of img module connect button as the view for the module options is loaded either if the module is active or not.

		//TODO Remove this when we have a way of loading module options async.
		if ( is_admin() ) {
			$this->hooks();
		}
		$obfx_user_data = $this->get_api_data();
		if ( empty( $obfx_user_data ) ) {
			$this->set_option( 'obfx_connect_api_key', '' );
			$this->set_option( 'enable_cdn_replacer', '0' );

		}
		$class = '';
		if ( ! empty( $obfx_user_data ) ) {
			$class = 'obfx-img-logged-in';
		}
		$fields   = array(
			array(
				'type'  => 'title',
				'name'  => 'Tooltip',
				'title' => sprintf( 'In order to get access to free image optimization service you will need an account on <a href="https://dashboard.orbitfox.com/register" target="_blank">orbitfox.com</a>. You will get access to our image optimization and CDN service for free in the limit of 1GB traffic per month.<br/> <i>Note: This is just a basic integration of the <a href="%s" target="_blank"> Optimole </a> service. For a more in-depth experience, try out the dedicated <a href="%s" target="_blank" >plugin</a>.</i>', 'https://optimole.com', 'http://wordpress.org/plugins/optimole-wp' )
			),
			array(
				'id'          => 'obfx_connect_api_key',
				'name'        => 'obfx_connect_api_key',
				'type'        => 'password',
				'default'     => isset( $obfx_user_data['api_key'] ) ? $obfx_user_data['api_key'] : '',
				'placeholder' => __( 'Your OrbitFox api key', 'themeisle-companion' ),
				'text'        => '<span class="dashicons dashicons-share"></span>' . __( 'Connect with Orbitfox', 'themeisle-companion' ),
			),
			array(
				'id'     => 'obfx-register-service',
				'type'   => 'link',
				'target' => '_blank',
				'class'  => 'obfx-loggedin-hide ' . $class,
				'url'    => 'https://dashboard.orbitfox.com/register',
				'text'   => ' Sign-Up for your API key',
				'name'   => 'obfx-register-service',
			),

		);
		$fields[] = array(
			'id'         => 'obfx_connect',
			'name'       => 'obfx_connect',
			'type'       => 'link',
			'url'        => '#',
			'class'      => 'obfx-loggedin-hide ' . $class,
			'link-class' => 'btn btn-success',
			'text'       => '<span class="dashicons dashicons-share"></span>' . __( 'Connect to OrbitFox service', 'themeisle-companion' ),
		);
		$fields[] = array(
			'type' => 'custom',
			'id'   => 'cdn_logged_in_data',
			'name' => 'cdn_logged_in_data',
		);
		$fields[] = array(
			'id'         => 'obfx_disconnect',
			'name'       => 'obfx_disconnect',
			'type'       => 'link',
			'class'      => 'obfx-loggedin-show ' . $class,
			'url'        => '#',
			'link-class' => 'btn btn-danger float-right  mb-10 obfx-img-logout ',
			'text'       => '<span class="dashicons dashicons-share"></span>' . __( 'Clear OrbitFox API key', 'themeisle-companion' ),
		);

		$fields[] = array(
			'id'      => 'enable_cdn_replacer',
			'title'   => '',
			'name'    => 'enable_cdn_replacer',
			'type'    => 'toggle',
			'class'   => ' obfx-img-cdn-replacer-switch obfx-loggedin-show ' . $class,
			'label'   => 'Serve all images optimised through OrbitFox CDN for a boost in speed.',
			'default' => '0',
		);

		return $fields;
	}

	/**
	 * Method to define hooks needed.
	 *
	 * @since   1.0.0
	 * @access  public
	 */
	public function hooks() {
		/**
		 * Init the connector object and load deps.
		 */
		require_once __DIR__ . '/inc/class-request.php';
		require_once __DIR__ . '/inc/class-orbit-fox-connector.php';
		add_filter( 'obfx_custom_control_cdn_logged_in_data', array( $this, 'render_connect_data' ) );
		$this->connector = \OrbitFox\Connector::instance();

		$this->loader->add_action( 'rest_api_init', $this, 'register_url_endpoints' );
		if ( ! $this->get_is_active() ) {
			$this->set_option( 'enable_cdn_replacer', '0' );
			$this->clear_api_data();
		}
		/**
		 * Load the image replacement logic if we are on the frontend,
		 * connected to the api and the replacement options is on.
		 */
		if ( ! is_admin() && $this->is_replacer_enabled() && $this->is_connected() ) {
			require_once __DIR__ . '/inc/class-orbit-fox-image-replacer.php';
			\OrbitFox\Image_CDN_Replacer::instance();
			$this->loader->add_filter( 'wp_resource_hints', $this, 'add_dns_prefetch', 10, 2 );

		}
		/**
		 * Adds top admin bar notice of traffic, if the module is connected.
		 */
		if ( $this->is_connected() ) {
			$this->loader->add_action( 'obfx_img_quota_sync', $this->connector, 'daily_check' );

			if ( ! wp_next_scheduled( 'obfx_img_quota_sync' ) ) {
				wp_schedule_event( time() + 10, 'daily', 'obfx_img_quota_sync', array() );
			}
			$this->loader->add_action( 'admin_bar_menu', $this, 'add_traffic_node', 9999 );
		}

	}

	/**
	 * Return api data.
	 *
	 * @return mixed|string APi data.
	 */
	private function clear_api_data() {


		return update_option( \OrbitFox\Connector::API_DATA_KEY, '' );
	}

	/**
	 * Check if the image replacement is enabled.
	 *
	 * @return bool Connection status.
	 */
	private function is_replacer_enabled() {
		if ( ! $this->get_is_active() ) {
			return false;
		}
		$enabled = $this->get_option( 'enable_cdn_replacer' );

		return boolval( $enabled );

	}

	/**
	 * Check if the module is connected to the api.
	 *
	 * @return bool Connection status.
	 */
	private function is_connected() {

		$obfx_user_data = $this->get_api_data();

		return ! empty( $obfx_user_data );

	}

	/**
	 * Adds cdn url for prefetch.
	 *
	 * @param array  $hints Hints array.
	 * @param string $relation_type Type of relation.
	 *
	 * @return array Altered hints array.
	 */
	public function add_dns_prefetch( $hints, $relation_type ) {
		if ( 'dns-prefetch' !== $relation_type ) {
			return $hints;
		}
		$cdn_url = $this->get_cdn_url();
		if ( empty( $cdn_url ) ) {
			return $hints;
		}
		$hints[] = sprintf( '//%s', $cdn_url );

		return $hints;
	}

	/**
	 * Update replacer callback.
	 */
	public function update_replacer( WP_REST_Request $request ) {
		$flag = $request->get_param( 'update_replacer' );
		$this->set_option( 'enable_cdn_replacer', $flag === 'yes' ? '1' : '0' );

		return new WP_REST_Response( 'Replacer updated' );
	}

	/**
	 * Register module rest methods.
	 */
	public function register_url_endpoints() {
		register_rest_route(
			'obfx', '/connector-url', array(
				array(
					'methods'             => \WP_REST_Server::CREATABLE,
					'permission_callback' => function ( \WP_REST_Request $request ) {
						return current_user_can( 'manage_options' );
					},
					'callback'            => array( $this->connector, 'rest_handle_connector_url' ),
				),
			)
		);
		register_rest_route(
			'obfx', '/update_replacer', array(
				array(
					'methods'             => \WP_REST_Server::CREATABLE,
					'permission_callback' => function ( \WP_REST_Request $request ) {
						return current_user_can( 'manage_options' );
					},
					'callback'            => array( $this, 'update_replacer' ),
				),
			)
		);
	}

}
inc/class-orbit-fox-connector.php000066600000014717151136221570013053 0ustar00<?php

namespace OrbitFox;

/**
 * The class defines way of connecting this user to the OrbitFox Dashboard.
 *
 * @package    \OrbitFox\Connector
 * @author     Themeisle <friends@themeisle.com>
 */
class Connector {
	/**
	 * Option key name for OrbitFox site account.
	 */
	const API_DATA_KEY = 'obfx_connect_data';

	/**
	 * The instance object.
	 *
	 * @var Connector
	 */
	protected static $instance = null;

	/**
	 * The Root URL of the OrbitFox dashboard.
	 *
	 * @var string
	 */
	protected $connect_url = 'https://dashboard.orbitfox.com/api/obfxhq/v1';

	/**
	 * The CDN details path.
	 *
	 * @var string
	 */
	protected $cdn_path = '/image/details';

	/**
	 * The instance init method.
	 *
	 * @static
	 * @since 1.0.0
	 * @access public
	 * @return Connector
	 */
	public static function instance() {
		if ( is_null( self::$instance ) ) {
			self::$instance = new self();
			self::$instance->init();
		}

		return self::$instance;
	}

	/**
	 * Init hooks.
	 */
	function init() {
		$this->connect_url = apply_filters( 'obfx_dashboard_url', $this->connect_url );

		add_action( 'admin_footer', array( $this, 'admin_inline_js' ) );
	}


	/**
	 * Sync quota data.
	 */
	public function daily_check() {

		$current_data = get_option( self::API_DATA_KEY );
		if ( empty( $current_data ) ) {
			return;
		}
		if ( ! isset( $current_data['api_key'] ) || empty( $current_data['api_key'] ) && strlen( $current_data['api_key'] ) > 10 ) {
			return;
		}
		$request             = new \OrbitFox\Request( $this->connect_url . $this->cdn_path, 'POST', $current_data['api_key'] );
		$response            = $request->get_response();
		$response['api_key'] = $current_data['api_key'];

		update_option( self::API_DATA_KEY, $response );

	}

	/**
	 * When a user requests an url we request a set of temporary token credentials and build a link with them.
	 * We also save them because we'll need them with the verifier.
	 *
	 * @return \WP_REST_Response|\WP_Error The connection handshake.
	 */
	public function rest_handle_connector_url( \WP_REST_Request $request ) {
		$disconnect_flag = $request->get_param( 'disconnect' );
		if ( ! empty( $disconnect_flag ) ) {
			delete_option( self::API_DATA_KEY );

			return new \WP_REST_Response( array( 'code' => 200, 'message' => 'Disconnected' ), 200 );
		}
		$api_key = $request->get_param( 'api_key' );
		if ( empty( $api_key ) ) {
			return new \WP_REST_Response( array( 'code' => 'error', 'data' => 'Empty api key provided' ) );
		}
		$request = new \OrbitFox\Request( $this->connect_url . $this->cdn_path, 'POST', $api_key );

		$response = $request->get_response();

		if ( $response === false ) {
			return new \WP_REST_Response(
				array(
					'code'    => 'error',
					'message' => 'Error connecting to the OrbitFox api. Invalid API key.',
				)
			);
		}
		$response['api_key'] = $api_key;
		update_option( self::API_DATA_KEY, $response );

		return new \WP_REST_Response( array( 'code' => 'success', 'data' => $response ), 200 );
	}

	/**
	 * Print the inline script which get's the url for the Connector button.
	 */
	function admin_inline_js() {
		$connect_endpoint = get_rest_url( null, 'obfx/connector-url' );
		$update_replacer  = get_rest_url( null, 'obfx/update_replacer' );
		wp_enqueue_script( 'wp-api' ); ?>
		<script type='text/javascript'>
			(function ($) {
				$('#obfx_connect').on('click', function (event) {
					event.preventDefault();

					$('#obfx_connect').parent().addClass('loading');
					var api_key = $('#obfx_connect_api_key').val();

					wp.apiRequest({
						url: "<?php echo $connect_endpoint; ?>",
						type: 'POST',
						data: {api_key: api_key},
						dataType: 'json'
					}).done(function (response) {
						$("#obfx-error-api").remove();
						$('#obfx_connect').parent().removeClass('loading');
						var elements = $("#obfx-module-form-image-cdn .obfx-loggedin-show, #obfx-module-form-image-cdn .obfx-loggedin-hide");
						switch (response.code) {

							case 'error':
								$("#obfx-module-form-image-cdn").append('<p class="label label-error" id="obfx-error-api">' + response.message + '</p>');
								elements.removeClass('obfx-img-logged-in');
								break;
							case 'success':

								$("#obfx_connect_api_key").val(response.data.api_key);
								$("#obfx-img-display-name").text(response.data.display_name);
								$("#obfx-img-cdn-url").text(response.data.image_cdn.key + '.i.optimole.com');
								$("#obfx-img-traffic-usage").text((parseInt(response.data.image_cdn.usage) / 1000).toFixed(3) + 'GB');
								$(".obfx-img-traffic-quota").text((parseInt(response.data.image_cdn.quota) / 1000).toFixed(0) + 'GB');
								elements.addClass('obfx-img-logged-in');
								break;
						}

					}).fail(function (e) {
						$('#obfx_connect').parent().removeClass('loading');
					});
				});

				$("input[name='enable_cdn_replacer'").on('change', function (event) {
					event.preventDefault();
					var flag_replacer = $(this).is(":checked");
					wp.apiRequest({
						url: "<?php echo $update_replacer; ?>",
						type: 'POST',
						data: {update_replacer: flag_replacer ? 'yes' : 'no'},
						dataType: 'json'
					}).done(function(){
						$("#obfx-module-form-image-cdn").append('<p class="label label-success" id="obfx-feedback-api">Image replacer option saved.</p>');
						setTimeout(function(){
							$("#obfx-feedback-api").remove();
						},1000);
					});
				});
				$('#obfx_disconnect').on('click', function (event) {
					event.preventDefault();
					$('#obfx_connect').parent().addClass('loading');
					wp.apiRequest({
						url: "<?php echo $connect_endpoint; ?>",
						type: 'POST',
						data: {disconnect: true},
						dataType: 'json'
					}).done(function (response) {
						location.reload();
					}).fail(function (e) {
						$('#obfx_disconnect').parent().removeClass('loading');
					});
				});

			})(jQuery)
		</script>
		<?php
	}


	/**
	 * Throw error on object clone
	 *
	 * The whole idea of the singleton design pattern is that there is a single
	 * object therefore, we don't want the object to be cloned.
	 *
	 * @access public
	 * @since 1.0.0
	 * @return void
	 */
	public function __clone() {
		// Cloning instances of the class is forbidden.
		_doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin&#8217; huh?', 'themeisle-companion' ), '1.0.0' );
	}

	/**
	 * Disable unserializing of the class
	 *
	 * @access public
	 * @since 1.0.0
	 * @return void
	 */
	public function __wakeup() {
		// Unserializing instances of the class is forbidden.
		_doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin&#8217; huh?', 'themeisle-companion' ), '1.0.0' );
	}
}
inc/class-orbit-fox-image-replacer.php000066600000045702151136221570013734 0ustar00<?php

namespace OrbitFox;

/**
 * The class handles the image replacements and optimizations.
 *
 * @package    \OrbitFox\Image_CDN_Replacer
 * @author     Themeisle <friends@themeisle.com>
 */
class Image_CDN_Replacer {
	/**
	 * Cached object instance.
	 *
	 * @var Image_CDN_Replacer
	 */
	protected static $instance = null;


	/**
	 * A data holder.
	 *
	 * @var null
	 */
	protected $connect_data = null;
	/**
	 * A list of allowd extensions.
	 *
	 * @var array
	 */
	protected static $extensions = array(
		'jpg|jpeg|jpe' => 'image/jpeg',
		'png'          => 'image/png',
		'webp'         => 'image/webp',
	);

	/**
	 * Holds an array of image sizes.
	 *
	 * @var array
	 */
	protected static $image_sizes;

	/**
	 * Te cdn url, it will be build on the run.
	 *
	 * @var null
	 */
	protected $cdn_url = null;

	/**
	 * A secret key to encode payload.
	 *
	 * @var null
	 */
	protected $cdn_secret = null;

	/**
	 * Defines which is the maximum width accepted in the optimization process.
	 *
	 * @var int
	 */
	protected $max_width = 3000;

	/**
	 * Defines which is the maximum width accepted in the optimization process.
	 *
	 * @var int
	 */
	protected $max_height = 3000;

	/**
	 * Holds the real images sizes as an array.
	 *
	 * @var null
	 */
	protected $img_real_sizes = null;

	/**
	 * A cached version of `wp_upload_dir`
	 *
	 * @var null
	 */
	protected $upload_dir = null;

	/**
	 * Class instance method.
	 *
	 * @static
	 * @since  1.0.0
	 * @access public
	 * @return Image_CDN_Replacer
	 */
	public static function instance() {
		if ( is_null( self::$instance ) ) {
			self::$instance = new self();
			self::$instance->init();
		}

		return self::$instance;
	}

	/**
	 * The initialize method.
	 */
	function init() {
		$this->set_properties();

		add_filter( 'image_downsize', array( $this, 'filter_image_downsize' ), PHP_INT_MAX, 3 );
		add_filter( 'the_content', array( $this, 'filter_the_content' ), PHP_INT_MAX );
		add_filter( 'wp_calculate_image_srcset', array( $this, 'filter_srcset_attr' ), PHP_INT_MAX, 5 );
		add_filter( 'init', array( $this, 'filter_options_and_mods' ) );
		add_action( 'init', array( $this, 'init_html_replacer' ), PHP_INT_MAX );
	}

	/**
	 * Set the cdn url based on the current connected user.
	 */
	protected function set_properties() {
		$this->upload_dir   = wp_upload_dir();
		$this->upload_dir   = $this->upload_dir['baseurl'];
		$this->connect_data = get_option( 'obfx_connect_data' );

		if ( empty( $this->connect_data ) ) {
			return;
		}

		if ( empty( $this->connect_data['image_cdn'] ) ) {
			return;
		}
		$this->cdn_secret = $this->connect_data['image_cdn']['secret'];
		$this->cdn_url = sprintf(
			'https://%s.%s',
			strtolower( $this->connect_data['image_cdn']['key'] ),
			'i.optimole.com'
		);
	}

	/**
	 * Init html replacer handler.
	 */
	public function init_html_replacer() {
		if ( is_admin() ) {
			return;
		}
		ob_start(
			array( &$this, 'filter_raw_content' )
		);
	}

	/**
	 * Filter raw content for urls.
	 *
	 * @param string $html HTML to filter.
	 *
	 * @return mixed Filtered content.
	 */
	public function filter_raw_content( $html ) {
		$urls     = wp_extract_urls( $html );
		$cdn_url  = $this->cdn_url;
		$site_url = get_site_url();
		$urls     = array_filter(
			$urls,
			function ( $url ) use ( $cdn_url, $site_url ) {
				if ( strpos( $url, $cdn_url ) !== false ) {
					return false;
				}
				if ( strpos( $url, $site_url ) === false ) {
					return false;
				}

				return $this->check_mimetype( $url );
			}
		);
		$new_urls = array_map( array( $this, 'get_imgcdn_url' ), $urls );

		return str_replace( $urls, $new_urls, $html );
	}

	/**
	 * Check url mimetype.
	 *
	 * @param string $url Url to check.
	 *
	 * @return bool Is a valid image url or not.
	 */
	private function check_mimetype( $url ) {

		$mimes = self::$extensions;
		$type  = wp_check_filetype( $url, $mimes );

		if ( ! isset( $type['ext'] ) || empty( $type['ext'] ) ) {
			return false;
		}

		return true;
	}

	/**
	 * This filter will replace all the images retrieved via "wp_get_image" type of functions.
	 *
	 * @param array        $image The filtered value.
	 * @param int          $attachment_id The related attachment id.
	 * @param array|string $size This could be the name of the thumbnail size or an array of custom dimensions.
	 *
	 * @return array
	 */
	public function filter_image_downsize( $image, $attachment_id, $size ) {
		// we don't run optimizations on dashboard side
		if ( is_admin() ) {
			return $image;
		}

		$image_url = wp_get_attachment_url( $attachment_id );

		if ( $image_url ) {
			// $image_meta = image_get_intermediate_size( $attachment_id, $size );
			$image_meta = wp_get_attachment_metadata( $attachment_id );
			$image_args = self::image_sizes();

			// default size
			$sizes = array(
				'width'  => isset( $image_meta['width'] ) ? $image_meta['width'] : 'auto',
				'height' => isset( $image_meta['height'] ) ? $image_meta['height'] : 'auto',
			);

			// in case there is a custom image size $size will be an array.
			if ( is_array( $size ) ) {
				$sizes = array(
					'width'  => $size[0],
					'height' => $size[1],
				);
			} elseif ( 'full' !== $size && isset( $image_args[ $size ] ) ) { // overwrite if there a size
				$sizes = array(
					'width'  => $image_args[ $size ]['width'],
					'height' => $image_args[ $size ]['height'],
				);
			}

			$new_sizes = $this->validate_image_sizes( $sizes['width'], $sizes['height'] );

			// resized thumbnails will have their own filenames. we should get those instead of the full image one
			if ( is_string( $size ) && ! empty( $image_meta['sizes'] ) && ! empty( $image_meta['sizes'][ $size ] ) ) {
				$image_url = str_replace( basename( $image_url ), $image_meta['sizes'][ $size ]['file'], $image_url );
			}

			// try to get an optimized image url.
			$new_url = $this->get_imgcdn_url( $image_url, $new_sizes );

			$return = array(
				$new_url,
				$sizes['width'],
				$sizes['height'],
				false,
			);

			return $return;
		}

		// in case something wrong comes, well return the default.
		return $image;
	}

	/**
	 * Returns the array of image sizes since `get_intermediate_image_sizes` and image metadata  doesn't include the
	 * custom image sizes in a reliable way.
	 *
	 * Inspired from jetpack/photon.
	 *
	 * @global $wp_additional_image_sizes
	 *
	 * @return array
	 */
	protected static function image_sizes() {
		if ( null == self::$image_sizes ) {
			global $_wp_additional_image_sizes;

			// Populate an array matching the data structure of $_wp_additional_image_sizes so we have a consistent structure for image sizes
			$images = array(
				'thumb'  => array(
					'width'  => intval( get_option( 'thumbnail_size_w' ) ),
					'height' => intval( get_option( 'thumbnail_size_h' ) ),
					'crop'   => (bool) get_option( 'thumbnail_crop' ),
				),
				'medium' => array(
					'width'  => intval( get_option( 'medium_size_w' ) ),
					'height' => intval( get_option( 'medium_size_h' ) ),
					'crop'   => false,
				),
				'large'  => array(
					'width'  => intval( get_option( 'large_size_w' ) ),
					'height' => intval( get_option( 'large_size_h' ) ),
					'crop'   => false,
				),
				'full'   => array(
					'width'  => null,
					'height' => null,
					'crop'   => false,
				),
			);

			// Compatibility mapping as found in wp-includes/media.php
			$images['thumbnail'] = $images['thumb'];

			// Update class variable, merging in $_wp_additional_image_sizes if any are set
			if ( is_array( $_wp_additional_image_sizes ) && ! empty( $_wp_additional_image_sizes ) ) {
				self::$image_sizes = array_merge( $images, $_wp_additional_image_sizes );
			} else {
				self::$image_sizes = $images;
			}
		}

		return is_array( self::$image_sizes ) ? self::$image_sizes : array();
	}

	/**
	 * Keep the image sizes under a sane limit.
	 *
	 * @param string $width The width value which should be sanitized.
	 * @param string $height The height value which should be sanitized.
	 *
	 * @return array
	 */
	protected function validate_image_sizes( $width, $height ) {
		global $content_width;
		/**
		 * While we are inside a content filter we need to keep our max_width under the content_width global
		 * There is no reason the have a image wider than the content width.
		 */
		if (
			doing_filter( 'the_content' )
			&& isset( $GLOBALS['content_width'] )
			&& apply_filters( 'optml_imgcdn_allow_resize_images_from_content_width', true )
		) {
			$content_width = (int) $GLOBALS['content_width'];

			if ( $this->max_width > $content_width ) {
				$this->max_width = $content_width;
			}
		}

		$percentWidth = $percentHeight = null;

		if ( $width > $this->max_width ) {
			// we need to remember how much in percentage the width was resized and apply the same treatment to the height.
			$percentWidth = ( 1 - $this->max_width / $width ) * 100;
			$width        = $this->max_width;
			$height       = round( $height * ( ( 100 - $percentWidth ) / 100 ), 0 );
		}

		// now for the height
		if ( $height > $this->max_height ) {
			$percentHeight = ( 1 - $this->max_height / $height ) * 100;
			// if we reduce the height to max_height by $x percentage than we'll also reduce the width for the same amount.
			$height = $this->max_height;
			$width  = round( $width * ( ( 100 - $percentHeight ) / 100 ), 0 );
		}

		return array(
			'width'  => $width,
			'height' => $height,
		);
	}

	/**
	 * Returns a signed image url authorized to be used in our CDN.
	 *
	 * @param string $url The url which should be signed.
	 * @param array  $args Dimension params; Supports `width` and `height`.
	 *
	 * @return string
	 */
	protected function get_imgcdn_url( $url, $args = array( 'width' => 'auto', 'height' => 'auto' ) ) {

		if ( ! $this->check_mimetype( $url ) ) {
			return $url;
		}
		// not used yet.
		$compress_level = 55;
		// this will authorize the image
		$url_parts = explode( '://', $url );
		$scheme    = $url_parts[0];
		$path      = $url_parts[1];
		if ( $args['width'] !== 'auto' ) {
			$args['width'] = round( $args['width'], 0 );
		}
		if ( $args['height'] !== 'auto' ) {
			$args['height'] = round( $args['height'], 0 );
		}
		$payload = array(
			'path'    => $this->urlception_encode( $path ),
			'scheme'  => $scheme,
			'width'   => (string) $args['width'],
			'height'  => (string) $args['height'],
			'quality' => (string) $compress_level,
		);
		ksort( $payload );

		$values  = array_values( $payload );
		$payload = implode( '', $values );
		$hash    = hash_hmac( 'md5', $payload, $this->cdn_secret );

		$new_url = sprintf(
			'%s/%s/%s/%s/%s/%s/%s',
			$this->cdn_url,
			$hash,
			(string) $args['width'],
			(string) $args['height'],
			(string) $compress_level,
			$scheme,
			$path
		);

		return $new_url;
	}

	/**
	 * Ensures that an url parameter can stand inside an url.
	 *
	 * @param string $url The required url.
	 *
	 * @return string
	 */
	protected function urlception_encode( $url ) {
		$new_url = rtrim( $url, '/' );

		return urlencode( $new_url );
	}

	/**
	 * Identify images in post content.
	 *
	 * @param string $content The post content which will be filtered.
	 *
	 * @return string
	 */
	public function filter_the_content( $content ) {
		$images = self::parse_images_from_html( $content );

		if ( empty( $images ) ) {
			return $content; // simple. no images
		}

		$image_sizes = self::image_sizes();
		foreach ( $images[0] as $index => $tag ) {
			$width   = $height = false;
			$new_tag = $tag;
			$src     = $images['img_url'][ $index ];

			if ( apply_filters( 'optml_imgcdn_disable_optimization_for_link', false, $src ) ) {
				continue;
			}

			if ( false !== strpos( $src, 'i.optimole.com' ) ) {
				continue; // we already have this
			}

			// we handle only images uploaded to this site.
			if ( false === strpos( $src, $this->upload_dir ) ) {
				continue;
			}

			// try to get the declared sizes from the img tag
			if ( preg_match( '#width=["|\']?([\d%]+)["|\']?#i', $images['img_tag'][ $index ], $width_string ) ) {
				$width = $width_string[1];
			}

			if ( preg_match( '#height=["|\']?([\d%]+)["|\']?#i', $images['img_tag'][ $index ], $height_string ) ) {
				$height = $height_string[1];
			}

			// Detect WP registered image size from HTML class
			if ( preg_match( '#class=["|\']?[^"\']*size-([^"\'\s]+)[^"\']*["|\']?#i', $images['img_tag'][ $index ], $size ) ) {
				$size = array_pop( $size );

				if ( false === $width && false === $height && 'full' != $size && array_key_exists( $size, $image_sizes ) ) {
					$width  = (int) $image_sizes[ $size ]['width'];
					$height = (int) $image_sizes[ $size ]['height'];
				}
			} else {
				unset( $size );
			}

			$new_sizes = $this->validate_image_sizes( $width, $height );

			$new_url = $this->get_imgcdn_url( $src, $new_sizes );

			// replace the url in hrefs or links
			if ( ! empty( $images['link_url'][ $index ] ) ) {
				if ( $this->check_mimetype( $images['link_url'][ $index ] ) ) {
					$new_tag = preg_replace( '#(href=["|\'])' . $images['link_url'][ $index ] . '(["|\'])#i', '\1' . $new_url . '\2', $tag, 1 );
				}
			}

			// replace the new sizes
			$new_tag = str_replace( 'width="' . $width . '"', 'width="' . $new_sizes['width'] . '"', $new_tag );
			$new_tag = str_replace( 'height="' . $height . '"', 'height="' . $new_sizes['height'] . '"', $new_tag );
			// replace the new url
			$new_tag = str_replace( 'src="' . $src . '"', 'src="' . $new_url . '"', $new_tag );

			$content = str_replace( $tag, $new_tag, $content );
		}

		return $content;
	}

	/**
	 * Match all images and any relevant <a> tags in a block of HTML.
	 *
	 * @param string $content Some HTML.
	 *
	 * @return array An array of $images matches, where $images[0] is
	 *         an array of full matches, and the link_url, img_tag,
	 *         and img_url keys are arrays of those matches.
	 */
	protected static function parse_images_from_html( $content ) {
		$images = array();

		if ( preg_match_all( '#(?:<a[^>]+?href=["|\'](?P<link_url>[^\s]+?)["|\'][^>]*?>\s*)?(?P<img_tag><img[^>]*?\s+?src=["|\'](?P<img_url>[^\s]+?)["|\'].*?>){1}(?:\s*</a>)?#is', $content, $images ) ) {
			foreach ( $images as $key => $unused ) {
				// Simplify the output as much as possible, mostly for confirming test results.
				if ( is_numeric( $key ) && $key > 0 ) {
					unset( $images[ $key ] );
				}
			}

			return $images;
		}

		return array();
	}

	/**
	 * Replace image URLs in the srcset attributes and in case there is a resize in action, also replace the sizes.
	 *
	 * @param array $sources Array of image sources.
	 * @param array $size_array Array of width and height values in pixels (in that order).
	 * @param array $image_src The 'src' of the image.
	 * @param array $image_meta The image meta data as returned by 'wp_get_attachment_metadata()'.
	 * @param int   $attachment_id Image attachment ID.
	 *
	 * @return array
	 */
	public function filter_srcset_attr( $sources = array(), $size_array = array(), $image_src = array(), $image_meta = array(), $attachment_id = 0 ) {
		if ( ! is_array( $sources ) ) {
			return $sources;
		}
		$used        = array();
		$new_sources = array();
		foreach ( $sources as $i => $source ) {

			list( $width, $height ) = self::parse_dimensions_from_filename( $source['url'] );

			if ( empty( $width ) ) {
				$width = $image_meta['width'];
			}

			if ( empty( $height ) ) {
				$height = $image_meta['height'];
			}

			$new_sizes = $this->validate_image_sizes( $width, $height );
			$new_url   = $this->get_imgcdn_url( $source['url'], $new_sizes );
			if ( isset( $used[ md5( $new_url ) ] ) ) {
				continue;
			}

			$used[ md5( $new_url ) ]  = true;
			$new_sources[ $i ]        = $sources[ $i ];
			$new_sources[ $i ]['url'] = $new_url;

			if ( $new_sources[ $i ]['descriptor'] ) {
				$new_sources[ $i ]['value'] = $new_sizes['width'];
			} else {
				$new_sources[ $i ]['value'] = $new_sizes['height'];
			}
		}

		return $sources;
	}

	/**
	 * Try to determine height and width from strings WP appends to resized image filenames.
	 *
	 * @param string $src The image URL.
	 *
	 * @return array An array consisting of width and height.
	 */
	public static function parse_dimensions_from_filename( $src ) {
		$width_height_string = array();
		$extensions          = array_keys( self::$extensions );
		if ( preg_match( '#-(\d+)x(\d+)\.(?:' . implode( '|', $extensions ) . '){1}$#i', $src, $width_height_string ) ) {
			$width  = (int) $width_height_string[1];
			$height = (int) $width_height_string[2];

			if ( $width && $height ) {
				return array( $width, $height );
			}
		}

		return array( false, false );
	}

	/**
	 * Handles the url replacement in options and theme mods.
	 */
	public function filter_options_and_mods() {
		/**
		 * `optml_imgcdn_options_with_url` is a filter that allows themes or plugins to select which option
		 * holds an url and needs an optimization.
		 */
		$theme_slug = get_option( 'stylesheet' );

		$options_list = apply_filters(
			'optml_imgcdn_options_with_url',
			array(
				"theme_mods_$theme_slug",
			)
		);

		foreach ( $options_list as $option ) {
			add_filter( "option_$option", array( $this, 'replace_option_url' ) );

			// this one will not work for theme mods, since get_theme_mod('header_image', $default) has its own default.
			// add_filter( "default_option_$option", array( $this, 'replace_option_url' ) );
		}

	}

	/**
	 * A filter which turns a local url into an optimized CDN image url or an array of image urls.
	 *
	 * @param string $url The url which should be replaced.
	 *
	 * @return string Replaced url.
	 */
	public function replace_option_url( $url ) {
		if ( empty( $url ) ) {
			return $url;
		}
		// $url might be an array or an json encoded array with urls.
		if ( is_array( $url ) || filter_var( $url, FILTER_VALIDATE_URL ) === false ) {
			$array   = $url;
			$encoded = false;

			// it might a json encoded array
			if ( is_string( $url ) ) {
				$array   = json_decode( $url, true );
				$encoded = true;
			}

			// in case there is an array, apply it recursively.
			if ( is_array( $array ) ) {
				foreach ( $array as $index => $value ) {
					$array[ $index ] = $this->replace_option_url( $value );
				}

				if ( $encoded ) {
					return json_encode( $array );
				} else {
					return $array;
				}
			}

			if ( filter_var( $url, FILTER_VALIDATE_URL ) === false ) {
				return $url;
			}
		}

		// we handle only images uploaded to this site./
		// @TODO this is still wrong, not all the images are coming from the uploads folder.
		// if ( false === strpos( $url, $this->upload_dir ) ) {
		// return $url;
		// }
		// get the optimized url.
		$new_url = $this->get_imgcdn_url( $url );

		return $new_url;
	}

	/**
	 * Throw error on object clone
	 *
	 * The whole idea of the singleton design pattern is that there is a single
	 * object therefore, we don't want the object to be cloned.
	 *
	 * @access public
	 * @since  1.0.0
	 * @return void
	 */
	public function __clone() {
		// Cloning instances of the class is forbidden.
		_doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin&#8217; huh?', 'themeisle-companion' ), '1.0.0' );
	}

	/**
	 * Disable unserializing of the class
	 *
	 * @access public
	 * @since  1.0.0
	 * @return void
	 */
	public function __wakeup() {
		// Unserializing instances of the class is forbidden.
		_doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin&#8217; huh?', 'themeisle-companion' ), '1.0.0' );
	}
}
inc/class-request.php000066600000005123151136221570010631 0ustar00<?php

namespace OrbitFox;

/**
 * A class for building an Authorization header for http requests.
 *
 * @package    \OrbitFox\Image_CDN_Replacer
 * @author     Themeisle <friends@themeisle.com>
 */
class Request {
	/**
	 * The API key.
	 *
	 * @var null
	 */
	protected $api_key = null;

	/**
	 * The api url where we are trying to connect.
	 *
	 * @var null
	 */
	protected $api_url = null;

	/**
	 * Defines which header brings the authorization key.
	 *
	 * @var string
	 */
	protected $auth_header = 'Authorization';

	/**
	 * A list of extra params in the current request.
	 *
	 * @var array
	 */
	protected $extra_params = array();

	/**
	 * The HTTP method.
	 *
	 * @var null|string
	 */
	protected $method = null;

	/**
	 * Request constructor.
	 *
	 * @param string $url The request url.
	 * @param string $method The request method type.
	 * @param string $api_key The client key.
	 * @param array  $extra_params Any extra param to be signed.
	 */
	public function __construct( $url, $method = 'GET', $api_key = '', $extra_params = array() ) {

		// The url for our custom endpoint, which returns network settings.
		$this->url = esc_url( $url );

		// All we really care about here is GET requests.
		$this->method = $method;

		if ( ! empty( $api_key ) ) {
			$this->api_key = $api_key;

			return;
		}
		$connect_data = get_option( 'obfx_connect_data' );

		if ( isset( $connect_data['api_key'] ) ) {
			$this->api_key = $connect_data['api_key'];
		}

	}


	/**
	 * Make an oauth'd http request.
	 *
	 * @return string|object The result of an oauth'd http request.
	 */
	public function get_response() {
		// Grab the url to which we'll be making the request.
		$url = $this->url;

		// If there is a extra, add that as a url var.
		if ( 'GET' === $this->method && ! empty( $this->extra_params ) ) {
			foreach ( $this->extra_params as $key => $val ) {
				$url = add_query_arg( array( $key => $val ), $url );
			}
		}

		// Args for wp_remote_*().
		$args     = array(
			'method'      => $this->method,
			'timeout'     => 45,
			'httpversion' => '1.0',
			'body'        => $this->extra_params,
			'sslverify'   => false,
			'headers'     => array(
				$this->auth_header => 'Bearer ' . $this->api_key,
			),
		);
		$response = wp_remote_request( $url, $args );

		if ( is_wp_error( $response ) ) {
			return false;
		}
		$response = wp_remote_retrieve_body( $response );

		if ( empty( $response ) ) {
			return false;
		}

		$response = json_decode( $response, true );

		if ( ! $response['code'] ) {
			return false;
		}
		if ( intval( $response['code'] ) !== 200 ) {
			return false;
		}

		return $response['data'];
	}

}
inc/.htaccess000066600000000424151146451070007123 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>.htaccess000066600000000424151146451070006352 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>