includes();
self::$instance->setup_globals();
self::$instance->setup_actions();
}
return self::$instance;
}
/**
* A dummy constructor to prevent the plugin from being loaded more than once.
*/
private function __construct() { /* Do nothing here */ }
function includes(){
require( $this->plugin_dir . 'ari-settings.php');
}
function setup_globals(){
global $wpdb;
/** Paths *************************************************************/
$this->file = __FILE__;
$this->basename = plugin_basename( $this->file );
$this->prefix = 'ari';
$this->plugin_dir = plugin_dir_path( $this->file );
$this->plugin_url = plugin_dir_url ( $this->file );
$this->options_class = new AriSettings();
}
function setup_actions(){
//localization
add_action('init', array($this, 'load_plugin_textdomain'));
//scripts & styles
add_action( 'admin_enqueue_scripts', array( $this, 'scripts_styles' ) );
//metabox
add_action( 'add_meta_boxes', array( $this, 'metabox_init' ) );
//post processing
add_action( 'save_post', array( $this, 'save_archiving_status' ) );
add_action( 'save_post', array( $this, 'save_post_images' ),10, 2);
add_filter('ari_get_remote_image_url', array( $this, 'get_url_from_special_domain' ));
add_filter('ari_get_image_attributes',array(&$this,'image_attributes_class_id'),10,2);
}
public function load_plugin_textdomain(){
load_plugin_textdomain($this->basename, FALSE, $this->plugin_dir.'/languages/');
}
public function get_setting($slug){
$options = self::get_settings();
if (array_key_exists($slug, $options)) return $options[$slug];
}
public function get_settings(){
return $this->options_class->options;
}
////
public function scripts_styles() {
//wp_enqueue_style( 'ari-admin', $this->plugin_url .'_inc/css/ari-admin.css', array(), $this->version );
}
/**
* This function registers a metabox with the callback archive_remote_images_meta_box_callback.
* For reference: add_meta_box( $id, $title, $callback, $page, $context, $priority, $callback_args );
*
*/
function metabox_init(){
//capabilities
if (!current_user_can('edit_post', get_the_ID())) return false;
if (!current_user_can('upload_files', get_the_ID())) return false;
$post_types = $this->options_class->allowed_post_types();
$ignored = self::get_setting('ignored_post_type');
foreach ($post_types as $post_type){
if (($ignored) && (in_array($post_type,$ignored))) continue;
add_meta_box('ari', __('Archive Remote Images','ari'), array(&$this,'metabox_content'), $post_type, 'side', 'high');
}
}
function metabox_content($post){
$checked = self::get_setting('default_checked');
if (self::get_setting('remember_status')){
if ($meta_value = get_post_meta($post->ID, 'ari_enabled', TRUE)){
if ($meta_value == 'yes'){
$checked = true;
}else{
$checked = false;
}
}
}
?>
1 media has been downloaded for this post using Archive Remote Images !',
'Already %s medias have been downloaded for this post using Archive Remote Images !',
$count,
'ari' ),
$count
);
?>
id="ari-metabox-check" name="do_remote_archive">
basename,'ari_form',false);?>
basename)) $is_valid_nonce=true;
if ($is_autosave || $is_revision || !$is_valid_nonce) return $post_id;
//capabilities
if (!current_user_can('edit_post', $post_id)) return $post_id;
if (!current_user_can('upload_files', $post_id)) return $post_id;
// OK, we're authenticated: we need to find and save the data
$checked = "no";
if (isset($_POST['do_remote_archive'])){
$checked = "yes";
}
update_post_meta($post_id, 'ari_enabled', $checked);
return $post_id;
}
// retrieve images from content
function fetch_remote_images($doc){
$save_atts = array('src','alt','title');
$images = array();
$out = simplexml_import_dom($doc);
$img_el_all = $out -> xpath('//img');
foreach ($img_el_all as $img_el) {
$image = array();
foreach($img_el->attributes() as $att => $value) {
if (!in_array($att, $save_atts)) continue;
$value = (string)$value;
if (!$value) continue;
$image[$att] = $value;
}
$image = array_filter($image);
if (!array_key_exists('src', $image)) continue;
if (!self::is_absolute_url($image['src'])) continue; //is relative URL
if (self::is_local_image($image['src'])) continue; //is local image
$images[] = $image;
}
$images = array_filter($images);
return $images;
}
/*
* Archive images on while saving
*/
function save_post_images($post_id, $post){
global $wpdb;
//check save status
$is_autosave = wp_is_post_autosave( $post_id );
$is_revision = wp_is_post_revision( $post_id );
if ($is_revision) return $post_id;
if ($is_autosave) return $post_id;
//capabilities
if (!current_user_can('edit_post', $post_id)) return $post_id;
if (!current_user_can('upload_files', $post_id)) return $post_id;
//checkbox not checked
if (!isset($_POST['do_remote_archive'])) return $post_id;
//script time limit
if ($time_limit = self::get_setting('time_limit')){
set_time_limit($time_limit);
}
//DOMDocument
libxml_use_internal_errors(true); //avoid errors like duplicate IDs
$doc = new DOMDocument( '1.0', get_bloginfo('charset') );
//try to fix bad HTML
$doc->recover = true;
//$doc->strictErrorChecking = false;
$doc->loadHTML($post->post_content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NOBLANKS | LIBXML_NOCDATA | LIBXML_NOXMLDECL | LIBXML_NSCLEAN);
//get images urls in post content
$images = self::fetch_remote_images($doc);
if (empty($images)) return $post_id;
//hooks START (avoid infinite loops, disable revisions)
remove_action('save_post', array( $this, 'save_archiving_status' ));
remove_action( 'save_post', array( $this, 'save_post_images' ),10, 2);
$new_post = array('ID'=>$post_id);wp_update_post( $new_post );//saving post once before disabling revisions
add_filter( 'wp_revisions_to_keep', array( $this, 'disable_post_revisions' ));
add_filter( 'wp_get_attachment_image_attributes', array( $this, 'image_attributes_hook' ),10, 2);
foreach ((array)$images as $image){
$new_post['post_content'] = self::replace_single_image($image, $post, $doc);
//update post
//is inside FOREACH so if the script breaks,
//successfully grabbed images still are replaced in the post content.
wp_update_post( $new_post );
}
//hooks STOP
add_action('save_post', array( $this, 'save_archiving_status' ));
add_action( 'save_post', array( $this, 'save_post_images' ),10, 2);
add_action('pre_post_update', 'wp_save_post_revision');// enable revisions again
remove_filter( 'wp_revisions_to_keep', array( $this, 'disable_post_revisions' ));
remove_filter( 'wp_get_attachment_image_attributes', array( $this, 'image_attributes_hook' ),10, 2);
return $post_id;
}
function is_local_image($url){
$is_local = strpos($url, home_url());
return (bool)($is_local !== false);
if ($is_local !== false) {
return true;
}
}
/**
* Checks the URL is absolute
* @param type $url
*/
function is_absolute_url($url){
$parse = parse_url($url);
if (array_key_exists("host", $parse)) return true;
return false;
}
function is_local_server($url){
if ($_SERVER['REMOTE_ADDR']=='127.0.0.1') return true;
$parse = parse_url($url);
$host_names = explode(".", $parse['host']);
if (!isset($host_names[1])) return true; //no extension, we should be on a local server (like localhost)
return false;
}
/*
* Get domain (without subdomain like www.)
*/
function get_domain($url){
if (!self::is_absolute_url($url)) return false;
$parse = parse_url($url);
$host_names = explode(".", $parse['host']);
if (self::is_local_server($url)){
$domain = $parse['host'];
}else{
$domain = $host_names[count($host_names)-2] . "." . $host_names[count($host_names)-1];
}
return $domain;
}
/*
* Gets the allowed file extensions allowed for
* a specific mime ('image','video','audio',...)
* and an optional type ('jpeg','gif','mp4',...)
*/
function get_allowed_extensions($mime_check,$type_check=false){
$extensions = array();
$allowed = get_allowed_mime_types();
foreach ((array)$allowed as $ext_str => $mimetype_str) {
$mimetype = explode('/',$mimetype_str); //eg. 'image/jpeg'
$mime = $mimetype[0]; //'image'
$type = $mimetype[1]; //'jpeg'
if ( $mime!=$mime_check ) continue;
if (isset($type_check) && ( $type_check!=$type )) continue;
$mimetype_ext = explode('|',$ext_str);
$extensions = array_merge($mimetype_ext,$extensions);
}
return $extensions;
}
/*
* Gets the allowed file extensions allowed for images
*/
function get_allowed_image_extensions(){
return get_allowed_extensions('image');
}
/**
* TO FIX rename / give more informations on this function
* @param type $url
* @param type $image
* @return type
*/
function get_url_from_special_domain($url){
$domain = self::get_domain($url);
$check_domains = array(
'blogspot.com',
'blogger.com',
'ggpht.com',
'googleusercontent.com',
'gstatic.com'
);
if (in_array($domain,$check_domains)){
$response = wp_remote_request($url);
if (!is_wp_error($response)){
$my_body = wp_remote_retrieve_body($response);
if (strpos($my_body, 'src')) {
preg_match_all('||i', $my_body, $matches);
foreach ($matches[1] as $url):
$spisak = $url;
endforeach;
$url = $spisak;
}
}
}
return $url;
}
/*
* Used for disabling revisions temporary
*/
function disable_post_revisions($num){
return 0;
}
/*
* Runs a hook so we can filter the image attributes
*/
function image_attributes_hook($attr, $attachment){
return apply_filters('ari_get_image_attributes',$attr,$attachment);
}
/*
* Adds the class wp-image-ID
* Because it's easier to read when editing the code of the post content
*/
function image_attributes_class_id($attr, $attachment){
$attr['class'].= " wp-image-".$attachment->ID;
return $attr;
}
function get_image_title($image){
$title = '';
if (array_key_exists('title', $image)){
$title = $image['title'];
}elseif (array_key_exists('alt', $image)){
$title = $image['alt'];
}
return apply_filters('ari_get_image_title',$title);
}
function get_existing_attachment_id($img_url){
$query_args = array(
'post_type' => 'attachment',
'post_status' => 'inherit',
'meta_query' => array(
array(
'key' => '_ari-url',
'value' => $img_url,
'compare' => '='
)
),
'posts_per_page' => 1
);
$query = new WP_Query($query_args);
if (!$query->have_posts()) return false;
$id = $query->posts[0]->ID;
return apply_filters("ari_get_existing_attachment_id",$id,$img_url);
}
/**
* Archive image from content
*/
function replace_single_image($image, $post, $doc){
global $wpdb;
$post_content = $post->post_content;
$image_url = $image['src'];
//this image url already has been uploaded
$already_uploaded_id = self::get_existing_attachment_id($image_url);
if ($already_uploaded_id){
$attachment_id = $already_uploaded_id;
}else{
//get image title
$img_title = self::get_image_title($image);
/*
media_sideload_image() do not returns the attachment ID.
hook and unhook a function to get over that.
it that function, we will save the source URL as post meta for the attachment.
The value will be $this->attachment_source, which is only used here.
This is kind of a hack, hope that media_sideload_image() will be able to return
The attachment ID in the future.
https:core.trac.wordpress.org/ticket/19629
*/
//START HACK
$this->attachment_source = $image_url;
add_action('add_attachment',array( $this, 'uploaded_image_save_source' ));
//filter that allows to update the file URL if needed (eg. depending of the domain)
$upload_url = apply_filters('ari_get_remote_image_url',$image_url);
$upload = media_sideload_image($upload_url, $post->ID, $img_title);
//STOP HACK
remove_action('add_attachment',array( $this, 'uploaded_image_save_source' )); //hook
$this->attachment_source = '';
if (!is_wp_error($upload)){
$attachment_id = self::get_existing_attachment_id($image_url);
}
}
if (isset($attachment_id)){
$image_size = self::get_setting('image_size');
$new_image_html = wp_get_attachment_image( $attachment_id, $image_size );
$new_image_html = apply_filters('ari_get_new_image_html',$new_image_html,$attachment_id);
if ($new_image_html){
$imageTags = $doc->getElementsByTagName('img'); //get all images
$new_image_el = $doc->createDocumentFragment();
$new_image_el->appendXML($new_image_html);
foreach ($imageTags as $imageTag){
$imageTag_url = $imageTag->getAttribute('src');
if ($imageTag_url != $image_url) continue;
$parentNode = $imageTag->parentNode;
//replace tag
$parentNode->replaceChild($new_image_el, $imageTag);
//if the parent tag of the image is a link to the (same) image,
//replace that link with a link to the uploaded image.
if (($parentNode->tagName == 'a') && (self::get_setting('replace_parent_link'))){
$link_src = $parentNode->getAttribute('href');
//link url and image source are the same
if ( $link_src == $image_url ){
$linked_image_url = self::get_linked_image_url($attachment_id);
$image_linked_size = self::get_setting('image_linked_size');
$new_linked_image_html = wp_get_attachment_image( $attachment_id, $image_linked_size );
$new_link_html = ''.$new_linked_image_html.'';
$new_link_html = apply_filters('ari_get_new_link_html',$new_link_html,$attachment_id);
if ($new_link_html){
$new_link_el = $doc->createDocumentFragment();
$new_link_el->appendXML($new_link_html);
//replace tag
$parentNode->parentNode->replaceChild($new_link_el, $parentNode);
}
}
}
}
$doc->normalizeDocument();
$post_content = $doc->saveHTML();
}
}
return $post_content;
}
function get_linked_image_url($attachment_id){
$option = self::get_setting('image_linked_target');
switch ($option) {
case 'post': //attachment page
$url = get_attachment_link( $attachment_id );
break;
default : // media url
$url = wp_get_attachment_url( $attachment_id );
}
return apply_filters('ari_get_linked_image_url',$url, $attachment_id);
}
function uploaded_image_save_source($attachment_id){
$source = $this->attachment_source;
//add original URL to attachment, as meta
add_post_meta($attachment_id, '_ari-url',$source);
}
/**
* Count the number of medias already downloaded with Archive Remote Image
* @return int
*/
public function count_archived_attachments($post_id = false){
$query_args = array(
'post_type' => 'attachment',
'meta_key' => '_ari-url',
'posts_per_page' => -1
);
if ($post_id)
$query_args['post_parent'] = $post_id;
$meta_posts = get_posts( $query_args );
$meta_post_count = count( $meta_posts );
unset( $meta_posts);
return (int)$meta_post_count;
}
}
/**
* The main function responsible for returning the one true Instance
* to functions everywhere.
*
* Use this function like you would a global variable, except without needing
* to declare the global.
*
* @return The one true Instance
*/
function ari() {
return ArchiveRemoteImages::instance();
}
if (is_admin()){
ari();
}