Last active
September 26, 2023 06:13
-
-
Save indikatordesign/71a82ce84717dd021474e5ef2f03ffa3 to your computer and use it in GitHub Desktop.
Bring your WordPress images into the modern Webp format
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
Plugin Name: Rewrite JPG, JPEG, PNG, GIF to WEBP | |
Plugin URI: https://indikator-design.com/get-divi-theme-to-100-in-your-pagespeed-score/ | |
Description: Rewrite image extensions from images in mediathek and linked in posts. | |
Version: 1.0 | |
Author: Bruno Bouyajdad | Indikator Design | |
Author URI: https://indikator-design.com | |
Author Email: [email protected] | |
*/ | |
/** | |
* Do not allow direct access | |
* | |
* @since 1.0 | |
*/ | |
if ( ! defined( 'ABSPATH' ) ) die( 'Nothing to find Ma\'am..' ); | |
/** | |
* Add the Menu Page | |
* | |
* @since 1.0 | |
*/ | |
if ( ! class_exists( 'rwwebpControllerMenu' ) ) | |
{ | |
final class rwwebpControllerMenu | |
{ | |
/** | |
* Constructor | |
* | |
* @since 1.0 | |
*/ | |
public function __construct() | |
{ | |
add_action( 'admin_menu', [ $this, 'addMenu' ] ); | |
} // end constructor | |
/** | |
* Add menu in admin area | |
* | |
* @since 1.0 | |
*/ | |
public function addMenu() | |
{ | |
$hook = add_submenu_page | |
( | |
'tools.php', | |
'Rewrite WEBP', | |
'Rewrite WEBP Execution', | |
'manage_options', | |
'rewrite-webp', | |
[ $this, 'menuCallback' ], | |
10 | |
); | |
} // end implement | |
/** | |
* Menu Callback | |
* | |
* @since 1.0 | |
*/ | |
public function menuCallback() | |
{ | |
( new rwwebpViewExecution )->render(); | |
} // end menuCallback | |
} // end rwwebpControllerMenu | |
} // end if | |
new rwwebpControllerMenu; | |
/** | |
* Execute the Ajax Request | |
* | |
* @since 1.0 | |
*/ | |
if ( ! class_exists( 'rwwebpControllerAjax' ) ) | |
{ | |
final class rwwebpControllerAjax | |
{ | |
/** | |
* Define properties | |
* | |
* @since 1.0 | |
*/ | |
private $transient; | |
const ACTION = 'rwwebp_execute_rewrite'; | |
/** | |
* Constructor | |
* | |
* @since 1.0 | |
*/ | |
public function __construct() | |
{ | |
$this->transient = 'rwwebp_list_blog_ids'; | |
add_action( 'wp_ajax_' . self::ACTION, [ $this, 'ajax' ] ); | |
} // end constructor | |
/** | |
* Execute the ajax call | |
* | |
* @since 1.0 | |
*/ | |
public function ajax() | |
{ | |
$p = $_POST; | |
if ( empty( $p['nonce'] ) || empty( $p['blog'] ) || empty( $p['blogs'] ) ) | |
$this->result( 'error' ); | |
if ( wp_verify_nonce( esc_attr( $p['nonce'] ), self::ACTION . '_nonce' ) ) : | |
if ( ! is_multisite() ) : | |
$blogToExecute = esc_attr( $p['blog'] ); | |
( new rwwebpControllerDB( new rwwebpModelDatabase( $blogToExecute ) ) )->initialize(); | |
else : | |
if ( ! ( $transientBlogs = get_site_transient( $this->transient ) ) ) : | |
foreach ( $p['blogs'] as $oneBlog ) | |
$transientBlogs[ (int) esc_attr( $oneBlog ) ] = false; | |
endif; | |
$blogToExecute = (int) esc_attr( $p['blog'] ); | |
if ( ! $transientBlogs[$blogToExecute] ) : | |
$currentBlog = (int) get_current_blog_id(); | |
$blogChange = ( $blogToExecute != $currentBlog ); | |
if ( $blogChange ) | |
switch_to_blog( $blogToExecute ); | |
( new rwwebpControllerDB( new rwwebpModelDatabase( $blogToExecute ) ) )->initialize(); | |
if ( $blogChange ) | |
switch_to_blog( $currentBlog ); | |
$transientBlogs[$blogToExecute] = true; | |
endif; | |
foreach ( $transientBlogs as $blog => $bool ) : | |
if ( ! $bool ) : | |
$notDelete = true; | |
break; | |
endif; | |
endforeach; | |
// delete as update doesn't work well | |
delete_site_transient( $this->transient ); | |
if ( isset( $notDelete ) ) // just if still needed | |
set_site_transient( $this->transient, $transientBlogs, DAY_IN_SECONDS * 7 ); | |
endif; | |
$this->result( 'success' ); | |
endif; | |
$this->result( 'error' ); | |
} // end ajax | |
/** | |
* Ajax error handler | |
* | |
* @since 1.0 | |
*/ | |
private function result( $case ) | |
{ | |
$obj[$case] = true; | |
echo json_encode( (object) $obj ); | |
wp_die(); | |
} // end error | |
} // end rwwebpControllerAjax | |
} // end if | |
new rwwebpControllerAjax; | |
/** | |
* Database Controller to execute the changes | |
* | |
* @since 1.0 | |
*/ | |
if ( ! class_exists( 'rwwebpControllerDB' ) ) | |
{ | |
final class rwwebpControllerDB | |
{ | |
/** | |
* Define properties | |
* | |
* @since 1.0 | |
*/ | |
private $posts; | |
private $modelDB; | |
/** | |
* Constructor | |
* | |
* @since 1.0 | |
*/ | |
public function __construct( rwwebpModelDatabase $modelDB ) | |
{ | |
/** | |
* Set properties | |
* | |
* @since 1.0 | |
*/ | |
$this->modelDB = $modelDB; | |
$this->images = $this->modelDB->getIds( '' ); | |
$this->posts = $this->modelDB->getIds( 'post' ); | |
} // end constructor | |
/** | |
* Initialize the class | |
* | |
* @since 1.0 | |
*/ | |
public function initialize() | |
{ | |
$this->posts(); | |
$this->attachments(); | |
} // end initialize | |
/** | |
* Execute the posts | |
* | |
* @since 1.0 | |
*/ | |
public function posts() | |
{ | |
foreach ( $this->posts as $post ) : | |
$content = $this->postLinks( $this->modelDB->getPostContent( $post->ID )[0]->post_content ); | |
$this->modelDB->setPostContent( $post->ID, $content ); | |
endforeach; | |
} // end posts | |
/** | |
* Execute the attachments | |
* | |
* @since 1.0 | |
*/ | |
public function attachments() | |
{ | |
foreach ( $this->images as $image ) : | |
$image = $this->modelDB->getPost( $image->ID ); | |
$update['ID'] = $image->ID; | |
$update['post_mime_type'] = $this->replaceMeta( $image->post_mime_type ); | |
// The guid value is deliberately ignored: | |
// https://wordpress.org/support/article/changing-the-site-url/#important-guid-note | |
$this->modelDB->setPost( $update ); | |
$fileKey = '_wp_attached_file'; | |
$metaKey = '_wp_attachment_metadata'; | |
$imageFile = $this->modelDB->getMeta( $image->ID, $fileKey ); | |
$this->modelDB->setMeta( $image->ID, $fileKey, $this->replaceMeta( $imageFile ) ); | |
$metaData = $this->modelDB->getMeta( $image->ID, $metaKey ); | |
$this->modelDB->setMeta( $image->ID, $metaKey, $this->adjustMetaArray( $metaData ) ); | |
endforeach; | |
} // end attachments | |
/** | |
* Adjust the meta data array | |
* | |
* @since 1.0 | |
*/ | |
function adjustMetaArray( $arr ) | |
{ | |
$arr['file'] = $this->replaceMeta( $arr['file'] ); | |
foreach ( $arr['sizes'] as $key => $val ) : | |
$arr['sizes'][$key]['file'] = $this->replaceMeta( $val['file'] ); | |
$arr['sizes'][$key]['mime-type'] = $this->replaceMeta( $val['mime-type'] ); | |
endforeach; | |
return $arr; | |
} // end adjustMetaArray | |
/** | |
* Replace the extension from post content | |
* | |
* @since 1.0 | |
*/ | |
function postLinks( $content ) | |
{ | |
$regex = '#(?<=wp-content\/uploads\/)([^\s\"]+.{1})(jpg|jpeg|png|gif)#i'; | |
return preg_replace( $regex, '$1webp', $content ); | |
} // end postLinks | |
/** | |
* Replace the extension from meta | |
* | |
* @since 1.0 | |
*/ | |
function replaceMeta( $source ) | |
{ | |
return preg_replace( '#(?<=\.|\/)(jpg|jpeg|png|gif)$#i', 'webp', $source ); | |
} // end replaceMeta | |
} // end rwwebpControllerDB | |
} // end if | |
// /** | |
// * Database Model to get and update images and posts | |
// * | |
// * @since 1.0 | |
// */ | |
if ( ! class_exists( 'rwwebpModelDatabase' ) ) | |
{ | |
final class rwwebpModelDatabase | |
{ | |
/** | |
* Define properties | |
* | |
* @since 1.0 | |
*/ | |
private $blog; | |
private $wpdb; | |
private $post; | |
/** | |
* Constructor | |
* | |
* @since 1.0 | |
*/ | |
public function __construct( $blogToExecute ) | |
{ | |
global $wpdb; | |
/** | |
* Set properties | |
* | |
* @since 1.0 | |
*/ | |
$this->wpdb = $wpdb; | |
$this->blog = $blogToExecute; | |
$this->post = $this->getPostsTable(); | |
} // end constructor | |
/** | |
* Get the attachment ids | |
* | |
* @since 1.0 | |
*/ | |
public function getIds( $type ) | |
{ | |
$post = 'post' == $type ? '!=' : '='; | |
$query = "SELECT ID FROM {$this->post} WHERE post_type {$post} 'attachment'"; | |
return $this->wpdb->get_results( $query ); | |
} // end getMediaIds | |
/** | |
* Get the post content | |
* | |
* @since 1.0 | |
*/ | |
public function getPostContent( $id ) | |
{ | |
$query = "SELECT post_content FROM {$this->post} WHERE ID = %d"; | |
return $this->wpdb->get_results( $this->wpdb->prepare( $query, $id ) ); | |
} // end getPostContent | |
/** | |
* Update the post content | |
* | |
* @since 1.0 | |
*/ | |
public function setPostContent( $id, $content ) | |
{ | |
return $this->wpdb->update( $this->post, [ 'post_content' => $content ], [ 'ID' => $id ] ); | |
} // end setPostContent | |
/** | |
* Get the posts table | |
* | |
* @since 1.0 | |
*/ | |
public function getPostsTable() | |
{ | |
$blog = ( 'single' == $this->blog ) ? '' : $this->blog . '_'; | |
return $this->wpdb->base_prefix . $blog . 'posts'; | |
} // end getPostsTable | |
/** | |
* Get a post by id | |
* | |
* @since 1.0 | |
*/ | |
public function getPost( $id ) | |
{ | |
return get_post( $id ); | |
} // end getPost | |
/** | |
* Update a post by id | |
* | |
* @since 1.1 | |
*/ | |
public function setPost( $data ) | |
{ | |
wp_update_post( $data, true ); | |
} // end setPost | |
/** | |
* Get the image meta data | |
* | |
* @since 1.0 | |
*/ | |
public function getMeta( $id, $key, $single = true ) | |
{ | |
return get_post_meta( $id, $key, $single ); | |
} // end getMeta | |
/** | |
* Update the image meta | |
* | |
* @since 1.0 | |
*/ | |
public function setMeta( $id, $key, $value ) | |
{ | |
update_post_meta( $id, $key, $value ); | |
} // end setMeta | |
} // end rwwebpModelDatabase | |
} // end if | |
/** | |
* Rewrite View Controller | |
* | |
* @since 1.0 | |
*/ | |
if ( ! class_exists( 'rwwebpControllerView' ) ) | |
{ | |
final class rwwebpControllerView | |
{ | |
/** | |
* Define properties | |
* | |
* @since 1.0 | |
*/ | |
private $action; | |
/** | |
* Constructor | |
* | |
* @since 1.0 | |
*/ | |
public function __construct() | |
{ | |
/** | |
* Set properties | |
* | |
* @since 1.0 | |
*/ | |
$this->action = rwwebpControllerAjax::ACTION; | |
} // end constructor | |
/** | |
* Get Site ID's | |
* | |
* @since 1.0 | |
*/ | |
private function getSiteIds() | |
{ | |
if ( ! is_multisite() ) | |
return [ [ 'single' ], [ $this->hostOrigin() ] ]; | |
foreach ( get_sites() as $index => $site ) : | |
$blogIds[] = $site->blog_id; | |
$domains[] = $site->domain; | |
endforeach; | |
return [ $blogIds, $domains ]; | |
} // end getSiteIds | |
/** | |
* Get the values for JS | |
* | |
* @since 1.0 | |
*/ | |
public function getProps() | |
{ | |
$info = $this->getSiteIds(); | |
return | |
[ | |
'props' => | |
[ | |
'blogs' => $info[0], | |
'domains' => $info[1], | |
'action' => $this->action, | |
'ajax_url' => admin_url( 'admin-ajax.php' ), | |
'nonce' => wp_create_nonce( $this->action . '_nonce' ), | |
], | |
]; | |
} // end getProps | |
/** | |
* Get the host origin | |
* | |
* @since 1.0 | |
*/ | |
private function hostOrigin() | |
{ | |
$s = $_SERVER; | |
return isset( $s[ 'HTTP_HOST' ] ) | |
? esc_attr( $s[ 'HTTP_HOST' ] ) | |
: ( function() use ( $s ) | |
{ | |
if ( isset( $s['HTTP_ORIGIN'] ) ) : | |
$parse = parse_url( $s['HTTP_ORIGIN'] ); | |
if ( isset( $parse['host'] ) ) | |
return $parse['host']; | |
else | |
return false; | |
endif; | |
return false; | |
})(); | |
} // end hostOrigin | |
} // end rwwebpControllerView | |
} // end if | |
/** | |
* Rewrite WEBP View | |
* | |
* @since 1.0 | |
*/ | |
if ( ! class_exists( 'rwwebpViewExecution' ) ) | |
{ | |
final class rwwebpViewExecution | |
{ | |
/** | |
* Render the view | |
* | |
* @since 1.0 | |
*/ | |
public function render() | |
{ ?> | |
<!-- View Style --> | |
<style> | |
h1 { | |
padding-bottom: 0px; | |
} | |
h1, p { | |
color: #656565; | |
font-weight: 200; | |
font-family: Open Sans,Arial,sans-serif; | |
} | |
p { | |
font-size: 16px; | |
} | |
#wpwrap { | |
background-color: white!important; | |
} | |
.outer-container { | |
width: 80%; | |
margin: 60px 10% 10%; | |
border: 1px solid #ccc; | |
} | |
.head { | |
padding: 20px; | |
border-bottom: 1px solid #ccc; | |
} | |
.start { | |
margin-bottom: 20px; | |
} | |
.main { | |
padding: 20px; | |
min-height: 100px; | |
} | |
button.start { | |
font-weight: 200; | |
background: white; | |
color: #656565!important; | |
font-size: 16px!important; | |
border-radius: 0px!important; | |
padding: 5px 100px!important; | |
border: 1px solid #656565!important; | |
} | |
.progress { | |
width: 100%; | |
height: auto; | |
display: none; | |
} | |
.outer { | |
width: 100%; | |
height: 30px; | |
display: flex; | |
border: 1px solid #ccc; | |
} | |
.inner { | |
width: 0; | |
height: 30px; | |
margin-right: auto; | |
background-color: #a5d3e5; | |
} | |
#confetti-canvas { | |
top: 0; | |
position: fixed; | |
z-index: 1!important; | |
} | |
</style> | |
<!-- View --> | |
<div class="outer-container"> | |
<div class="info-container"> | |
<div class="head"> | |
<h1>Rewrite Image Paths To Webp</h1> | |
</div> | |
<div class="main"> | |
<p class="start">Press start to execute the script:</p> | |
<button id="start-process" class="start">Start</button> | |
<div id="progress" class="progress"> | |
<div id="outer" class="outer"> | |
<div id="inner" class="inner"></div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- JavaScript Rewrite Image Extensions Handler --> | |
<script> | |
(function($,w){$(function(){ | |
class rewriteWebp | |
{ | |
setProps() | |
{ | |
this.props; | |
this.blogs; | |
this.multi; | |
} // end setProps | |
constructor() | |
{ | |
this.setProps(); | |
this.getPhpProps(); | |
this.bindEvents(); | |
} // end constructor | |
ajax( self = this ) | |
{ | |
const data = | |
{ | |
nonce : self.props.nonce, | |
blogs : self.props.blogs, | |
action : self.props.action, | |
blog : self.blogs.shift(), | |
}; | |
$( '#blog' ).html( 'single' == data.blog ? 1 : data.blog ); | |
$( '#domain' ).html( self.props.domains.shift() ); | |
$.ajax( | |
{ | |
data : data, | |
type : 'post', | |
dataType : 'json', | |
url : self.props.ajax_url, | |
success: function( data ) | |
{ | |
if ( data.hasOwnProperty( 'success' ) ) | |
{ | |
if ( 0 == self.blogs.length ) | |
{ | |
if ( ! self.multi ) | |
$( '#inner' ).css( 'width', '100%' ) | |
$( 'p.start' ).html( self.finished() ); | |
$( '#inner' ).css( 'backgroundColor', '#a7e5a5' ); | |
return $.getScript( self.confetti(), function() | |
{ | |
startConfetti(); | |
setTimeout( () => { stopConfetti(); }, 5000 ); | |
}); | |
} | |
$( '#inner' ).css( 'width', self.progressWidth() + '%' ); | |
self.ajax(); | |
} | |
if ( data.hasOwnProperty( 'error' ) ) | |
self.error(); | |
}, | |
error: function( data ) | |
{ | |
self.error(); | |
} | |
}); | |
} // end ajax | |
error() | |
{ | |
$( 'p.start' ).html( 'Something did not work. Check your "debug.log" for errors and correct them. Then restart the script.' ); | |
$( '#inner' ).css( { 'width': '100%', 'backgroundColor': '#b86969' } ); | |
} // end error | |
getPhpProps() | |
{ | |
this.php = <?php echo json_encode( ( new rwwebpControllerView )->getProps() ); ?>; | |
this.props = this.php.props; | |
} // end getPhpProps | |
bindEvents( self = this ) | |
{ | |
$( '#start-process' ).on( 'click', function( e ) | |
{ | |
e.preventDefault(); | |
if ( confirm( self.confirm() ) ) | |
{ | |
$( this ).fadeOut( '10', function() | |
{ | |
$( '#progress' ).fadeIn( '300', function() | |
{ | |
self.blogs = self.props.blogs.slice(); | |
self.multi = self.blogs.length > 1 ? true : false; | |
$( '#inner' ).css( 'width', ( self.multi ? self.progressWidth() : '33' ) + '%' ); | |
$( 'p.start' ).html( self.start() ); | |
return self.ajax(); | |
}); | |
}); | |
} | |
}); | |
} // end bindEvents | |
progressWidth() | |
{ | |
let all = this.props.blogs.length; | |
return ( 100 / all ) * ( all - this.blogs.length + 1 ); | |
} // end progressWidth | |
start() | |
{ | |
return 'Process is being executed.. At the moment on Blog: <span id="blog"></span> - Domain: <span id="domain" />'; | |
} // end start | |
confirm() | |
{ | |
return 'If the process is started, it cannot be undone without an existing backup of the database. Are you sure you want to start now?'; | |
} // end confirm | |
finished() | |
{ | |
return 'Finished.. The paths in your blogs are all adjusted. Don\'t forget to remove the script from the "mu-plugins" folder. A total of ' + this.props.blogs.length + ' blogs were edited.<br><br>If you feel like donating me a coffee or something, feel free to do so here: <a href="https://www.paypal.com/donate/?hosted_button_id=YBQSPNKV7UYXA" target="_blank">donation</a>. Much appreciated 🙏 Or check my <a href="https://www.elegantthemes.com/marketplace/author/indikator-design/" target="_blank">plugins</a>.'; | |
} // end finished | |
confetti() | |
{ | |
// Just to celebrate. Check the code here: https://cdn.indikator-design.com/media/blogs/divi-to-100/confetti/confetti.js | |
return 'https://cdn.indikator-design.com/media/blogs/divi-to-100/confetti/confetti.min.js'; | |
} // end confetti | |
} // end class rewriteWebp | |
new rewriteWebp; | |
});}(jQuery,window)); | |
</script> | |
<?php } // end render | |
} // end rwwebpViewExecution | |
} // end if |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment