File: D:/HostingSpaces/Velosophe/dev.abloc.cc/wwwroot/app/plugins/media-file-renamer/core.php
<?php
class Meow_MFRH_Core {
private $admin = null;
public function __construct( $admin ) {
$this->admin = $admin;
add_action( 'plugins_loaded', array( $this, 'init' ) );
}
function init() {
include( 'api.php' );
include( 'updates.php' );
new Meow_MFRH_Updates( $this, $this->admin );
load_plugin_textdomain( 'media-file-renamer', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
$method = apply_filters( 'mfrh_method', 'media_title' );
if ( $method === 'product_title' && class_exists( 'WooCommerce' ) )
include( 'plugins/woocommerce.php' );
// Those actions/filters are only for the admin screens
if ( is_admin() ) {
add_filter( 'attachment_fields_to_save', array( $this, 'attachment_fields_to_save' ), 20, 2 );
add_action( 'save_post', array( $this, 'save_post' ) );
if ( get_option( 'mfrh_on_upload', false ) ) {
add_filter( 'wp_handle_upload_prefilter', array( $this, 'wp_handle_upload_prefilter' ), 10, 2 );
}
}
}
/**
*
* TOOLS / HELPERS
*
*/
// Check if the file exists, if it is, return the real path for it
// https://stackoverflow.com/questions/3964793/php-case-insensitive-version-of-file-exists
static function sensitive_file_exists( $filename, $fullpath = true, $caseInsensitive = true ) {
$output = false;
$directoryName = mfrh_dirname( $filename );
$fileArray = glob( $directoryName . '/*', GLOB_NOSORT );
$i = ( $caseInsensitive ) ? "i" : "";
// Check if \ is in the string
if ( preg_match( "/\\\|\//", $filename) ) {
$array = preg_split("/\\\|\//", $filename);
$filename = $array[count( $array ) -1];
}
// Compare filenames
foreach ( $fileArray as $file ) {
if ( preg_match( "/" . preg_quote( $filename ) . "$/{$i}", $file ) ) {
$output = $file;
break;
}
}
return $output;
}
static function rmdir_recursive( $directory ) {
foreach ( glob( "{$directory}/*" ) as $file ) {
if ( is_dir( $file ) )
Meow_MFRH_Core::rmdir_recursive( $file );
else
unlink( $file );
}
rmdir( $directory );
}
function wpml_media_is_installed() {
return defined( 'WPML_MEDIA_VERSION' );
}
// To avoid issue with WPML Media for instance
function is_real_media( $id ) {
if ( $this->wpml_media_is_installed() ) {
global $sitepress;
$language = $sitepress->get_default_language( $id );
return icl_object_id( $id, 'attachment', true, $language ) == $id;
}
return true;
}
function is_header_image( $id ) {
static $headers = false;
if ( $headers == false ) {
global $wpdb;
$headers = $wpdb->get_col( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_wp_attachment_is_custom_header'" );
}
return in_array( $id, $headers );
}
function generate_unique_filename( $actual, $dirname, $filename, $counter = null ) {
$new_filename = $filename;
if ( !is_null( $counter ) ) {
$whereisdot = strrpos( $new_filename, '.' );
$new_filename = substr( $new_filename, 0, $whereisdot ) . '-' . $counter
. '.' . substr( $new_filename, $whereisdot + 1 );
}
if ( $actual == $new_filename )
return false;
if ( file_exists( $dirname . "/" . $new_filename ) )
return $this->generate_unique_filename( $actual, $dirname, $filename,
is_null( $counter ) ? 2 : $counter + 1 );
return $new_filename;
}
function get_post_from_media( $id ) {
global $wpdb;
$postid = $wpdb->get_var( $wpdb->prepare( "
SELECT post_parent p
FROM $wpdb->posts p
WHERE ID = %d", $id ),
0, 0 );
if ( empty( $postid ) )
return null;
return get_post( $postid, OBJECT );
}
/**
* Returns all the media sharing the same file
* @param string $file The attached file path
* @param int|array $excludes The post ID(s) to exclude from the results
* @return array An array of IDs
*/
function get_posts_by_attached_file( $file, $excludes = null ) {
global $wpdb;
$r = array ();
$q = <<< SQL
SELECT post_id
FROM {$wpdb->postmeta}
WHERE meta_key = '%s'
AND meta_value = '%s'
SQL;
$rows = $wpdb->get_results( $wpdb->prepare( $q, '_wp_attached_file', _wp_relative_upload_path( $file ) ), OBJECT );
if ( $rows && is_array( $rows ) ) {
if ( !is_array( $excludes ) )
$excludes = $excludes ? array ( (int) $excludes ) : array ();
foreach ( $rows as $item ) {
$id = (int) $item->post_id;
if ( in_array( $id, $excludes ) ) continue;
$r[] = $id;
}
$r = array_unique( $r );
}
return $r;
}
/*****************************************************************************
RENAME ON UPLOAD
*****************************************************************************/
function wp_handle_upload_prefilter( $file ) {
$this->log( "** On Upload: " . $file['name'] );
$pp = mfrh_pathinfo( $file['name'] );
// If everything's fine, renames in based on the Title in the EXIF
$method = apply_filters( 'mfrh_method', 'media_title' );
switch ( $method ) {
case 'media_title':
$exif = wp_read_image_metadata( $file['tmp_name'] );
if ( !empty( $exif ) && isset( $exif[ 'title' ] ) && !empty( $exif[ 'title' ] ) ) {
$new_filename = $this->new_filename( null, $exif[ 'title' ] );
if ( !is_null( $new_filename ) ) {
$file['name'] = "{$new_filename}.{$pp['extension']}";
$this->log( "New file should be: " . $file['name'] );
}
return $file;
}
break;
case 'post_title':
case 'product_title':
if ( !isset( $_POST['post_id'] ) || $_POST['post_id'] < 1 ) break;
$post = get_post( $_POST['post_id'] );
if ( !empty( $post ) && !empty( $post->post_title ) ) {
$new_filename = $this->new_filename( null, $post->post_title );
if ( !is_null( $new_filename ) ) {
$file['name'] = "{$new_filename}.{$pp['extension']}";
$this->log( "New file should be: " . $file['name'] );
}
return $file;
}
break;
}
// Otherwise, let's do the basics based on the filename
// The name will be modified at this point so let's keep it in a global
// and re-inject it later
global $mfrh_title_override;
$mfrh_title_override = $pp['filename'];
add_filter( 'wp_read_image_metadata', array( $this, 'wp_read_image_metadata' ), 10, 2 );
// Modify the filename
$pp = mfrh_pathinfo( $file['name'] );
$new_filename = $this->new_filename( null, $pp['basename'] );
if ( !is_null( $new_filename ) ) $file['name'] = $new_filename;
return $file;
}
function wp_read_image_metadata( $meta, $file ) {
// Override the title, without this it is using the new filename
global $mfrh_title_override;
$meta['title'] = $mfrh_title_override;
return $meta;
}
/****************************************************************************/
// Return false if everything is fine, otherwise return true with an output
// which details the conditions and results about the renaming.
function check_attachment( $post, &$output = array(), $manual_filename = null ) {
$id = $post['ID'];
$old_filepath = get_attached_file( $id );
$old_filepath = Meow_MFRH_Core::sensitive_file_exists( $old_filepath );
$path_parts = mfrh_pathinfo( $old_filepath );
// If the file doesn't exist, let's not go further.
if ( !isset( $path_parts['dirname'] ) || !isset( $path_parts['basename'] ) )
return false;
//print_r( $path_parts );
$directory = $path_parts['dirname'];
$old_filename = $path_parts['basename'];
// Check if media/file is dead
if ( !$old_filepath || !file_exists( $old_filepath ) ) {
delete_post_meta( $id, '_require_file_renaming' );
return false;
}
// Is it forced/manual
// Check mfrh_new_filename (coming from manual input) if it is different than previous filename
if ( empty( $manual_filename ) && isset( $post['mfrh_new_filename'] ) ) {
if ( strtolower( $post['mfrh_new_filename'] ) != strtolower( $old_filename ) )
$manual_filename = $post['mfrh_new_filename'];
}
if ( !empty( $manual_filename ) ) {
$new_filename = $manual_filename;
$output['manual'] = true;
}
else {
$method = apply_filters( 'mfrh_method', 'media_title' );
if ( $method === 'none') {
delete_post_meta( $id, '_require_file_renaming' );
return false;
}
if ( get_post_meta( $id, '_manual_file_renaming', true ) ) {
return false;
}
// Skip header images
if ( $this->is_header_image( $id ) ) {
delete_post_meta( $id, '_require_file_renaming' );
return false;
}
// Get information
$base_title = $post['post_title'];
switch ( $method ) {
case 'post_title':
case 'product_title':
$attachedpost = $this->get_post_from_media( $id );
if ( is_null( $attachedpost ) )
return false;
$base_title = $attachedpost->post_title;
break;
case 'alt_text':
$image_alt = get_post_meta( $id, '_wp_attachment_image_alt', true );
if ( is_null( $image_alt ) )
return false;
$base_title = $image_alt;
break;
}
$new_filename = $this->new_filename( $post, $base_title );
if ( is_null( $new_filename ) ) return false; // Leave it as it is
}
// If a filename has a counter, and the ideal is without the counter, let's ignore it
$ideal = preg_replace( '/-[1-9]{1,10}\./', '$1.', $old_filename );
if ( !$manual_filename ) {
if ( $ideal == $new_filename ) {
delete_post_meta( $id, '_require_file_renaming' );
return false;
}
}
// Filename is equal to sanitized title
if ( $new_filename == $old_filename ) {
delete_post_meta( $id, '_require_file_renaming' );
return false;
}
// Check for case issue, numbering
$new_filepath = trailingslashit( $directory ) . $new_filename;
$existing_file = Meow_MFRH_Core::sensitive_file_exists( $new_filepath );
$case_issue = strtolower( $old_filename ) == strtolower( $new_filename );
if ( $existing_file && !$case_issue ) {
$is_numbered = apply_filters( 'mfrh_numbered', false );
if ( $is_numbered ) {
$new_filename = $this->generate_unique_filename( $ideal, $directory, $new_filename );
if ( !$new_filename ) {
delete_post_meta( $id, '_require_file_renaming' );
return false;
}
$new_filepath = trailingslashit( $directory ) . $new_filename;
}
}
// Send info to the requester function
$output['post_id'] = $id;
$output['post_name'] = $post['post_name'];
$output['post_title'] = $post['post_title'];
$output['current_filename'] = $old_filename;
$output['current_filepath'] = $old_filepath;
$output['desired_filename'] = $new_filename;
$output['desired_filepath'] = $new_filepath;
$output['case_issue'] = $case_issue;
$output['manual'] = !empty( $manual_filename );
$output['locked'] = get_post_meta( $id, '_manual_file_renaming', true );
$output['desired_filename_exists'] = !!$existing_file;
$output['original_image'] = null;
// If the ideal filename already exists
// Maybe that's the original_image! If yes, we should let it go through
// as the original_rename will be renamed into another filename anyway.
if ( !!$existing_file ) {
$meta = wp_get_attachment_metadata( $id );
if ( isset( $meta['original_image'] ) && $new_filename === $meta['original_image'] ) {
$output['original_image'] = $meta['original_image'];
$output['desired_filename_exists'] = false;
}
}
// Set the '_require_file_renaming', even though it's not really used at this point.
if ( !get_post_meta( $post['ID'], '_require_file_renaming' ) && $output['desired_filename_exists'] && !$output['locked']) {
add_post_meta( $post['ID'], '_require_file_renaming', true, true );
}
return true;
}
function check_text() {
$issues = array();
global $wpdb;
$ids = $wpdb->get_col( "
SELECT p.ID
FROM $wpdb->posts p
WHERE post_status = 'inherit'
AND post_type = 'attachment'
" );
foreach ( $ids as $id )
if ( $this->check_attachment( get_post( $id, ARRAY_A ), $output ) )
array_push( $issues, $output );
return $issues;
}
/**
*
* RENAME ON SAVE / PUBLISH
* Originally proposed by Ben Heller
* Added and modified by Jordy Meow
*/
function save_post( $post_id ) {
$status = get_post_status( $post_id );
if ( !in_array( $status, array( 'publish', 'draft', 'future', 'private' ) ) )
return;
$onsave = get_option( "mfrh_rename_on_save" );
if ( !$onsave )
return;
$args = array( 'post_type' => 'attachment', 'numberposts' => -1, 'post_status' =>'any', 'post_parent' => $post_id );
$medias = get_posts( $args );
if ( $medias ) {
$this->log( '[save_post]' );
foreach ( $medias as $attach ) {
// In the past, I used this to detect if the Media Library is NOT used:
// isset( $attachment['image-size'] );
$this->rename( $attach->ID );
}
}
}
/**
*
* EDITOR
*
*/
function attachment_fields_to_save( $post, $attachment ) {
$this->log( '[attachment_fields_to_save]' );
$post = $this->rename( $post );
return $post;
}
function log_sql( $data, $antidata ) {
if ( !get_option( 'mfrh_logsql' ) || !$this->admin->is_registered() )
return;
$fh = fopen( trailingslashit( dirname(__FILE__) ) . 'mfrh_sql.log', 'a' );
$fh_anti = fopen( trailingslashit( dirname(__FILE__) ) . 'mfrh_sql_revert.log', 'a' );
$date = date( "Y-m-d H:i:s" );
fwrite( $fh, "{$data}\n" );
fwrite( $fh_anti, "{$antidata}\n" );
fclose( $fh );
fclose( $fh_anti );
}
function log( $data, $inErrorLog = false ) {
if ( $inErrorLog )
error_log( $data );
if ( !get_option( 'mfrh_log' ) ) {
return;
}
$fh = fopen( trailingslashit( dirname(__FILE__) ) . 'media-file-renamer.log', 'a' );
$date = date( "Y-m-d H:i:s" );
fwrite( $fh, "$date: {$data}\n" );
fclose( $fh );
}
/**
*
* GENERATE A NEW FILENAME
*
*/
/**
* Performs transliteration
* @param string $str The string to transliterate
* @return string
*/
function transliterate( $str ) {
// Conversion table
static $chars = null;
if ( is_null($chars) ) {
$chars = array (
/** Cyrillics **/
'А' => 'A', 'Б' => 'B', 'В' => 'V', 'Г' => 'G',
'Д' => 'D', 'Е' => 'E', 'Ё' => 'E', 'Ж' => 'Zh',
'З' => 'Z', 'И' => 'I', 'Й' => 'I', 'К' => 'K',
'Л' => 'L', 'М' => 'M', 'Н' => 'N', 'О' => 'O',
'П' => 'P', 'Р' => 'R', 'С' => 'S', 'Т' => 'T',
'У' => 'U', 'Ф' => 'F', 'Х' => 'Kh', 'Ц' => 'Ts',
'Ч' => 'Ch', 'Ш' => 'Sh', 'Щ' => 'Shch', 'Ъ' => 'Ie',
'Ы' => 'Y', 'Ь' => '', 'Э' => 'E', 'Ю' => 'Iu',
'Я' => 'Ia',
'а' => 'a', 'б' => 'b', 'в' => 'v', 'г' => 'g',
'д' => 'd', 'е' => 'e', 'ё' => 'e', 'ж' => 'zh',
'з' => 'z', 'и' => 'i', 'й' => 'i', 'к' => 'k',
'л' => 'l', 'м' => 'm', 'н' => 'n', 'о' => 'o',
'п' => 'p', 'р' => 'r', 'с' => 's', 'т' => 't',
'у' => 'u', 'ф' => 'f', 'х' => 'kh', 'ц' => 'ts',
'ч' => 'ch', 'ш' => 'sh', 'щ' => 'shch', 'ъ' => 'ie',
'ы' => 'y', 'ь' => '', 'э' => 'e', 'ю' => 'iu',
'я' => 'ia'
);
}
// Preform conversion
foreach ( $chars as $from => $to )
$str = str_replace( $from, $to, $str );
return $str;
}
/**
* Removes some unicode puncuation characters from a string.
* The conversion table derived from `sanitize_title_with_dashes()`
* @param string $str The string to remove from
* @return string
* @see sanitize_title_with_dashes()
*/
function remove_special_chars( $str ) {
// Conversion table
static $chars = null;
if ( is_null( $chars ) ) {
$chars = array (
// iexcl and iquest
'%c2%a1', '%c2%bf',
// angle quotes
'%c2%ab', '%c2%bb', '%e2%80%b9', '%e2%80%ba',
// curly quotes
'%e2%80%98', '%e2%80%99', '%e2%80%9c', '%e2%80%9d',
'%e2%80%9a', '%e2%80%9b', '%e2%80%9e', '%e2%80%9f',
// copy, reg, deg, hellip and trade
'%c2%a9', '%c2%ae', '%c2%b0', '%e2%80%a6', '%e2%84%a2',
// acute accents
'%c2%b4', '%cb%8a', '%cc%81', '%cd%81',
// grave accent, macron, caron
'%cc%80', '%cc%84', '%cc%8c',
);
$chars = array_map( 'urldecode', $chars );
// Combining Diacritical Marks (U+0300 - U+036F)
// @see http://www.fileformat.info/info/unicode/block/combining_diacritical_marks/list.htm
for ( $i = 0x0300; $i <= 0x036f; $i++ ) $chars[] = $this->u( $i );
// Combining Diacritical Marks Extended (U+1AB0 - U+1ABE)
// @see http://www.fileformat.info/info/unicode/block/combining_diacritical_marks_extended/list.htm
for ( $i = 0x1ab0; $i <= 0x1abe; $i++ ) $chars[] = $this->u( $i );
// Combining Diacritical Marks Supplement (U+1DC0 - U+1DEF)
// @see http://www.fileformat.info/info/unicode/block/combining_diacritical_marks_supplement/list.htm
for ( $i = 0x1dc0; $i <= 0x1def; $i++ ) $chars[] = $this->u( $i );
// Common Diacrical Marks
$x = array (
// Circumflex
0x005e, 0x02c6,
// Low Circumflex
0xa788,
// Tilde
0x007e, 0x02dc,
// Low Tilde
0x02f7,
);
$chars = array_merge( $chars, array_map( array ( $this, 'u' ), $x ) );
}
// Preform conversion
foreach ( $chars as $char )
$str = str_replace( $char, '', $str );
return $str;
}
/**
* Returns a decoded unicode character from its code point
* @param int $code_point
* @return string
*/
function u( $code_point ) {
$u = str_pad( dechex( $code_point ), 4, '0', STR_PAD_LEFT ); // 4 digits hexadecimal string
return json_decode( '"\u' . $u . '"' );
}
function replace_chars( $str ) {
$special_chars = array();
$special_chars = apply_filters( 'mfrh_replace_rules', $special_chars );
if ( !empty( $special_chars ) )
foreach ( $special_chars as $key => $value )
$str = str_replace( $key, $value, $str );
return $str;
}
/**
* Transform full width hyphens and other variety hyphens in half size into simple hyphen,
* and avoid consecutive hyphens and also at the beginning and end as well.
*/
function format_hyphens( $str ) {
$hyphen = '-';
$hyphens = [
'﹣', '-', '−', '⁻', '₋',
'‐', '‑', '‒', '–', '—',
'―', '﹘', 'ー','ー',
];
$str = str_replace( $hyphens, $hyphen, $str );
// remove at the beginning and end.
$beginning = mb_substr( $str, 0, 1 );
if ( $beginning === $hyphen ) {
$str = mb_substr( $str, 1 );
}
$end = mb_substr( $str, -1 );
if ( $end === $hyphen ) {
$str = mb_strcut( $str, 0, mb_strlen( $str ) - 1 );
}
$str = preg_replace( '/-{2,}/u', '-', $str );
$str = trim( $str, implode( '', $hyphens ) );
return $str;
}
/**
* Computes the ideal filename based on a text
* @param array $media
* @param string $text
* @param string $manual_filename
* @return string|NULL If the resulting filename had no any valid characters, NULL is returned
*/
function new_filename( $media, $text, $manual_filename = null ) {
$old_filename = null;
$old_filename_no_ext = null;
$new_ext = null;
if ( !empty( $media ) ) {
// Media already exists (not a fresh upload). Gets filename and ext.
$old_filepath = get_attached_file( $media['ID'] );
$pp = mfrh_pathinfo( $old_filepath );
$new_ext = empty( $pp['extension'] ) ? "" : $pp['extension'];
$old_filename = $pp['basename'];
$old_filename_no_ext = $pp['filename'];
}
else {
// It's an upload, let's check if the extension is provided in the text
$pp = mfrh_pathinfo( $text );
$new_ext = empty( $pp['extension'] ) ? "" : $pp['extension'];
$text = $pp['filename'];
}
// Generate the new filename.
if ( !empty( $manual_filename ) ) {
// Filename is forced. Strip the extension. Keeps this extension in $new_ext.
$pp = mfrh_pathinfo( $manual_filename );
$manual_filename = $pp['filename'];
$new_ext = empty( $pp['extension'] ) ? $new_ext : $pp['extension'];
$new_filename = $manual_filename;
}
else {
// Filename is generated from $text, without an extension.
$text = str_replace( ".jpg", "", $text );
$text = str_replace( ".png", "", $text );
$text = str_replace( ['.','…'], "", $text );
$text = str_replace( "'s", "", $text );
$text = str_replace( "'", "-", $text );
$text = $this->replace_chars( $text );
// Changed strolower to mb_strtolower...
$text = mb_strtolower( $text );
$text = sanitize_file_name( $text );
$new_filename = $this->format_hyphens( $text );
$new_filename = trim( $new_filename, '-.' );
}
// Convert all accent characters to ASCII characters
if ( apply_filters( 'mfrh_converts', false ) ) {
$new_filename = remove_accents( $new_filename );
$new_filename = $this->remove_special_chars( $new_filename );
$new_filename = $this->transliterate( $new_filename );
$new_filename = strtolower( $new_filename );
}
// If the resulting filename had no any valid character, return NULL
if ( empty( $new_filename ) ) return null;
if ( !$manual_filename )
$new_filename = apply_filters( 'mfrh_new_filename', $new_filename, $old_filename_no_ext, $media );
// We know have a new filename, let's add an extension.
$new_filename = !empty( $new_ext ) ? ( $new_filename . '.' . $new_ext ) : $new_filename;
return $new_filename;
}
// Only replace the first occurence
function str_replace( $needle, $replace, $haystack ) {
$pos = strpos( $haystack, $needle );
if ( $pos !== false )
$haystack = substr_replace( $haystack, $replace, $pos, strlen( $needle ) );
return $haystack;
}
/**
*
* RENAME FILES + COFFEE TIME
*/
function call_hooks_rename_url( $post, $orig_image_url, $new_image_url ) {
// With the full URLs
do_action( 'mfrh_url_renamed', $post, $orig_image_url, $new_image_url );
// With DB URLs
$upload_dir = wp_upload_dir();
do_action( 'mfrh_url_renamed', $post, str_replace( $upload_dir, "", $orig_image_url ),
str_replace( $upload_dir, "", $new_image_url ) );
}
function rename_file( $old, $new, $case_issue = false ) {
// Some plugins can create custom thumbnail folders instead in the same folder, so make sure
// the thumbnail folders are available.
wp_mkdir_p( dirname($new) );
// If there is a case issue, that means the system doesn't make the difference between AA.jpg and aa.jpg even though WordPress does.
// In that case it is important to rename the file to a temporary filename in between like: AA.jpg -> TMP.jpg -> aa.jpg.
if ( $case_issue ) {
if ( !rename( $old, $old . md5( $old ) ) ) {
$this->log( "The file couldn't be renamed (case issue) from $old to " . $old . md5( $old ) . "." );
return false;
}
if ( !rename( $old . md5( $old ), $new ) ) {
$this->log( "The file couldn't be renamed (case issue) from " . $old . md5( $old ) . " to $new." );
return false;
}
}
else if ( ( !rename( $old, $new ) ) ) {
$this->log( "The file couldn't be renamed from $old to $new." );
return false;
}
return true;
}
function move( $media, $newPath ) {
$id = null;
$post = null;
// Check the arguments
if ( is_numeric( $media ) ) {
$id = $media;
$post = get_post( $media, ARRAY_A );
}
else if ( is_array( $media ) ) {
$id = $media['ID'];
$post = $media;
}
else {
die( 'Media File Renamer: move() requires the ID or the array for the media.' );
}
// Prepare the variables
$old_filepath = get_attached_file( $id );
$path_parts = mfrh_pathinfo( $old_filepath );
$old_ext = $path_parts['extension'];
$upload_dir = wp_upload_dir();
$old_directory = trim( str_replace( $upload_dir['basedir'], '', $path_parts['dirname'] ), '/' ); // '2011/01'
$new_directory = trim( $newPath, '/' );
$filename = $path_parts['basename']; // 'whatever.jpeg'
$new_filepath = trailingslashit( trailingslashit( $upload_dir['basedir'] ) . $new_directory ) . $filename;
$this->log( "** Move Media: " . $filename );
$this->log( "The new directory will be: " . mfrh_dirname( $new_filepath ) );
// Create the directory if it does not exist
if ( !file_exists( mfrh_dirname( $new_filepath ) ) ) {
mkdir( mfrh_dirname( $new_filepath ), 0777, true );
}
// There is no support for UNDO (as the current process of Media File Renamer doesn't keep the path for the undo, only the filename... so the move breaks this - let's deal with this later).
// Move the main media file
if ( !$this->rename_file( $old_filepath, $new_filepath ) ) {
$this->log( "[!] File\t$old_filepath -> $new_filepath" );
return false;
}
$this->log( "File\t$old_filepath -> $new_filepath" );
do_action( 'mfrh_path_renamed', $post, $old_filepath, $new_filepath );
// Update the attachment meta
$meta = wp_get_attachment_metadata( $id );
if ( $meta ) {
if ( isset( $meta['file'] ) && !empty( $meta['file'] ) )
$meta['file'] = $this->str_replace( $old_directory, $new_directory, $meta['file'] );
if ( isset( $meta['url'] ) && !empty( $meta['url'] ) && count( $meta['url'] ) > 4 )
$meta['url'] = $this->str_replace( $old_directory, $new_directory, $meta['url'] );
}
// Better to check like this rather than with wp_attachment_is_image
// PDFs also have thumbnails now, since WP 4.7
$has_thumbnails = isset( $meta['sizes'] );
if ( $has_thumbnails ) {
$orig_image_urls = array();
$orig_image_data = wp_get_attachment_image_src( $id, 'full' );
$orig_image_urls['full'] = $orig_image_data[0];
foreach ( $meta['sizes'] as $size => $meta_size ) {
if ( !isset($meta['sizes'][$size]['file'] ) )
continue;
$meta_old_filename = $meta['sizes'][$size]['file'];
$meta_old_filepath = trailingslashit( $upload_dir['basedir'] ) . trailingslashit( $old_directory ) . $meta_old_filename;
$meta_new_filepath = trailingslashit( $upload_dir['basedir'] ) . trailingslashit( $new_directory ) . $meta_old_filename;
$orig_image_data = wp_get_attachment_image_src( $id, $size );
$orig_image_urls[$size] = $orig_image_data[0];
// Double check files exist before trying to rename.
if ( file_exists( $meta_old_filepath )
&& ( ( !file_exists( $meta_new_filepath ) ) || is_writable( $meta_new_filepath ) ) ) {
// WP Retina 2x is detected, let's rename those files as well
if ( function_exists( 'wr2x_get_retina' ) ) {
$wr2x_old_filepath = $this->str_replace( '.' . $old_ext, '@2x.' . $old_ext, $meta_old_filepath );
$wr2x_new_filepath = $this->str_replace( '.' . $old_ext, '@2x.' . $old_ext, $meta_new_filepath );
if ( file_exists( $wr2x_old_filepath )
&& ( ( !file_exists( $wr2x_new_filepath ) ) || is_writable( $wr2x_new_filepath ) ) ) {
// Rename retina file
if ( !$this->rename_file( $wr2x_old_filepath, $wr2x_new_filepath ) ) {
$this->log( "[!] Retina $wr2x_old_filepath -> $wr2x_new_filepath" );
return $post;
}
$this->log( "Retina\t$wr2x_old_filepath -> $wr2x_new_filepath" );
do_action( 'mfrh_path_renamed', $post, $wr2x_old_filepath, $wr2x_new_filepath );
}
}
// Rename meta file
if ( !$this->rename_file( $meta_old_filepath, $meta_new_filepath ) ) {
$this->log( "[!] File $meta_old_filepath -> $meta_new_filepath" );
return false;
}
// Success, call other plugins
$this->log( "File\t$meta_old_filepath -> $meta_new_filepath" );
do_action( 'mfrh_path_renamed', $post, $meta_old_filepath, $meta_new_filepath );
}
}
}
else {
$orig_attachment_url = wp_get_attachment_url( $id );
}
// Update metadata
if ( $meta )
wp_update_attachment_metadata( $id, $meta );
update_attached_file( $id, $new_filepath );
// I wonder about cleaning the cache for this media. It might have no impact, and will not reset the cache for the posts using this media anyway, and it adds processing time. I keep it for now, but there might be something better to do.
clean_post_cache( $id );
// Call the actions so that the plugin's plugins can update everything else (than the files)
if ( $has_thumbnails ) {
$orig_image_url = $orig_image_urls['full'];
$new_image_data = wp_get_attachment_image_src( $id, 'full' );
$new_image_url = $new_image_data[0];
$this->call_hooks_rename_url( $post, $orig_image_url, $new_image_url );
if ( !empty( $meta['sizes'] ) ) {
foreach ( $meta['sizes'] as $size => $meta_size ) {
$orig_image_url = $orig_image_urls[$size];
$new_image_data = wp_get_attachment_image_src( $id, $size );
$new_image_url = $new_image_data[0];
$this->call_hooks_rename_url( $post, $orig_image_url, $new_image_url );
}
}
}
else {
$new_attachment_url = wp_get_attachment_url( $id );
$this->call_hooks_rename_url( $post, $orig_attachment_url, $new_attachment_url );
}
do_action( 'mfrh_media_renamed', $post, $old_filepath, $new_filepath );
return true;
}
function rename( $media, $manual_filename = null, $fromMediaLibrary = true ) {
$id = null;
$post = null;
// Check the arguments
if ( is_numeric( $media ) ) {
$id = $media;
$post = get_post( $media, ARRAY_A );
}
else if ( is_array( $media ) ) {
$id = $media['ID'];
$post = $media;
}
else {
die( 'Media File Renamer: rename() requires the ID or the array for the media.' );
}
$force_rename = apply_filters( 'mfrh_force_rename', false );
$method = apply_filters( 'mfrh_method', 'media_title' );
// Check attachment
$need_rename = $this->check_attachment( $post, $output, $manual_filename );
if ( !$need_rename ) {
delete_post_meta( $id, '_require_file_renaming' );
return $post;
}
// Prepare the variables
$old_filepath = $output['current_filepath'];
$case_issue = $output['case_issue'];
$new_filepath = $output['desired_filepath'];
$new_filename = $output['desired_filename'];
$manual = $output['manual'] || !empty( $manual_filename );
$path_parts = mfrh_pathinfo( $old_filepath );
$directory = $path_parts['dirname']; // Directory where the files are, under 'uploads', such as '2011/01'
$old_filename = $path_parts['basename']; // 'whatever.jpeg'
// Get old extension and new extension
$old_ext = $path_parts['extension'];
$new_ext = $old_ext;
if ( $manual_filename ) {
$pp = mfrh_pathinfo( $manual_filename );
$new_ext = $pp['extension'];
}
$noext_old_filename = $this->str_replace( '.' . $old_ext, '', $old_filename ); // Old filename without extension
$noext_new_filename = $this->str_replace( '.' . $old_ext, '', $new_filename ); // New filename without extension
$this->log( "** Rename Media: " . $old_filename );
$this->log( "New file should be: " . $new_filename );
// Check for issues with the files
if ( !file_exists( $old_filepath ) ) {
$this->log( "The original file ($old_filepath) cannot be found." );
return $post;
}
// Get the attachment meta
$meta = wp_get_attachment_metadata( $id );
// Get the information about the original image
// (which means the current file is a rescaled version of it)
$has_original_image = isset( $meta['original_image'] ) && !empty( $meta['original_image'] );
$original_is_ideal = $has_original_image ? $new_filename === $meta['original_image'] : false;
if ( !$original_is_ideal && !$case_issue && !$force_rename && file_exists( $new_filepath ) ) {
$this->log( "The new file already exists ($new_filepath). It is not a case issue. Renaming cancelled." );
return $post;
}
// Keep the original filename (that's for the "Undo" feature)
$original_filename = get_post_meta( $id, '_original_filename', true );
if ( empty( $original_filename ) )
add_post_meta( $id, '_original_filename', $old_filename, true );
// Support for the original image if it was "-rescaled".
// We should rename the -rescaled image first, as it could cause an issue
// if renamed after the main file. In fact, the original file might have already
// the best filename and evidently, the "-rescaled" one not.
if ( $has_original_image ) {
$meta_old_filename = $meta['original_image'];
$meta_old_filepath = trailingslashit( $directory ) . $meta_old_filename;
// In case of the undo, since we do not have the actual real original filename for that un-scaled image,
// we make sure the -scaled part of the original filename is not used (that could bring some confusion otherwise).
$meta_new_filename = str_replace( '-scaled', '', $noext_new_filename ) . '-mfrh-original.' . $new_ext;
$meta_new_filepath = trailingslashit( $directory ) . $meta_new_filename;
if ( !$this->rename_file( $meta_old_filepath, $meta_new_filepath, $case_issue ) && !$force_rename ) {
$this->log( "[!] File $meta_old_filepath -> $meta_new_filepath" );
return $post;
}
// Manual Rename also uses the new extension (if it was not stripped to avoid user mistake)
if ( $force_rename && !empty( $new_ext ) ) {
$meta_new_filename = $this->str_replace( $old_ext, $new_ext, $meta_new_filename );
}
$this->log( "Original File\t$old_filepath -> $new_filepath" );
//do_action( 'mfrh_path_renamed', $post, $old_filepath, $new_filepath );
$meta['original_image'] = $meta_new_filename;
}
// Rename the main media file.
if ( !$this->rename_file( $old_filepath, $new_filepath, $case_issue ) && !$force_rename ) {
$this->log( "[!] File $old_filepath -> $new_filepath" );
return $post;
}
$this->log( "File\t$old_filepath -> $new_filepath" );
do_action( 'mfrh_path_renamed', $post, $old_filepath, $new_filepath );
// Rename the main media file in WebP if it exists.
$this->rename_webp_file_if_exist(
$old_filepath,
$old_ext,
$new_filepath,
$new_ext,
$case_issue,
$force_rename,
$post
);
if ( $meta ) {
if ( isset( $meta['file'] ) && !empty( $meta['file'] ) )
$meta['file'] = $this->str_replace( $noext_old_filename, $noext_new_filename, $meta['file'] );
if ( isset( $meta['url'] ) && !empty( $meta['url'] ) && strlen( $meta['url'] ) > 4 )
$meta['url'] = $this->str_replace( $noext_old_filename, $noext_new_filename, $meta['url'] );
else
$meta['url'] = $noext_new_filename . '.' . $old_ext;
}
// Better to check like this rather than with wp_attachment_is_image
// PDFs also have thumbnails now, since WP 4.7
$has_thumbnails = isset( $meta['sizes'] );
// Loop through the different sizes in the case of an image, and rename them.
if ( $has_thumbnails ) {
$orig_image_urls = array();
$orig_image_data = wp_get_attachment_image_src( $id, 'full' );
$orig_image_urls['full'] = $orig_image_data[0];
foreach ( $meta['sizes'] as $size => $meta_size ) {
if ( !isset($meta['sizes'][$size]['file'] ) )
continue;
$meta_old_filename = $meta['sizes'][$size]['file'];
$meta_old_filepath = trailingslashit( $directory ) . $meta_old_filename;
$meta_new_filename = $this->str_replace( $noext_old_filename, $noext_new_filename, $meta_old_filename );
// Manual Rename also uses the new extension (if it was not stripped to avoid user mistake)
if ( $force_rename && !empty( $new_ext ) ) {
$meta_new_filename = $this->str_replace( $old_ext, $new_ext, $meta_new_filename );
}
$meta_new_filepath = trailingslashit( $directory ) . $meta_new_filename;
$orig_image_data = wp_get_attachment_image_src( $id, $size );
$orig_image_urls[$size] = $orig_image_data[0];
// Double check files exist before trying to rename.
if ( $force_rename || ( file_exists( $meta_old_filepath ) &&
( ( !file_exists( $meta_new_filepath ) ) || is_writable( $meta_new_filepath ) ) ) ) {
// WP Retina 2x is detected, let's rename those files as well
if ( function_exists( 'wr2x_get_retina' ) ) {
$wr2x_old_filepath = $this->str_replace( '.' . $old_ext, '@2x.' . $old_ext, $meta_old_filepath );
$wr2x_new_filepath = $this->str_replace( '.' . $new_ext, '@2x.' . $new_ext, $meta_new_filepath );
if ( file_exists( $wr2x_old_filepath )
&& ( ( !file_exists( $wr2x_new_filepath ) ) || is_writable( $wr2x_new_filepath ) ) ) {
// Rename retina file
if ( !$this->rename_file( $wr2x_old_filepath, $wr2x_new_filepath, $case_issue ) && !$force_rename ) {
$this->log( "[!] Retina $wr2x_old_filepath -> $wr2x_new_filepath" );
return $post;
}
$this->log( "Retina\t$wr2x_old_filepath -> $wr2x_new_filepath" );
do_action( 'mfrh_path_renamed', $post, $wr2x_old_filepath, $wr2x_new_filepath );
}
}
// If webp file existed, that one as well.
$this->rename_webp_file_if_exist(
$meta_old_filepath,
$old_ext,
$meta_new_filepath,
$new_ext,
$case_issue,
$force_rename,
$post
);
// Rename meta file
if ( !$this->rename_file( $meta_old_filepath, $meta_new_filepath, $case_issue ) && !$force_rename ) {
$this->log( "[!] File $meta_old_filepath -> $meta_new_filepath" );
return $post;
}
$meta['sizes'][$size]['file'] = $meta_new_filename;
// Detect if another size has exactly the same filename
foreach ( $meta['sizes'] as $s => $m ) {
if ( !isset( $meta['sizes'][$s]['file'] ) )
continue;
if ( $meta['sizes'][$s]['file'] == $meta_old_filename ) {
$this->log( "Updated $s based on $size, as they use the same file (probably same size)." );
$meta['sizes'][$s]['file'] = $meta_new_filename;
}
}
// Success, call other plugins
$this->log( "File\t$meta_old_filepath -> $meta_new_filepath" );
do_action( 'mfrh_path_renamed', $post, $meta_old_filepath, $meta_new_filepath );
}
}
}
else {
$orig_attachment_url = wp_get_attachment_url( $id );
}
// This media doesn't require renaming anymore
delete_post_meta( $id, '_require_file_renaming' );
// If it was renamed manually (including undo), lock the file
if ( $manual )
add_post_meta( $id, '_manual_file_renaming', true, true );
// Update metadata
if ( $meta )
wp_update_attachment_metadata( $id, $meta );
update_attached_file( $id, $new_filepath );
// I wonder about cleaning the cache for this media. It might have no impact, and will not reset the cache for the posts using this media anyway, and it adds processing time. I keep it for now, but there might be something better to do.
clean_post_cache( $id );
// Rename slug/permalink
if ( get_option( "mfrh_rename_slug" ) ) {
$oldslug = $post['post_name'];
$info = mfrh_pathinfo( $new_filepath );
$newslug = preg_replace( '/\\.[^.\\s]{3,4}$/', '', $info['basename'] );
$post['post_name'] = $newslug;
if ( wp_update_post( $post ) )
$this->log( "Slug\t$oldslug -> $newslug" );
}
// Call the actions so that the plugin's plugins can update everything else (than the files)
if ( $has_thumbnails ) {
$orig_image_url = $orig_image_urls['full'];
$new_image_data = wp_get_attachment_image_src( $id, 'full' );
$new_image_url = $new_image_data[0];
$this->call_hooks_rename_url( $post, $orig_image_url, $new_image_url );
if ( !empty( $meta['sizes'] ) ) {
foreach ( $meta['sizes'] as $size => $meta_size ) {
$orig_image_url = $orig_image_urls[$size];
$new_image_data = wp_get_attachment_image_src( $id, $size );
$new_image_url = $new_image_data[0];
$this->call_hooks_rename_url( $post, $orig_image_url, $new_image_url );
}
}
}
else {
$new_attachment_url = wp_get_attachment_url( $id );
$this->call_hooks_rename_url( $post, $orig_attachment_url, $new_attachment_url );
}
// HTTP REFERER set to the new media link
if ( isset( $_REQUEST['_wp_original_http_referer'] ) &&
strpos( $_REQUEST['_wp_original_http_referer'], '/wp-admin/' ) === false ) {
$_REQUEST['_wp_original_http_referer'] = get_permalink( $id );
}
do_action( 'mfrh_media_renamed', $post, $old_filepath, $new_filepath );
return $post;
}
/**
* Rename webp file only if existed.
*/
function rename_webp_file_if_exist(
$old_filepath,
$old_ext,
$new_finepath,
$new_ext,
$case_issue,
$force_rename,
$post
) {
// 2 patterns exist.
// - filename.webp
// - filename.ext.webp
$webp_files = [
[
'old' => $this->str_replace( '.' . $old_ext, '.webp', $old_filepath ),
'new' => $this->str_replace( '.' . $new_ext, '.webp', $new_finepath ),
],
[
'old' => $this->str_replace( '.' . $old_ext, '.' . $old_ext . '.webp', $old_filepath ),
'new' => $this->str_replace( '.' . $new_ext, '.' . $new_ext . '.webp', $new_finepath ),
],
];
foreach ($webp_files as $webp_file) {
if ( file_exists( $webp_file['old'] )
&& ( ( !file_exists( $webp_file['new'] ) ) || is_writable( $webp_file['new'] ) ) ) {
// Rename webp file
if ( !$this->rename_file( $webp_file['old'], $webp_file['new'], $case_issue ) && !$force_rename ) {
$this->log( "[!] Retina $webp_file[old] -> $webp_file[new]" );
return $post;
}
$this->log( "Retina\t$webp_file[old] -> $webp_file[new]" );
do_action( 'mfrh_path_renamed', $post, $webp_file['old'], $webp_file['new'] );
}
}
}
/**
* Locks a post to be manual-rename only
* @param int|WP_Post $post The post to lock
* @return True on success, false on failure
*/
function lock( $post ) {
return !!add_post_meta( $post instanceof WP_Post ? $post->ID : $post, '_manual_file_renaming', true, true );
}
/**
* Unlocks a locked post
* @param int|WP_Post $post The post to unlock
* @return True on success, false on failure
*/
function unlock( $post ) {
return delete_post_meta( $post instanceof WP_Post ? $post->ID : $post, '_manual_file_renaming' );
}
/**
* Determines whether a post is locked
* @param int|WP_Post $post The post to check
* @return Boolean
*/
function is_locked( $post ) {
return get_post_meta( $post instanceof WP_Post ? $post->ID : $post, '_manual_file_renaming', true ) === true;
}
}