false )
);
}
// Cloudflare
if ( has_action( 'cloudflare_purge_everything' ) ) {
do_action( 'cloudflare_purge_everything' );
}
}
// ------------------------------------------------------------------------
// Translations cache functions
// ------------------------------------------------------------------------
/**
* Clear cached translations
*
* Deletes the translations transient and clears the website cache.
*
* @return void
*/
function wplng_clear_translations_cache() {
delete_option( 'wplng_cached_translations' );
wplng_clear_website_cache();
}
/**
* Clear cached translations when a translation post is trashed or untrashed
*
* @param int $post_id The ID of the post being trashed or untrashed.
* @return void
*/
function wplng_clear_translations_cache_trash_untrash( $post_id ) {
if ( 'wplng_translation' !== get_post_type( $post_id ) ) {
return;
}
wplng_clear_translations_cache();
}
// ------------------------------------------------------------------------
// Slug cache functions
// ------------------------------------------------------------------------
/**
* Clear cached slugs
*
* Deletes the slugs option and clears the website cache.
*
* @return void
*/
function wplng_clear_slugs_cache() {
delete_option( 'wplng_cached_slugs' );
wplng_clear_website_cache();
}
/**
* Clear cached slugs when a slug post is trashed or untrashed
*
* @param int $post_id The ID of the post being trashed or untrashed.
* @return void
*/
function wplng_clear_slugs_cache_trash_untrash( $post_id ) {
if ( 'wplng_slug' !== get_post_type( $post_id ) ) {
return;
}
wplng_clear_slugs_cache();
}
// ------------------------------------------------------------------------
// wpLingua file cache functions
// ------------------------------------------------------------------------
/**
* Write data to a cache file in the wpLingua cache directory.
*
* Creates the cache directory structure and protection files if needed,
* then writes the provided data to the specified file.
*
* @param string $file_name Relative path to the cache file (from WPLNG_CACHE_DIR).
* @param string $data Data to write to the file.
* @return int|false Number of bytes written, or false on failure.
*/
function wplng_put_cache_file( $file_name, $data ) {
// Security: prevent path traversal
if ( wplng_str_contains( $file_name, '..' ) ) {
return false;
}
// Create main wpLingua cache folder if not exist
if ( ! is_dir( WPLNG_CACHE_DIR ) ) {
if ( ! wp_mkdir_p( WPLNG_CACHE_DIR ) ) {
return false;
}
}
// Create minimal protections in main folder (only once per request)
static $main_protection_checked = false;
if ( ! $main_protection_checked ) {
// Create .htaccess if not exists
if ( ! file_exists( WPLNG_CACHE_DIR . '/.htaccess' ) ) {
$htaccess = '##############################' . PHP_EOL;
$htaccess .= '# File generated by wpLingua #' . PHP_EOL;
$htaccess .= '##############################' . PHP_EOL;
$htaccess .= PHP_EOL;
$htaccess .= '# Disable files listing' . PHP_EOL;
$htaccess .= 'Options -Indexes' . PHP_EOL;
$htaccess .= PHP_EOL;
$htaccess .= '# Deny all HTTP access to this directory' . PHP_EOL;
$htaccess .= '' . PHP_EOL;
$htaccess .= ' Require all denied' . PHP_EOL;
$htaccess .= '' . PHP_EOL;
$htaccess .= PHP_EOL;
$htaccess .= '' . PHP_EOL;
$htaccess .= ' Deny from all' . PHP_EOL;
$htaccess .= '' . PHP_EOL;
file_put_contents( WPLNG_CACHE_DIR . '/.htaccess', $htaccess );
}
// Create an empty index.html file in main folder
wplng_create_cache_index_html( WPLNG_CACHE_DIR );
$main_protection_checked = true;
}
// Create the subfolder if needed
$dir = dirname( WPLNG_CACHE_DIR . $file_name );
if ( ! is_dir( $dir ) ) {
if ( ! wp_mkdir_p( $dir ) ) {
return false;
}
}
// Create index.html in all directories between main path and target directory
wplng_create_cache_index_html_recursive( $dir );
// Write the cache file
return file_put_contents( WPLNG_CACHE_DIR . $file_name, $data );
}
/**
* Create an empty index.html file in the specified directory.
*
* @param string $dir_path Absolute path to the directory.
* @return bool True if file exists or was created, false on failure.
*/
function wplng_create_cache_index_html( $dir_path ) {
$index_file = rtrim( $dir_path, '/\\' ) . '/index.html';
if ( file_exists( $index_file ) ) {
return true;
}
return file_put_contents( $index_file, '' ) !== false;
}
/**
* Create index.html files in all directories from WPLNG_CACHE_DIR to the target directory.
*
* @param string $target_dir Absolute path to the target directory.
* @return void
*/
function wplng_create_cache_index_html_recursive( $target_dir ) {
$target_dir = wp_normalize_path( $target_dir );
$base_path = wp_normalize_path( WPLNG_CACHE_DIR );
// Ensure target is within cache path
if ( strpos( $target_dir, $base_path ) !== 0 ) {
return;
}
// Get relative path from base
$relative_path = substr( $target_dir, strlen( $base_path ) );
$relative_path = trim( $relative_path, '/' );
if ( empty( $relative_path ) ) {
return;
}
// Build each subdirectory and create index.html
$parts = explode( '/', $relative_path );
$current_path = $base_path;
foreach ( $parts as $part ) {
$current_path .= '/' . $part;
if ( is_dir( $current_path ) ) {
wplng_create_cache_index_html( $current_path );
}
}
}
/**
* Read data from a cache file in the wpLingua cache directory.
*
* @param string $file_name Relative path to the cache file (from WPLNG_CACHE_DIR).
* @return string|false File contents, or false if file doesn't exist or isn't readable.
*/
function wplng_get_cache_file( $file_name ) {
// Security: prevent path traversal
if ( wplng_str_contains( $file_name, '..' ) ) {
return false;
}
$file_path = WPLNG_CACHE_DIR . $file_name;
if ( ! is_readable( $file_path ) ) {
return false;
}
return file_get_contents( $file_path );
}
/**
* Clear wpLingua cache folder or a specific file/directory.
*
* @param string|false $file Relative path to delete, or false to delete entire cache folder.
* @return bool True on success, false on failure.
*/
function wplng_clear_folder_cache( $file = false ) {
if ( $file === false ) {
$path = WPLNG_CACHE_DIR;
} else {
$path = wp_normalize_path( WPLNG_CACHE_DIR . $file );
// Security: ensure path is within cache directory
if ( wplng_str_contains( $file, '..' )
|| ! wplng_str_starts_with( $path, wp_normalize_path( WPLNG_CACHE_DIR ) )
) {
return false;
}
}
if ( ! file_exists( $path ) ) {
return true;
}
if ( is_file( $path ) ) {
return @unlink( $path );
}
return wplng_delete_directory( $path );
}
/**
* Clear wpLingua cache when WordPress core, plugins, themes or translations are updated.
*
* Hooked to:
* - upgrader_process_complete (core, plugins, themes, translations updates)
* - activated_plugin
* - deactivated_plugin
* - switch_theme
*
* @param mixed $upgrader_or_plugin WP_Upgrader instance or plugin slug (depends on hook).
* @param array $options Array of update data (only for upgrader_process_complete).
* @return void
*/
function wplng_clear_cache_on_update( $upgrader_or_plugin = null, $options = array() ) {
// For upgrader_process_complete hook, check update type
if ( ! empty( $options ) && is_array( $options ) ) {
$type = isset( $options['type'] ) ? $options['type'] : '';
// Clear cache for core, plugin, theme or translation updates
if ( in_array( $type, array( 'core', 'plugin', 'theme', 'translation' ), true ) ) {
wplng_clear_folder_cache();
}
return;
}
// For other hooks (activated_plugin, deactivated_plugin, switch_theme)
wplng_clear_folder_cache();
}
/**
* Recursively delete a directory and all its contents.
*
* Only deletes directories within WPLNG_CACHE_DIR for security.
*
* @param string $dir_path Absolute path to the directory.
* @return bool True on success, false on failure.
*/
function wplng_delete_directory( $dir_path ) {
$dir_path = wp_normalize_path( $dir_path );
$base_path = wp_normalize_path( WPLNG_CACHE_DIR );
// Security: ensure path is within cache directory
if ( strpos( $dir_path, $base_path ) !== 0 ) {
return false;
}
if ( ! is_dir( $dir_path ) ) {
return false;
}
$items = scandir( $dir_path );
if ( $items === false ) {
return false;
}
foreach ( $items as $item ) {
if ( $item === '.' || $item === '..' ) {
continue;
}
$item_path = $dir_path . '/' . $item;
if ( is_dir( $item_path ) ) {
wplng_delete_directory( $item_path );
} else {
@unlink( $item_path );
}
}
return @rmdir( $dir_path );
}