718 lines
21 KiB
PHP
718 lines
21 KiB
PHP
<?php
|
||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||
|
||
/**
|
||
* Class handling everything that is related to "custom folders optimization".
|
||
*
|
||
* @since 1.7
|
||
* @author Grégory Viguier
|
||
*/
|
||
class Imagify_Files_Scan {
|
||
|
||
/**
|
||
* Class version.
|
||
*
|
||
* @var string
|
||
* @since 1.7
|
||
* @author Grégory Viguier
|
||
*/
|
||
const VERSION = '1.1.1';
|
||
|
||
/**
|
||
* Get files (optimizable by Imagify) recursively from a specific folder.
|
||
*
|
||
* @since 1.7
|
||
* @access public
|
||
* @author Grégory Viguier
|
||
*
|
||
* @param string $folder An absolute path to a folder.
|
||
* @return array|object An array of absolute paths. A WP_Error object on error.
|
||
*/
|
||
public static function get_files_from_folder( $folder ) {
|
||
$filesystem = imagify_get_filesystem();
|
||
|
||
// Formate and validate the folder path.
|
||
if ( ! is_string( $folder ) ) {
|
||
return new WP_Error( 'invalid_folder', __( 'Invalid folder.', 'imagify' ) );
|
||
}
|
||
|
||
$folder = realpath( $folder );
|
||
|
||
if ( ! $folder ) {
|
||
return new WP_Error( 'folder_not_exists', __( 'This folder does not exist.', 'imagify' ) );
|
||
}
|
||
|
||
if ( ! $filesystem->is_dir( $folder ) ) {
|
||
return new WP_Error( 'not_a_folder', __( 'This file is not a folder.', 'imagify' ) );
|
||
}
|
||
|
||
if ( self::is_path_forbidden( trailingslashit( $folder ) ) ) {
|
||
return new WP_Error( 'folder_forbidden', __( 'This folder is not allowed.', 'imagify' ) );
|
||
}
|
||
|
||
// Finally we made all our validations.
|
||
if ( $filesystem->is_site_root( $folder ) ) {
|
||
// For the site's root, we don't look in sub-folders.
|
||
$dir = new DirectoryIterator( $folder );
|
||
$dir = new Imagify_Files_Iterator( $dir, false );
|
||
$images = array();
|
||
|
||
foreach ( new IteratorIterator( $dir ) as $file ) {
|
||
$images[] = $file->getPathname();
|
||
}
|
||
|
||
return $images;
|
||
}
|
||
|
||
/**
|
||
* 4096 stands for FilesystemIterator::SKIP_DOTS, which was introduced in php 5.3.0.
|
||
* 8192 stands for FilesystemIterator::UNIX_PATHS, which was introduced in php 5.3.0.
|
||
*/
|
||
$dir = new RecursiveDirectoryIterator( $folder, 4096 | 8192 );
|
||
$dir = new Imagify_Files_Recursive_Iterator( $dir );
|
||
$images = new RecursiveIteratorIterator( $dir );
|
||
$images = array_keys( iterator_to_array( $images ) );
|
||
|
||
return $images;
|
||
}
|
||
|
||
|
||
/** ----------------------------------------------------------------------------------------- */
|
||
/** FORBIDDEN FOLDERS AND FILES ============================================================= */
|
||
/** ----------------------------------------------------------------------------------------- */
|
||
|
||
/**
|
||
* Tell if a path is autorized.
|
||
* When testing a folder, the path MUST have a trailing slash.
|
||
*
|
||
* @since 1.7.1
|
||
* @since 1.8 The path must have a trailing slash if for a folder.
|
||
* @access public
|
||
* @author Grégory Viguier
|
||
*
|
||
* @param string $file_path A file or folder absolute path.
|
||
* @return bool
|
||
*/
|
||
public static function is_path_autorized( $file_path ) {
|
||
return ! self::is_path_forbidden( $file_path );
|
||
}
|
||
|
||
/**
|
||
* Tell if a path is forbidden.
|
||
* When testing a folder, the path MUST have a trailing slash.
|
||
*
|
||
* @since 1.7
|
||
* @since 1.8 The path must have a trailing slash if for a folder.
|
||
* @access public
|
||
* @author Grégory Viguier
|
||
*
|
||
* @param string $file_path A file or folder absolute path.
|
||
* @return bool
|
||
*/
|
||
public static function is_path_forbidden( $file_path ) {
|
||
static $folders;
|
||
|
||
$filesystem = imagify_get_filesystem();
|
||
|
||
if ( self::is_filename_forbidden( $filesystem->file_name( $file_path ) ) ) {
|
||
return true;
|
||
}
|
||
|
||
if ( $filesystem->is_symlinked( $file_path ) ) {
|
||
// Files outside the site's folder are forbidden.
|
||
return true;
|
||
}
|
||
|
||
if ( ! isset( $folders ) ) {
|
||
$folders = self::get_forbidden_folders();
|
||
$folders = array_map( 'strtolower', $folders );
|
||
$folders = array_flip( $folders );
|
||
}
|
||
|
||
$file_path = self::normalize_path_for_comparison( $file_path );
|
||
|
||
if ( isset( $folders[ $file_path ] ) ) {
|
||
return true;
|
||
}
|
||
|
||
$delim = Imagify_Filesystem::PATTERN_DELIMITER;
|
||
|
||
foreach ( self::get_forbidden_folder_patterns() as $pattern ) {
|
||
if ( preg_match( $delim . '^' . $pattern . $delim, $file_path ) ) {
|
||
return true;
|
||
}
|
||
}
|
||
|
||
foreach ( $folders as $folder => $i ) {
|
||
if ( strpos( $file_path, $folder ) === 0 ) {
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* Get the list of folders where Imagify won't look for files to optimize.
|
||
*
|
||
* @since 1.7
|
||
* @access public
|
||
* @author Grégory Viguier
|
||
*
|
||
* @return array A list of absolute paths.
|
||
*/
|
||
public static function get_forbidden_folders() {
|
||
static $folders;
|
||
|
||
if ( isset( $folders ) ) {
|
||
return $folders;
|
||
}
|
||
|
||
$filesystem = imagify_get_filesystem();
|
||
$site_root = $filesystem->get_site_root();
|
||
$abspath = $filesystem->get_abspath();
|
||
$folders = array(
|
||
// Server.
|
||
$site_root . 'cgi-bin', // `cgi-bin`
|
||
// WordPress.
|
||
$abspath . 'wp-admin', // `wp-admin`
|
||
$abspath . WPINC, // `wp-includes`
|
||
WP_CONTENT_DIR . '/mu-plugins', // MU plugins.
|
||
WP_CONTENT_DIR . '/upgrade', // Upgrade.
|
||
// Plugins.
|
||
WP_CONTENT_DIR . '/bps-backup', // BulletProof Security.
|
||
self::get_ewww_tools_path(), // EWWW: /wp-content/ewww.
|
||
WP_CONTENT_DIR . '/ngg', // NextGen Gallery.
|
||
WP_CONTENT_DIR . '/ngg_styles', // NextGen Gallery.
|
||
WP_CONTENT_DIR . '/w3tc-config', // W3 Total Cache.
|
||
WP_CONTENT_DIR . '/wfcache', // WP Fastest Cache.
|
||
WP_CONTENT_DIR . '/wp-rocket-config', // WP Rocket.
|
||
Imagify_Custom_Folders::get_backup_dir_path(), // Imagify "Custom folders" backup: /imagify-backup.
|
||
IMAGIFY_PATH, // Imagify plugin: /wp-content/plugins/imagify.
|
||
self::get_shortpixel_path(), // ShortPixel: /wp-content/uploads/ShortpixelBackups.
|
||
);
|
||
|
||
if ( ! is_multisite() ) {
|
||
$uploads_dir = $filesystem->get_upload_basedir( true );
|
||
$ngg_galleries = self::get_ngg_galleries_path();
|
||
|
||
if ( $ngg_galleries ) {
|
||
$folders[] = $ngg_galleries; // NextGen Gallery: /wp-content/gallery.
|
||
}
|
||
|
||
$folders[] = $uploads_dir . 'formidable'; // Formidable Forms: /wp-content/uploads/formidable.
|
||
$folders[] = get_imagify_backup_dir_path( true ); // Imagify Media Library backup: /wp-content/uploads/backup.
|
||
$folders[] = self::get_wc_logs_path(); // WooCommerce Logs: /wp-content/uploads/wc-logs.
|
||
$folders[] = $uploads_dir . 'woocommerce_uploads'; // WooCommerce uploads: /wp-content/uploads/woocommerce_uploads.
|
||
}
|
||
|
||
$folders = array_map( array( $filesystem, 'normalize_dir_path' ), $folders );
|
||
|
||
/**
|
||
* Add folders to the list of forbidden ones.
|
||
*
|
||
* @since 1.7
|
||
* @author Grégory Viguier
|
||
*
|
||
* @param array $added_folders List of absolute paths.
|
||
* @param array $folders List of folders already forbidden.
|
||
*/
|
||
$added_folders = apply_filters( 'imagify_add_forbidden_folders', array(), $folders );
|
||
$added_folders = array_filter( (array) $added_folders );
|
||
$added_folders = array_filter( $added_folders, 'is_string' );
|
||
|
||
if ( ! $added_folders ) {
|
||
return $folders;
|
||
}
|
||
|
||
$added_folders = array_map( array( $filesystem, 'normalize_dir_path' ), $added_folders );
|
||
|
||
$folders = array_merge( $folders, $added_folders );
|
||
$folders = array_flip( array_flip( $folders ) );
|
||
|
||
return $folders;
|
||
}
|
||
|
||
/**
|
||
* Get the list of folder patterns where Imagify won't look for files to optimize. This is meant for paths that are dynamic.
|
||
* `^` will be prepended to each pattern (aka, the pattern must match an absolute path).
|
||
* Pattern delimiter is `Imagify_Filesystem::PATTERN_DELIMITER`.
|
||
* Paths tested against these patterns are lower-cased.
|
||
*
|
||
* @since 1.7
|
||
* @access public
|
||
* @author Grégory Viguier
|
||
*
|
||
* @return array A list of regex patterns.
|
||
*/
|
||
public static function get_forbidden_folder_patterns() {
|
||
static $folders;
|
||
|
||
if ( isset( $folders ) ) {
|
||
return $folders;
|
||
}
|
||
|
||
$folders = array();
|
||
|
||
// Media Library: /wp\-content/uploads/(sites/\d+/)?\d{4}/\d{2}/.
|
||
$folders[] = self::get_media_library_pattern();
|
||
|
||
if ( is_multisite() ) {
|
||
/**
|
||
* On multisite we can't exclude Imagify's library backup folders, or any other folder located in the uploads folders (created by other plugins): there are too many ways it can fail.
|
||
* Only exception we're aware of so far is NextGen Gallery, because it provides a clear pattern to use.
|
||
*/
|
||
$ngg_galleries = self::get_ngg_galleries_multisite_pattern();
|
||
|
||
if ( $ngg_galleries ) {
|
||
// NextGen Gallery: /wp\-content/uploads/sites/\d+/nggallery/.
|
||
$folders[] = $ngg_galleries;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Add folder patterns to the list of forbidden ones.
|
||
* Don't forget to use `Imagify_Files_Scan::normalize_path_for_regex( $path )`!
|
||
*
|
||
* @since 1.7
|
||
* @author Grégory Viguier
|
||
*
|
||
* @param array $added_folders List of patterns.
|
||
* @param array $folders List of patterns already forbidden.
|
||
*/
|
||
$added_folders = apply_filters( 'imagify_add_forbidden_folder_patterns', array(), $folders );
|
||
$added_folders = array_filter( (array) $added_folders );
|
||
$added_folders = array_filter( $added_folders, 'is_string' );
|
||
|
||
if ( ! $added_folders ) {
|
||
return $folders;
|
||
}
|
||
|
||
$folders = array_merge( $folders, $added_folders );
|
||
$folders = array_flip( array_flip( $folders ) );
|
||
|
||
return $folders;
|
||
}
|
||
|
||
/**
|
||
* Tell if a file/folder name is forbidden.
|
||
*
|
||
* @since 1.7
|
||
* @access public
|
||
* @author Grégory Viguier
|
||
*
|
||
* @param string $file_name A file or folder name.
|
||
* @return bool
|
||
*/
|
||
public static function is_filename_forbidden( $file_name ) {
|
||
static $file_names;
|
||
|
||
if ( ! isset( $file_names ) ) {
|
||
$file_names = array_flip( self::get_forbidden_file_names() );
|
||
}
|
||
|
||
return isset( $file_names[ strtolower( $file_name ) ] );
|
||
}
|
||
|
||
/**
|
||
* Get the list of file names that Imagify won't optimize.
|
||
* It can contain folder names. Names are case-lowered.
|
||
*
|
||
* @since 1.7
|
||
* @access public
|
||
* @author Grégory Viguier
|
||
*
|
||
* @return array A list of file names
|
||
*/
|
||
public static function get_forbidden_file_names() {
|
||
static $file_names;
|
||
|
||
if ( isset( $file_names ) ) {
|
||
return $file_names;
|
||
}
|
||
|
||
$file_names = array(
|
||
'.',
|
||
'..',
|
||
'.DS_Store',
|
||
'.git',
|
||
'.svn',
|
||
'backup',
|
||
'backups',
|
||
'cache',
|
||
'lang',
|
||
'langs',
|
||
'languages',
|
||
'node_modules',
|
||
'Thumbs.db',
|
||
);
|
||
$file_names = array_map( 'strtolower', $file_names );
|
||
|
||
/**
|
||
* Add file names to the list of forbidden ones.
|
||
*
|
||
* @since 1.7
|
||
* @author Grégory Viguier
|
||
*
|
||
* @param array $added_file_names List of file names.
|
||
* @param array $file_names List of file names already forbidden.
|
||
*/
|
||
$added_file_names = apply_filters( 'imagify_add_forbidden_file_names', array(), $file_names );
|
||
|
||
if ( ! $added_file_names || ! is_array( $added_file_names ) ) {
|
||
return $file_names;
|
||
}
|
||
|
||
$added_file_names = array_filter( $added_file_names, 'is_string' );
|
||
$added_file_names = array_map( 'strtolower', $added_file_names );
|
||
|
||
$file_names = array_merge( $file_names, $added_file_names );
|
||
$file_names = array_flip( array_flip( $file_names ) );
|
||
|
||
return $file_names;
|
||
}
|
||
|
||
|
||
/** ----------------------------------------------------------------------------------------- */
|
||
/** PLACEHOLDERS ============================================================================ */
|
||
/** ----------------------------------------------------------------------------------------- */
|
||
|
||
/**
|
||
* Add a placeholder to a path.
|
||
*
|
||
* @since 1.7
|
||
* @access public
|
||
* @author Grégory Viguier
|
||
*
|
||
* @param string $file_path An absolute path.
|
||
* @return string A "placeholdered" path.
|
||
*/
|
||
public static function add_placeholder( $file_path ) {
|
||
$file_path = wp_normalize_path( $file_path );
|
||
$locations = self::get_placeholder_paths();
|
||
|
||
foreach ( $locations as $placeholder => $location_path ) {
|
||
if ( strpos( $file_path, $location_path ) === 0 ) {
|
||
return preg_replace( '@^' . preg_quote( $location_path, '@' ) . '@', $placeholder, $file_path );
|
||
}
|
||
}
|
||
|
||
// Should not happen.
|
||
return $file_path;
|
||
}
|
||
|
||
/**
|
||
* Change a path with a placeholder into a real path or URL.
|
||
*
|
||
* @since 1.7
|
||
* @access public
|
||
* @author Grégory Viguier
|
||
*
|
||
* @param string $file_path A path with a placeholder.
|
||
* @param string $type What to return: 'path' or 'url'.
|
||
* @return string An absolute path or a URL.
|
||
*/
|
||
public static function remove_placeholder( $file_path, $type = 'path' ) {
|
||
if ( 'path' === $type ) {
|
||
$locations = self::get_placeholder_paths();
|
||
} else {
|
||
$locations = self::get_placeholder_urls();
|
||
}
|
||
|
||
foreach ( $locations as $placeholder => $location_path ) {
|
||
if ( strpos( $file_path, $placeholder ) === 0 ) {
|
||
return preg_replace( '@^' . preg_quote( $placeholder, '@' ) . '@', $location_path, $file_path );
|
||
}
|
||
}
|
||
|
||
// Should not happen.
|
||
return $file_path;
|
||
}
|
||
|
||
/**
|
||
* Get array of pairs of placeholder => corresponding path.
|
||
*
|
||
* @since 1.7
|
||
* @access public
|
||
* @author Grégory Viguier
|
||
*
|
||
* @return array
|
||
*/
|
||
public static function get_placeholder_paths() {
|
||
static $replacements;
|
||
|
||
if ( isset( $replacements ) ) {
|
||
return $replacements;
|
||
}
|
||
|
||
$filesystem = imagify_get_filesystem();
|
||
$replacements = array(
|
||
'{{PLUGINS}}/' => WP_PLUGIN_DIR,
|
||
'{{MU_PLUGINS}}/' => WPMU_PLUGIN_DIR,
|
||
'{{THEMES}}/' => WP_CONTENT_DIR . '/themes',
|
||
'{{UPLOADS}}/' => $filesystem->get_main_upload_basedir(),
|
||
'{{CONTENT}}/' => WP_CONTENT_DIR,
|
||
'{{ROOT}}/' => $filesystem->get_site_root(),
|
||
);
|
||
$replacements = array_map( array( $filesystem, 'normalize_dir_path' ), $replacements );
|
||
|
||
return $replacements;
|
||
}
|
||
|
||
/**
|
||
* Get array of pairs of placeholder => corresponding URL.
|
||
*
|
||
* @since 1.7
|
||
* @access public
|
||
* @author Grégory Viguier
|
||
*
|
||
* @return array
|
||
*/
|
||
public static function get_placeholder_urls() {
|
||
static $replacements;
|
||
|
||
if ( isset( $replacements ) ) {
|
||
return $replacements;
|
||
}
|
||
|
||
$filesystem = imagify_get_filesystem();
|
||
$replacements = array(
|
||
'{{PLUGINS}}/' => plugins_url( '/' ),
|
||
'{{MU_PLUGINS}}/' => plugins_url( '/', WPMU_PLUGIN_DIR . '/.' ),
|
||
'{{THEMES}}/' => content_url( 'themes/' ),
|
||
'{{UPLOADS}}/' => $filesystem->get_main_upload_baseurl(),
|
||
'{{CONTENT}}/' => content_url( '/' ),
|
||
'{{ROOT}}/' => $filesystem->get_site_root_url(),
|
||
);
|
||
|
||
return $replacements;
|
||
}
|
||
|
||
/**
|
||
* A file_exists() for paths with a placeholder.
|
||
*
|
||
* @since 1.7
|
||
* @access public
|
||
* @author Grégory Viguier
|
||
*
|
||
* @param string $file_path The file path.
|
||
* @return bool
|
||
*/
|
||
public static function placeholder_path_exists( $file_path ) {
|
||
return imagify_get_filesystem()->is_readable( self::remove_placeholder( $file_path ) );
|
||
}
|
||
|
||
|
||
/** ----------------------------------------------------------------------------------------- */
|
||
/** PATHS =================================================================================== */
|
||
/** ----------------------------------------------------------------------------------------- */
|
||
|
||
/**
|
||
* Get the path to NextGen galleries on monosites.
|
||
*
|
||
* @since 1.7
|
||
* @access public
|
||
* @author Grégory Viguier
|
||
*
|
||
* @return string|bool An absolute path. False if it can't be retrieved.
|
||
*/
|
||
public static function get_ngg_galleries_path() {
|
||
$galleries_path = get_site_option( 'ngg_options' );
|
||
|
||
if ( empty( $galleries_path['gallerypath'] ) ) {
|
||
return false;
|
||
}
|
||
|
||
$filesystem = imagify_get_filesystem();
|
||
$galleries_path = $filesystem->normalize_dir_path( $galleries_path['gallerypath'] );
|
||
$galleries_path = trim( $galleries_path, '/' ); // Something like `wp-content/gallery`.
|
||
|
||
$ngg_root = defined( 'NGG_GALLERY_ROOT_TYPE' ) ? NGG_GALLERY_ROOT_TYPE : 'site';
|
||
|
||
if ( $galleries_path && 'content' === $ngg_root ) {
|
||
$ngg_root = $filesystem->normalize_dir_path( WP_CONTENT_DIR );
|
||
$ngg_root = trim( $ngg_root, '/' ); // Something like `abs-path/to/wp-content`.
|
||
|
||
$exploded_root = explode( '/', $ngg_root );
|
||
$exploded_galleries = explode( '/', $galleries_path );
|
||
$first_gallery_dirname = reset( $exploded_galleries );
|
||
$last_root_dirname = end( $exploded_root );
|
||
|
||
if ( $last_root_dirname === $first_gallery_dirname ) {
|
||
array_shift( $exploded_galleries );
|
||
$galleries_path = implode( '/', $exploded_galleries );
|
||
}
|
||
}
|
||
|
||
if ( 'content' === $ngg_root ) {
|
||
$ngg_root = $filesystem->normalize_dir_path( WP_CONTENT_DIR );
|
||
} else {
|
||
$ngg_root = $filesystem->get_abspath();
|
||
}
|
||
|
||
if ( strpos( $galleries_path, $ngg_root ) !== 0 ) {
|
||
$galleries_path = $ngg_root . $galleries_path;
|
||
}
|
||
|
||
return $galleries_path . '/';
|
||
}
|
||
|
||
/**
|
||
* Get the path to WooCommerce logs on monosites.
|
||
*
|
||
* @since 1.7
|
||
* @access public
|
||
* @author Grégory Viguier
|
||
*
|
||
* @return string An absolute path.
|
||
*/
|
||
public static function get_wc_logs_path() {
|
||
if ( defined( 'WC_LOG_DIR' ) ) {
|
||
return WC_LOG_DIR;
|
||
}
|
||
|
||
return imagify_get_filesystem()->get_upload_basedir( true ) . 'wc-logs/';
|
||
}
|
||
|
||
/**
|
||
* Get the path to EWWW optimization tools.
|
||
* It is the same for all sites on multisite.
|
||
*
|
||
* @since 1.7
|
||
* @access public
|
||
* @author Grégory Viguier
|
||
*
|
||
* @return string An absolute path.
|
||
*/
|
||
public static function get_ewww_tools_path() {
|
||
if ( defined( 'EWWW_IMAGE_OPTIMIZER_TOOL_PATH' ) ) {
|
||
return EWWW_IMAGE_OPTIMIZER_TOOL_PATH;
|
||
}
|
||
|
||
return WP_CONTENT_DIR . '/ewww/';
|
||
}
|
||
|
||
/**
|
||
* Get the path to ShortPixel backup folder.
|
||
* It is the same for all sites on multisite (and yes, you'll get a surprise if your upload base dir -aka uploads/sites/12/- is not 2 folders deeper than theuploads folder).
|
||
*
|
||
* @since 1.8
|
||
* @access public
|
||
* @author Grégory Viguier
|
||
*
|
||
* @return string An absolute path.
|
||
*/
|
||
public static function get_shortpixel_path() {
|
||
if ( defined( 'SHORTPIXEL_BACKUP_FOLDER' ) ) {
|
||
return trailingslashit( SHORTPIXEL_BACKUP_FOLDER );
|
||
}
|
||
|
||
$filesystem = imagify_get_filesystem();
|
||
$path = $filesystem->get_upload_basedir( true );
|
||
$path = is_main_site() ? $path : $filesystem->dir_path( $filesystem->dir_path( $path ) );
|
||
|
||
return $path . 'ShortpixelBackups/';
|
||
}
|
||
|
||
|
||
/** ----------------------------------------------------------------------------------------- */
|
||
/** REGEX PATTERNS ========================================================================== */
|
||
/** ----------------------------------------------------------------------------------------- */
|
||
|
||
/**
|
||
* Get the regex pattern used to match the paths to the media library.
|
||
* Pattern delimiter is `Imagify_Filesystem::PATTERN_DELIMITER`.
|
||
* Paths tested against these patterns are lower-cased.
|
||
*
|
||
* @since 1.8
|
||
* @access public
|
||
* @author Grégory Viguier
|
||
*
|
||
* @return string Something like `/wp\-content/uploads/(sites/\d+/)?\d{4}/\d{2}/`.
|
||
*/
|
||
public static function get_media_library_pattern() {
|
||
$filesystem = imagify_get_filesystem();
|
||
$uploads_dir = self::normalize_path_for_regex( $filesystem->get_main_upload_basedir() );
|
||
|
||
if ( ! is_multisite() ) {
|
||
if ( get_option( 'uploads_use_yearmonth_folders' ) ) {
|
||
// In year/month folders.
|
||
return $uploads_dir . '\d{4}/\d{2}/';
|
||
}
|
||
|
||
// Not in year/month folders.
|
||
return $uploads_dir . '[^/]+$';
|
||
}
|
||
|
||
$pattern = $filesystem->get_multisite_uploads_subdir_pattern();
|
||
|
||
if ( get_option( 'uploads_use_yearmonth_folders' ) ) {
|
||
// In year/month folders.
|
||
return $uploads_dir . '(' . $pattern . ')?\d{4}/\d{2}/';
|
||
}
|
||
|
||
// Not in year/month folders.
|
||
return $uploads_dir . '(' . $pattern . ')?[^/]+$';
|
||
}
|
||
|
||
/**
|
||
* Get the regex pattern used to match the paths to NextGen galleries on multisite.
|
||
* Pattern delimiter is `Imagify_Filesystem::PATTERN_DELIMITER`.
|
||
* Paths tested against these patterns are lower-cased.
|
||
*
|
||
* @since 1.8
|
||
* @access public
|
||
* @author Grégory Viguier
|
||
*
|
||
* @return string|bool Something like `/wp-content/uploads/sites/\d+/nggallery/`. False if it can't be retrieved.
|
||
*/
|
||
public static function get_ngg_galleries_multisite_pattern() {
|
||
$galleries_path = self::get_ngg_galleries_path(); // Something like `wp-content/uploads/sites/%BLOG_ID%/nggallery/`.
|
||
|
||
if ( ! $galleries_path ) {
|
||
return false;
|
||
}
|
||
|
||
$galleries_path = self::normalize_path_for_regex( $galleries_path );
|
||
$galleries_path = str_replace( array( '%blog_name%', '%blog_id%' ), array( '.+', '\d+' ), $galleries_path );
|
||
|
||
return $galleries_path;
|
||
}
|
||
|
||
|
||
/** ----------------------------------------------------------------------------------------- */
|
||
/** NORMALIZATION TOOLS ===================================================================== */
|
||
/** ----------------------------------------------------------------------------------------- */
|
||
|
||
/**
|
||
* Normalize a file path, aiming for path comparison.
|
||
* The path is normalized and case-lowered.
|
||
*
|
||
* @since 1.7
|
||
* @since 1.8 No trailing slash anymore, because it can be used for files.
|
||
* @access public
|
||
* @author Grégory Viguier
|
||
*
|
||
* @param string $file_path The file path.
|
||
* @return string The normalized file path.
|
||
*/
|
||
public static function normalize_path_for_comparison( $file_path ) {
|
||
return strtolower( wp_normalize_path( $file_path ) );
|
||
}
|
||
|
||
/**
|
||
* Normalize a file path, aiming for use in a regex pattern.
|
||
* The path is normalized, case-lowered, and escaped.
|
||
*
|
||
* @since 1.8
|
||
* @access public
|
||
* @author Grégory Viguier
|
||
*
|
||
* @param string $file_path The file path.
|
||
* @return string The normalized file path.
|
||
*/
|
||
public static function normalize_path_for_regex( $file_path ) {
|
||
return preg_quote( imagify_get_filesystem()->normalize_path_for_comparison( $file_path ), Imagify_Filesystem::PATTERN_DELIMITER );
|
||
}
|
||
}
|