<?php
/**
* Class description
*
* @package package_name
* @author Cherry Team
* @license GPL-2.0+
*/
// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
die;
}
if ( ! class_exists( 'Jet_Theme_Core_Backups' ) ) {
/**
* Define Jet_Theme_Core_Backups class
*/
class Jet_Theme_Core_Backups {
/**
* Path to backup file
* @var string
*/
public $path;
/**
* Theme files to backup
* @var array
*/
public $files = array();
/**
* Messages holder
* @var string
*/
public $message = null;
/**
* A reference to an instance of this class.
*
* @since 1.0.0
* @var object
*/
private static $instance = null;
/**
* Constructor for the class
*/
public function __construct() {
// connect filesystem
$this->fs_connect();
// set path
$upload_dir = wp_upload_dir();
$upload_base_dir = $upload_dir['basedir'];
$this->path = trailingslashit( $upload_base_dir ) . 'kava-backups';
}
/**
* Try to create backup archive
*
* @since 1.0.0
* @return bool
*/
public function make_backup( $theme, $version ) {
global $wp_filesystem;
if ( ! current_user_can( 'manage_options' ) ) {
return false;
}
include_once( ABSPATH . '/wp-admin/includes/class-pclzip.php' );
if ( ! $this->check_path() ) {
return false;
}
$this->protect_path();
ini_set( 'max_execution_time', -1 );
set_time_limit( 0 );
$zip_name = $this->path . '/' . $theme . '-' . $version . '.zip';
$files = $this->get_files( $theme );
$files = implode( ',', $files );
$remove_path = $wp_filesystem->wp_themes_dir();
$zip = new PclZip( $zip_name );
$result = $zip->create( $files, PCLZIP_OPT_REMOVE_PATH, $remove_path );
if ( ! $result ) {
return false;
}
return str_replace( ABSPATH, home_url( '/' ), $zip_name );
}
/**
* Get backup files list
*
* @since 1.0.0
* @return array
*/
public function get_files( $theme ) {
global $wp_filesystem;
$path = $wp_filesystem->wp_themes_dir() . $theme;
$this->parse_dir( $path );
return $this->files;
}
/**
* Recursive function, that parse passed directory and add found files into 'file' property.
*
* @since 1.0.0
* @param string $dir path to directory to search files in.
* @return void|bool false
*/
public function parse_dir( $dir ) {
global $wp_filesystem;
$files = $wp_filesystem->dirlist( $dir );
if ( ! is_array( $files ) ) {
return false;
}
foreach ( $wp_filesystem->dirlist( $dir ) as $name => $data ) {
$current_path = trailingslashit( $dir ) . $name;
if ( 'd' == $data['type'] ) {
$this->parse_dir( $current_path );
continue;
}
$this->files[] = $current_path;
}
}
/**
* Check if backup directory exists and create it if not
*
* @since 1.0.0
* @return bool
*/
public function check_path() {
global $wp_filesystem;
$path = $this->prepare_path( $this->path );
if ( $wp_filesystem->exists( $path ) ) {
return true;
}
return $wp_filesystem->mkdir( $path );
}
/**
* Create .htaccess file in updates backup dir to protect it from direct access
*
* @since 1.0.0
* @return void|bool
*/
public function protect_path() {
global $wp_filesystem;
$path = $this->prepare_path( $this->path );
$file = $path . '/.htaccess';
if ( $wp_filesystem->exists( $file ) ) {
return true;
}
$wp_filesystem->put_contents( $file, 'deny from all' );
}
/**
* Prepeare path for using with filesystem API
*
* @since 1.0.0
* @param string $path path to prepare.
* @return string
*/
public function prepare_path( $path ) {
global $wp_filesystem;
return str_replace( ABSPATH, $wp_filesystem->abspath(), $path );
}
/**
* Get avaliable backups list
*
* @since 1.0.0
* @return array
*/
public function get_backups() {
global $wp_filesystem;
$path = $this->prepare_path( $this->path );
$files = $wp_filesystem->dirlist( $path );
$result = array();
if ( ! is_array( $files ) ) {
return $result;
}
if ( isset( $files['.htaccess'] ) ) {
unset( $files['.htaccess'] );
}
foreach ( $files as $file => $data ) {
if ( 'zip' !== pathinfo( $file, PATHINFO_EXTENSION ) ) {
continue;
}
$result[] = array(
'name' => $file,
'date' => date( 'M d Y, H:i', $data['lastmodunix'] ),
);
}
usort( $result, array( $this, 'date_compare' ) );
return $result;
}
/**
* Compare backups by date
*
* @since 1.0.0
* @param array $a 1st value.
* @param array $b 2nd value.
* @return bool
*/
public function date_compare( $a, $b ) {
$t1 = strtotime( $a['date'] );
$t2 = strtotime( $b['date'] );
return $t2 - $t1;
}
/**
* Download backup by filename
*
* @since 1.0.0
* @param string $file backup filename.
* @return void|bool false
*/
public function download_backup( $file ) {
if ( ! current_user_can( 'manage_options' ) ) {
$this->message = __( 'Permission denied', 'jet-theme-core' );
return false;
}
global $wp_filesystem;
$path = $this->prepare_path( $this->path );
$filepath = $path . '/' . $file;
if ( ! $wp_filesystem->exists( $filepath ) ) {
$this->message = __( 'File not exists', 'jet-theme-core' );
return false;
}
session_write_close();
header( 'Pragma: public' );
header( 'Expires: 0' );
header( 'Cache-Control: must-revalidate, post-check=0, pre-check=0' );
header( 'Cache-Control: public' );
header( 'Content-Description: File Transfer' );
header( 'Content-type: application/octet-stream' );
header( 'Content-Disposition: attachment; filename="' . $file . '"' );
header( 'Content-Transfer-Encoding: binary' );
header( 'Content-Length: ' . @filesize( $filepath ) );
$this->readfile_chunked( $filepath ) or header( 'Location: ' . $filepath );
exit();
}
/**
* Delete existing backup by filename
*
* @since 1.0.0
* @param string $file backup filename.
* @return bool
*/
public function delete_backup( $file ) {
if ( ! current_user_can( 'manage_options' ) ) {
$this->message = __( 'Permission denied', 'jet-theme-core' );
return false;
}
global $wp_filesystem;
$path = $this->prepare_path( $this->path );
$filepath = $path . '/' . $file;
if ( ! $wp_filesystem->exists( $filepath ) ) {
$this->message = __( 'File not exists', 'jet-theme-core' );
return false;
}
$delete = $wp_filesystem->delete( $filepath );
if ( false === $delete ) {
$this->message = __( 'Can\'t delete selected backup', 'jet-theme-core' );
}
return $delete;
}
/**
* Chunked file reading
*
* @since 1.0.0
* @param string $file fileptah.
* @param boolean $retbytes return bytes number or not.
* @return bool|int
*/
public function readfile_chunked( $file, $retbytes = true ) {
$chunksize = 1024 * 1024;
$buffer = '';
$cnt = 0;
$handle = @fopen( $file, 'r' );
if ( $size = @filesize( $file ) ) {
header( 'Content-Length: ' . $size );
}
if ( false === $handle ) {
return false;
}
while ( ! @feof( $handle ) ) {
$buffer = @fread( $handle, $chunksize );
echo $buffer;
ob_flush();
flush();
if ( $retbytes ) {
$cnt += strlen( $buffer );
}
}
$status = @fclose( $handle );
if ( $retbytes && $status ) {
return $cnt;
}
return $status;
}
/**
* Check if backup manager returned any messages during processing
*
* @since 1.0.0
* @return string
*/
public function get_message() {
return (string) $this->message;
}
/**
* Connect to the filesystem.
*
* @since 1.0.0
*
* @param array $directories Optional. A list of directories. If any of these do
* not exist, a {@see WP_Error} object will be returned.
* Default empty array.
* @param bool $allow_relaxed_file_ownership Whether to allow relaxed file ownership.
* Default false.
* @return bool|WP_Error True if able to connect, false or a {@see WP_Error} otherwise.
*/
public function fs_connect( $directories = array(), $allow_relaxed_file_ownership = false ) {
global $wp_filesystem;
if ( ! function_exists( 'request_filesystem_credentials' ) ) {
include_once ( ABSPATH . '/wp-admin/includes/file.php' );
}
$url = admin_url( 'options.php' );
if ( false === ( $credentials = request_filesystem_credentials( $url, '', false, false, array(), $allow_relaxed_file_ownership ) ) ) {
return false;
}
if ( ! empty( $directories[0] ) ) {
$dirs = $directories[0];
} else {
$dirs = array();
}
if ( ! WP_Filesystem( $credentials, $dirs, $allow_relaxed_file_ownership ) ) {
$error = true;
if ( is_object( $wp_filesystem ) && $wp_filesystem->errors->get_error_code() ) {
$error = $wp_filesystem->errors;
}
return false;
}
if ( ! is_object( $wp_filesystem ) ) {
return new WP_Error( 'fs_unavailable', $this->strings['fs_unavailable'] );
}
if ( is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) {
return new WP_Error( 'fs_error', $this->strings['fs_error'], $wp_filesystem->errors );
}
foreach ( (array) $directories as $dir ) {
switch ( $dir ) {
case ABSPATH:
if ( ! $wp_filesystem->abspath() ) {
return new WP_Error( 'fs_no_root_dir', $this->strings['fs_no_root_dir'] );
}
break;
case WP_CONTENT_DIR:
if ( ! $wp_filesystem->wp_content_dir() ) {
return new WP_Error( 'fs_no_content_dir', $this->strings['fs_no_content_dir'] );
}
break;
case WP_PLUGIN_DIR:
if ( ! $wp_filesystem->wp_plugins_dir() ) {
return new WP_Error( 'fs_no_plugins_dir', $this->strings['fs_no_plugins_dir'] );
}
break;
case get_theme_root():
if ( ! $wp_filesystem->wp_themes_dir() ) {
return new WP_Error( 'fs_no_themes_dir', $this->strings['fs_no_themes_dir'] );
}
break;
default:
if ( ! $wp_filesystem->find_folder( $dir ) ) {
return new WP_Error( 'fs_no_folder', sprintf( $this->strings['fs_no_folder'], esc_html( basename( $dir ) ) ) );
}
break;
}
}
return true;
}
}
}