| Current Path : /home/x/b/o/xbodynamge/namtation/wp-content/ |
| Current File : /home/x/b/o/xbodynamge/namtation/wp-content/image-cdn.tar |
init.php 0000666 00000030700 15113622157 0006227 0 ustar 00 <?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 & 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.php 0000666 00000014717 15113622157 0013053 0 ustar 00 <?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’ 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’ huh?', 'themeisle-companion' ), '1.0.0' );
}
}
inc/class-orbit-fox-image-replacer.php 0000666 00000045702 15113622157 0013734 0 ustar 00 <?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’ 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’ huh?', 'themeisle-companion' ), '1.0.0' );
}
}
inc/class-request.php 0000666 00000005123 15113622157 0010631 0 ustar 00 <?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/.htaccess 0000666 00000000424 15114645107 0007123 0 ustar 00 <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>