get_base_directory() . '/' . sanitize_file_name( $name );
}
/**
* Creates a subfolder and the access protection of it and of the base directory.
*
* @param string $name Subfolder name.
* @return string Absolute path, without trailing slash.
* @throws SyncException When the folder cannot be created.
*/
public function ensure_directory( string $name ): string {
$directory = $this->get_directory( $name );
if ( ! wp_mkdir_p( $directory ) ) {
throw new SyncException( esc_html__( 'The plugin folder in the uploads directory could not be created.', 'cherrylift' ) );
}
foreach ( array( $this->get_base_directory(), $directory ) as $folder ) {
$this->protect( $folder );
}
return $directory;
}
/**
* Deletes files with the given extension older than the lifetime from a subfolder.
*
* @param string $name Subfolder name.
* @param string $extension File extension, without dot.
* @param int $lifetime Lifetime in seconds.
* @return int Number of files deleted.
*/
public function delete_expired_files( string $name, string $extension, int $lifetime ): int {
$directory = $this->get_directory( $name );
if ( ! is_dir( $directory ) ) {
return 0;
}
$deleted = 0;
$suffix = '.' . $extension;
$entries = $this->filesystem()->dirlist( $directory, false );
foreach ( is_array( $entries ) ? $entries : array() as $file => $entry ) {
if ( 'f' === $entry['type'] && substr( (string) $file, -strlen( $suffix ) ) === $suffix && (int) $entry['lastmodunix'] < time() - $lifetime ) {
wp_delete_file( $directory . '/' . $file );
++$deleted;
}
}
return $deleted;
}
/**
* Adds the files that deny web access to a folder, if missing.
*
* @param string $directory Absolute path.
* @return void
*/
private function protect( string $directory ): void {
$filesystem = $this->filesystem();
if ( ! $filesystem->exists( $directory . '/index.php' ) ) {
// Explicit mode: FS_CHMOD_FILE is only defined once WP_Filesystem() has run, which REST requests don't do.
$filesystem->put_contents( $directory . '/index.php', "exists( $directory . '/.htaccess' ) ) {
$filesystem->put_contents( $directory . '/.htaccess', "\n\tRequire all denied\n\n\n\tDeny from all\n\n", 0644 );
}
}
/**
* Returns a direct filesystem instance. Only used for the plugin's own folders.
*
* @return WP_Filesystem_Direct
*/
private function filesystem(): WP_Filesystem_Direct {
require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php';
require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php';
return new WP_Filesystem_Direct( null );
}
}