/** * {@inheritdoc} * @param $args * * destination_dir - Path to dir where the downloaded files must be placed * * source - Path to dir or zip file */ public function download(array $args, array $state = array()) { // Note: destination_dir is already validated if (empty($args['source'])) { return new WP_Error('no_source', __('Source not specified', 'fw')); } elseif (!($args['source'] = fw_fix_path(realpath($args['source'])))) { return new WP_Error('invalid_source', __('Invalid source', 'fw')); } elseif (!($is_dir = is_dir($args['source'])) || !($is_zip = substr($args['source'], -4, 4) !== '.zip')) { return new WP_Error('invalid_source_type', __('Invalid source type', 'fw')); } if ($is_dir) { return fw_ext_backups_copy_dir_recursive($args['source'], $args['destination_dir']); } elseif ($is_zip) { if (!class_exists('ZipArchive')) { return new WP_Error('zip_ext_missing', __('Zip extension missing', 'fw')); } $zip = new ZipArchive(); if (true === $zip->open($args['source'])) { return new WP_Error('zip_open_fail', sprintf(__('Cannot open zip: %s', 'fw'), $args['source'])); } $zip->extractTo($args['destination_dir']); $zip->close(); unset($zip); return true; } else { return new WP_Error('unhandled_type', __('Unhandled type', 'fw')); } }
/** * {@inheritdoc} * @param array $args * * zip - file path * * dir - where the zip file will be extract */ public function execute(array $args, array $state = array()) { if (!isset($args['zip'])) { return new WP_Error('no_zip', __('Zip file not specified', 'fw')); } else { $args['zip'] = fw_fix_path($args['zip']); } if (!isset($args['dir'])) { return new WP_Error('no_dir', __('Destination dir not specified', 'fw')); } else { $args['dir'] = fw_fix_path($args['dir']); } if (empty($state)) { if (!fw_ext_backups_is_dir_empty($args['dir'])) { return new WP_Error('destination_not_empty', __('Destination dir is not empty', 'fw')); } $state = array('entry' => '', 'extracted_files' => 0); } wp_cache_flush(); FW_Cache::clear(); if (is_wp_error($extract_result = fw_ext_backups_unzip_partial($args['zip'], $args['dir'], $state['entry']))) { return $extract_result; } else { if ($extract_result['finished']) { return true; } else { $state['entry'] = $extract_result['last_entry']; $state['extracted_files'] += $extract_result['extracted_files']; return $state; } } }
/** * @return FW_Ext_Backups_Demo[] */ private function get_demos() { if (is_null(self::$demos)) { $demos = array(); foreach (apply_filters('fw_ext_backups_demo_dirs', array(fw_fix_path(get_template_directory()) . '/demo-content' => get_template_directory_uri() . '/demo-content')) as $dir_path => $dir_uri) { if (!is_dir($dir_path) || !($dirs = glob($dir_path . '/*', GLOB_ONLYDIR))) { continue; } foreach (array_map('fw_fix_path', $dirs) as $demo_dir) { $demo_dir_name = basename($demo_dir); if (!file_exists($demo_dir . '/manifest.php')) { continue; } $manifest = fw_get_variables_from_file($demo_dir . '/manifest.php', array('manifest' => array()), array('uri' => $dir_uri . '/' . $demo_dir_name)); $manifest = array_merge(array('title' => fw_id_to_title($demo_dir_name), 'screenshot' => fw_get_framework_directory_uri('/static/img/no-image.png'), 'preview_link' => '', 'extra' => array()), $manifest['manifest']); $demo = new FW_Ext_Backups_Demo('local-' . md5($demo_dir), 'local', array('source' => $demo_dir)); $demo->set_title($manifest['title']); $demo->set_screenshot($manifest['screenshot']); $demo->set_preview_link($manifest['preview_link']); $demo->set_extra($manifest['extra']); $demos[$demo->get_id()] = $demo; unset($demo); } } self::$demos = array_merge(apply_filters('fw:ext:backups-demo:demos', array()), $demos); } return self::$demos; }
/** * @param string $source_dir * @param string $destination_dir * @return bool|WP_Error */ function fw_ext_backups_copy_dir_recursive($source_dir, $destination_dir) { $source_dir = fw_fix_path($source_dir); $destination_dir = fw_fix_path($destination_dir); $dir_chmod = 0755; if (!file_exists($destination_dir)) { if (!mkdir($destination_dir, $dir_chmod)) { return new WP_Error('mkdir_fail', sprintf(__('Failed to create dir: %s'), $destination_dir)); } } try { foreach ($iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source_dir), RecursiveIteratorIterator::SELF_FIRST) as $item) { if (in_array(basename($iterator->getSubPathName()), array('.', '..'), true)) { continue; // We can't use RecursiveDirectoryIterator::SKIP_DOTS, it was added in php 5.3 } $destination_path = $destination_dir . '/' . $iterator->getSubPathName(); if ($item->isDir()) { if (!mkdir($destination_path, $dir_chmod)) { return new WP_Error('mk_sub_dir_fail', sprintf(__('Failed to create dir: %s'), $destination_path)); } } else { if (!copy($item->getPathname(), $destination_path)) { return new WP_Error('copy_fail', sprintf(__('Failed to copy: %s'), $destination_path)); } } } } catch (UnexpectedValueException $e) { return new WP_Error('dir_copy_fail', (string) $e->getMessage()); } return true; }
/** * {@inheritdoc} * @param array $args * * uploads_dir - path to exported uploads dir (not the actual WP uploads dir! that remains untouched) */ public function execute(array $args, array $state = array()) { $backups = fw_ext('backups'); if (empty($args['uploads_dir'])) { return new WP_Error('no_uploads_dir', __('Uploads dir not specified', 'fw')); } else { $args['uploads_dir'] = fw_fix_path($args['uploads_dir']); if (!file_exists($args['uploads_dir'])) { /** * The uploads directory was not exported, nothing to do. */ return true; } } if (empty($state)) { $state = array('attachment_id' => 0); } /** * @var WPDB $wpdb */ global $wpdb; $sql = implode(array("SELECT * FROM {$wpdb->posts}", "WHERE post_type = 'attachment' AND post_mime_type LIKE %s AND ID > %d", "ORDER BY ID", "LIMIT 7"), " \n"); $wp_uploads_dir = wp_upload_dir(); $wp_uploads_dir_length = mb_strlen($wp_uploads_dir['basedir']); $started_time = time(); $timeout = $backups->get_timeout() - 7; while (time() - $started_time < $timeout) { $attachments = $wpdb->get_results($wpdb->prepare($sql, $wpdb->esc_like('image/') . '%', $state['attachment_id']), ARRAY_A); if (empty($attachments)) { return true; } foreach ($attachments as $attachment) { if (($meta = wp_get_attachment_metadata($attachment['ID'])) && isset($meta['sizes']) && is_array($meta['sizes'])) { $attachment_path = get_attached_file($attachment['ID']); $filename_length = mb_strlen(basename($attachment_path)); foreach ($meta['sizes'] as $size => $sizeinfo) { // replace wp_uploads_dir path $file_path = $args['uploads_dir'] . mb_substr($attachment_path, $wp_uploads_dir_length); // replace file name with resize name $file_path = mb_substr($file_path, 0, -$filename_length) . $sizeinfo['file']; if (file_exists($file_path)) { @unlink($file_path); } } } if (is_array($backup_sizes = get_post_meta($attachment['ID'], '_wp_attachment_backup_sizes', true))) { foreach ($backup_sizes as $size) { $del_file = path_join(dirname($meta['file']), $size['file']); @unlink(path_join($args['uploads_dir'], $del_file)); } } } $state['attachment_id'] = $attachment['ID']; } return $state; }
/** * {@inheritdoc} * @param array $args * * source_dir - everything from this directory will be added in zip * * destination_dir - where the zip file will be created * * Warning! * Zip can't be executed in steps, it will execute way too long, * because it is impossible to update a zip file, every time you add a file to zip, * a new temp copy of original zip is created with new modifications, it is compressed, * and the original zip is replaced. So when the zip will grow in size, * just adding a single file, will take a very long time. */ public function execute(array $args, array $state = array()) { if (!isset($args['source_dir'])) { return new WP_Error('no_source_dir', __('Source dir not specified', 'fw')); } else { $args['source_dir'] = fw_fix_path($args['source_dir']); } if (!isset($args['destination_dir'])) { return new WP_Error('no_destination_dir', __('Destination dir not specified', 'fw')); } else { $args['destination_dir'] = fw_fix_path($args['destination_dir']); } if (!class_exists('ZipArchive')) { return new WP_Error('zip_ext_missing', __('Zip extension missing', 'fw')); } $zip_path = $args['source_dir'] . '/' . implode('-', array('fw-backup', date('Y_m_d-H_i_s'), fw_ext('backups')->manifest->get_version())) . '.zip'; $zip = new ZipArchive(); if (false === ($zip_error_code = $zip->open($zip_path, ZipArchive::CREATE))) { return new WP_Error('cannot_open_zip', sprintf(__('Cannot open zip (Error code: %s)', 'fw'), $zip_error_code)); } $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($args['source_dir']), RecursiveIteratorIterator::LEAVES_ONLY); foreach ($files as $name => $file) { if (!$file->isDir()) { // Skip directories (they would be added automatically) if (($file_path = $file->getRealPath()) !== $zip_path) { $zip->addFile($file_path, substr(fw_fix_path($file_path), strlen($args['source_dir']) + 1)); } } } wp_cache_flush(); FW_Cache::clear(); // Zip archive will be created only after closing object if (!$zip->close()) { return new WP_Error('cannot_close_zip', __('Cannot close the zip file', 'fw')); } if (!rename($zip_path, $args['destination_dir'] . '/' . basename($zip_path))) { return new WP_Error('cannot_move_zip', __('Cannot move zip in destination dir', 'fw')); } return true; }
/** * {@inheritdoc} * @param array $args * * dir - source directory in which is located `database.json.txt` * * [full] - (bool) force full or content restore. if not specified, will be detected automatically * * [required] - (default: false) if database file must exist, else if db restore is optional */ public function execute(array $args, array $state = array()) { if (!isset($args['dir'])) { return new WP_Error('no_source_dir', __('Source dir not specified', 'fw')); } else { $args['dir'] = fw_fix_path($args['dir']); } if (!isset($args['required'])) { $args['required'] = false; } else { $args['required'] = (bool) $args['required']; } if (empty($state)) { if (!file_exists($args['dir'] . '/database.json.txt')) { if ($args['required']) { return new WP_Error('no_db_file', __('Database file not found', 'fw')); } else { return true; } } $state = array('task' => 'cleanup', 'step' => 0, 'params' => array(), 'tables' => array(), 'full' => isset($args['full']) ? (bool) $args['full'] : null); } if (!class_exists('SplFileObject')) { return new WP_Error('no_spl_file_object', __('PHP class SplFileObject is required but not available. Please contact your hosting', 'fw')); } if ($state['task'] === 'cleanup') { return $this->do_cleanup($args, $state); } elseif ($state['task'] === 'inspect') { return $this->do_inspect($args, $state); } elseif ($state['task'] === 'import') { return $this->do_import($args, $state); } elseif ($state['task'] === 'keep:options') { return $this->do_keep_options($args, $state); } elseif ($state['task'] === 'replace') { return $this->do_replace($args, $state); } else { return new WP_Error('invalid_sub_task', sprintf(__('Invalid sub task %s', 'fw'), $state['task'])); } return $state; }
private function get_wp_fs_tmp_dir() { return FW_WP_Filesystem::real_path_to_filesystem_path(apply_filters('fw_tmp_dir', fw_fix_path(WP_CONTENT_DIR) . '/tmp')); }
private static function get_path() { if (is_null(self::$path)) { self::$path = wp_upload_dir(); self::$path = fw_fix_path(self::$path['basedir']) . '/fw/file-cache.php'; } return self::$path; }
/** * Add restore tasks to a collection * @param FW_Ext_Backups_Task_Collection $collection * @param bool $full * @param string|null $zip_path * @param array $filesystem_args {}|{hostname: '', username: '', password: '', connection_type: ''} * @return FW_Ext_Backups_Task_Collection */ public function add_restore_tasks(FW_Ext_Backups_Task_Collection $collection, $full = false, $zip_path = null, $filesystem_args = array()) { $full = (bool) $full; $tmp_dir = self::backups()->get_tmp_dir(); $dirs = $this->get_dirs($full); $id_prefix = 'restore:'; if ($zip_path) { $collection->add_task(new FW_Ext_Backups_Task($id_prefix . 'unzip', 'unzip', array('zip' => $zip_path, 'dir' => $tmp_dir))); } $collection->add_task(new FW_Ext_Backups_Task($id_prefix . 'files-restore', 'files-restore', array('source_dir' => $tmp_dir . '/f', 'destinations' => $dirs, 'filesystem_args' => $filesystem_args, 'skip_dirs' => is_multisite() && ($wp_upload_dir = wp_upload_dir()) ? array(fw_fix_path($wp_upload_dir['basedir']) . '/sites' => true) : array()))); $collection->add_task(new FW_Ext_Backups_Task($id_prefix . 'db-restore', 'db-restore', array('dir' => $tmp_dir, 'full' => $full))); if (!$full && apply_filters('fw:ext:backups:add-restore-task:image-sizes-restore', true, $collection)) { $collection->add_task(new FW_Ext_Backups_Task($id_prefix . 'image-sizes-restore', 'image-sizes-restore', array())); } $collection->add_task(new FW_Ext_Backups_Task($id_prefix . 'tmp-dir-clean:after', 'dir-clean', array('dir' => $tmp_dir))); /** @since 2.0.16 */ do_action('fw:ext:backups:add-restore-tasks', $collection, array('is_full' => $full, 'tmp_dir' => $tmp_dir, 'dirs' => $dirs)); return $collection; }
private function get_tmp_dir($append = '') { return apply_filters('fw_tmp_dir', fw_fix_path(WP_CONTENT_DIR) . '/tmp') . $append; }
/** * {@inheritdoc} * @param array $args * * type - registered download type * * type_args - args for registered type instance * * destination_dir - Where must be placed the downloaded files */ public function execute(array $args, array $state = array()) { if (empty($args['destination_dir'])) { return new WP_Error('no_destination_dir', __('Destination dir not specified', 'fw')); } elseif (!($args['destination_dir'] = fw_fix_path($args['destination_dir']))) { return new WP_Error('invalid_destination_dir', __('Invalid destination dir', 'fw')); } if (empty($args['type']) || !($type = self::get_types()) || !isset($type[$args['type']])) { return new WP_Error('invalid_type', sprintf(__('Invalid type: %s', 'fw'), $args['type'])); } $type = $type[$args['type']]; if (empty($args['type_args'])) { return new WP_Error('no_type_args', sprintf(__('Args not specified for type: %s', 'fw'), $type->get_type())); } $args['type_args'] = array_merge($args['type_args'], array('destination_dir' => $args['destination_dir'])); return $type->download($args['type_args'], $state); }
/** * Create wp filesystem directory recursive * @param string $wp_filesystem_dir_path * @return bool */ public static final function mkdir_recursive($wp_filesystem_dir_path) { /** @var WP_Filesystem_Base $wp_filesystem */ global $wp_filesystem; if (!$wp_filesystem) { trigger_error('Filesystem is not available', E_USER_WARNING); return false; } $wp_filesystem_dir_path = fw_fix_path($wp_filesystem_dir_path); $path = ''; $check_if_exists = true; $firs_loop = true; foreach (explode('/', $wp_filesystem_dir_path) as $dir_name) { if (empty($dir_name)) { if ($firs_loop) { // in first loop $dir_name can be empty because it's starting with } else { trigger_error('Invalid path: ' . $wp_filesystem_dir_path, E_USER_WARNING); return false; } } $firs_loop = false; $path .= '/' . $dir_name; if ($check_if_exists) { if ($wp_filesystem->is_dir($path)) { // do nothing if exists continue; } else { // do not check anymore, next directories sure does not exists $check_if_exists = false; } } $wp_filesystem->mkdir($path, FS_CHMOD_DIR); } return true; }
/** * @internal */ public function _action_update_framework() { $nonce_name = '_nonce_fw_ext_update_framework'; if (!isset($_POST[$nonce_name]) || !wp_verify_nonce($_POST[$nonce_name])) { wp_die(__('Invalid nonce.', 'fw')); } if (!class_exists('_FW_Ext_Update_Framework_Upgrader_Skin')) { fw_include_file_isolated($this->get_declared_path('/includes/classes/class--fw-ext-update-framework-upgrader-skin.php')); } $skin = new _FW_Ext_Update_Framework_Upgrader_Skin(array('title' => __('Update Framework', 'fw'))); require_once ABSPATH . 'wp-admin/admin-header.php'; $skin->header(); $update = $this->get_framework_update(true); do { if ($update === false) { $skin->error(__('Failed to get framework latest version.', 'fw')); break; } elseif (is_wp_error($update)) { $skin->error($update); break; } $context = $this->context; if (!FW_WP_Filesystem::request_access($context, fw_current_url(), array($nonce_name))) { break; } $this->maintenance_mode(true); /** @var WP_Filesystem_Base $wp_filesystem */ global $wp_filesystem; $tmp_download_dir = FW_WP_Filesystem::real_path_to_filesystem_path(fw_fix_path(WP_CONTENT_DIR) . '/cache/framework/update'); // just in case it already exists, clear everything, it may contain broken/old files $wp_filesystem->rmdir($tmp_download_dir, true); if (!FW_WP_Filesystem::mkdir_recursive($tmp_download_dir)) { $skin->error(__('Cannot create directory: ' . $tmp_download_dir, 'fw')); break; } $skin->feedback(__('Downloading framework...', 'fw')); /** @var FW_Ext_Update_Service $service */ $service = $this->get_child($update['service']); $downloaded_files_dir = $service->_download_framework($update['latest_version'], $tmp_download_dir); if (!$downloaded_files_dir) { $skin->error(__('Failed to download framework.', 'fw')); break; } elseif (is_wp_error($downloaded_files_dir)) { $skin->error($downloaded_files_dir); break; } $skin->feedback(__('Installing framework...', 'fw')); $framework_dir = FW_WP_Filesystem::real_path_to_filesystem_path(fw_get_framework_directory()); // remove entire framework directory $wp_filesystem->rmdir($framework_dir, true); // move downloaded directory as new framework directory $wp_filesystem->move($downloaded_files_dir, $framework_dir); $skin->feedback(__('Framework updated.', 'fw')); $wp_filesystem->delete($tmp_download_dir, true, 'd'); $skin->set_result(true); $skin->after(); } while (false); $this->maintenance_mode(false); $skin->footer(); require_once ABSPATH . 'wp-admin/admin-footer.php'; }
private function get_dirs($full = false) { $wp_upload_dir = wp_upload_dir(); $dirs = array('uploads' => fw_fix_path($wp_upload_dir['basedir']), 'plugins' => fw_fix_path(WP_PLUGIN_DIR), 'themes' => fw_fix_path(get_theme_root())); if (is_multisite() && WPMU_PLUGIN_DIR) { $dirs['mu-plugins'] = fw_fix_path(WPMU_PLUGIN_DIR); } if (!$full) { unset($dirs['plugins']); unset($dirs['mu-plugins']); unset($dirs['themes']); } return $dirs; }
/** * Try everything to create and make a path (file or directory) writable * Works only within /wp-content dir, cannot make writable something outside it * @param string $path * @return bool * @deprecated It's not safe to make files writable or create files as php user. Use http://codex.wordpress.org/Filesystem_API */ function fw_try_make_writable($path) { static $root_dir = null; if ($root_dir === null) { $root_dir = fw_fix_path(WP_CONTENT_DIR); } $chmod_writable_dir = 0777; $chmod_writable_file = 0666; do { if (is_writable($path)) { return true; } $path = fw_fix_path($path); $rel_path = explode($root_dir, $path); if (!empty($rel_path[0])) { break; } // is not located in root dir array_shift($rel_path); // remove first empty element $rel_path = implode($root_dir, $rel_path); $rel_path_arr = explode('/', $rel_path); array_shift($rel_path_arr); // remove first empty element if (empty($rel_path_arr)) { break; } $file_name = ''; if (!is_dir($path)) { if (file_exists($path)) { // file exists, we can only try to make it writable if (@chmod($path, $chmod_writable_file)) { true; } else { break; } } $file_name = array_pop($rel_path_arr); } if (!empty($rel_path_arr)) { // check if parent directories exists, if not, create and make writable $dir_path = $root_dir . '/' . implode('/', $rel_path_arr); if (!file_exists($dir_path)) { $created = @mkdir($dir_path, $chmod_writable_dir, true); if (!$created) { break; } } if (!is_writable($dir_path)) { if (@chmod($dir_path, $chmod_writable_dir)) { true; } else { break; } } } if ($file_name) { // file not exists, need to create if (false === file_put_contents($path, '')) { break; } if (is_writable($path)) { return true; } if (@chmod($path, $chmod_writable_file)) { true; } else { break; } } return true; } while (false); return false; }
/** @deprecated */ define('FW_CT_EXTENSIONS_URI', FW_CT_CUSTOM_URI . '/extensions'); } /** @deprecated */ define('FW_PT_DIR', fw_fix_path(get_template_directory())); /** @deprecated */ define('FW_PT_CUSTOM_DIR', FW_PT_DIR . '/framework-customizations'); /** @deprecated */ define('FW_PT_THEME_DIR', FW_PT_CUSTOM_DIR . '/theme'); /** @deprecated */ define('FW_PT_EXTENSIONS_DIR', FW_PT_CUSTOM_DIR . '/extensions'); /** @deprecated */ define('FW_PT_URI', get_template_directory_uri()); /** @deprecated */ define('FW_PT_CUSTOM_URI', FW_PT_URI . '/framework-customizations'); /** @deprecated */ define('FW_PT_THEME_URI', FW_PT_CUSTOM_URI . '/theme'); /** @deprecated */ define('FW_PT_EXTENSIONS_URI', FW_PT_CUSTOM_URI . '/extensions'); /** @deprecated */ define('FW_DIR', FW_PT_DIR . '/framework'); /** @deprecated */ define('FW_EXTENSIONS_DIR', FW_DIR . '/extensions'); /** @deprecated */ define('FW_URI', FW_PT_URI . '/framework'); /** @deprecated */ define('FW_EXTENSIONS_URI', FW_URI . '/extensions'); /** @deprecated */ define('FW_CACHE_DIR', fw_fix_path(WP_CONTENT_DIR) . '/cache/framework'); /** @deprecated */ define('FW_CACHE_URI', WP_CONTENT_URL . '/cache/framework');
private function do_install(FW_Ext_Backups_Demo $demo) { $tmp_dir = self::backups()->get_tmp_dir(); $wp_upload_dir = wp_upload_dir(); $dirs = array('uploads' => fw_fix_path($wp_upload_dir['basedir'])); $id_prefix = 'demo:'; $collection = new FW_Ext_Backups_Task_Collection(self::$task_collection_id); if (!self::backups()->is_disabled()) { $collection = self::backups()->tasks()->add_backup_tasks($collection, false); } $collection->set_title(__('Demo Content Install', 'fw')); $collection->add_task(new FW_Ext_Backups_Task($id_prefix . 'tmp-dir-clean:before', 'dir-clean', array('dir' => $tmp_dir))); $collection->add_task(new FW_Ext_Backups_Task($id_prefix . 'demo-download', 'download', array('type' => $demo->get_source_type(), 'type_args' => $demo->get_source_args(), 'destination_dir' => $tmp_dir))); $collection->add_task(new FW_Ext_Backups_Task($id_prefix . 'files-restore', 'files-restore', array('source_dir' => $tmp_dir . '/f', 'destinations' => $dirs))); $collection->add_task(new FW_Ext_Backups_Task($id_prefix . 'db-restore', 'db-restore', array('dir' => $tmp_dir))); $collection->add_task(new FW_Ext_Backups_Task($id_prefix . 'tmp-dir-clean:after', 'dir-clean', array('dir' => $tmp_dir))); $this->set_active_demo(array('id' => $demo->get_id(), 'result' => null)); self::backups()->tasks()->execute_task_collection($collection); }
/** * @param string $root_dir * @param array $dirs * @param string $previous_file * @param array $exclude_paths * @return array|true|WP_Error ['file.txt', 'file.php', ...] * Important: It will never return an empty array. Only: (array)files, true, WP_Error */ private function get_next_files($root_dir, array &$dirs, $previous_file, array $exclude_paths) { $rel_dir = empty($dirs) ? '' : '/' . implode('/', $dirs); $included_hidden_names = fw_ext('backups')->get_config('included_hidden_names'); if ($paths = glob($root_dir . $rel_dir . '/{,.}[!.,!..]*', GLOB_MARK | GLOB_BRACE)) { $files = array(); // result $file_found = empty($previous_file); // find previous file and return next files $count = 0; foreach ($paths as $path) { $file = basename($path); if (!$file_found) { $file_found = $file === $previous_file; continue; } if ($file[0] === '.' && !isset($included_hidden_names[$file])) { continue; } if (is_dir($path)) { if (isset($exclude_paths[fw_fix_path($path)])) { continue; } elseif ($files) { // return collected files, will go inside directory on next call return $files; } else { $dirs[] = $file; return $this->get_next_files($root_dir, $dirs, '', $exclude_paths); } } else { $files[] = $file; if (++$count > $this->get_max_files_per_cycle()) { return $files; } } } if (empty($files)) { if ($file_found) { // reached end of the directory if ($dirs) { // go a directory back $previous_file = array_pop($dirs); return $this->get_next_files($root_dir, $dirs, $previous_file, $exclude_paths); } else { // root directory end reached return true; } } else { return new WP_Error('previous_file_not_found', sprintf(__('Failed to restore dir listing from: %s', 'fw'), $root_dir . $rel_dir . '/' . $previous_file)); } } return $files; } else { // directory is empty if ($dirs) { // go a directory back $previous_file = array_pop($dirs); return $this->get_next_files($root_dir, $dirs, $previous_file, $exclude_paths); } else { // root directory end reached return true; } } }
/** * {@inheritdoc} * @param array $args * * dir - source directory in which is located `database.json.txt` * * [full] - (bool) force full or content restore. if not specified, will be detected automatically * * [required] - (default: false) if database file must exist, else if db restore is optional */ public function execute(array $args, array $state = array()) { if (!isset($args['dir'])) { return new WP_Error('no_source_dir', __('Source dir not specified', 'fw')); } else { $args['dir'] = fw_fix_path($args['dir']); } if (!isset($args['required'])) { $args['required'] = false; } else { $args['required'] = (bool) $args['required']; } if (empty($state)) { if (!file_exists($args['dir'] . '/database.json.txt')) { if ($args['required']) { return new WP_Error('no_db_file', __('Database file not found', 'fw')); } else { return true; } } $state = array('task' => 'cleanup', 'step' => 0, 'params' => array(), 'tables' => array(), 'full' => isset($args['full']) ? (bool) $args['full'] : null); } global $wpdb; /** @var WPDB $wpdb */ if ($state['task'] === 'cleanup') { // delete all tables with temporary prefix $this->get_tmp_table_prefix() if ($table_names = $wpdb->get_col($wpdb->prepare('SHOW TABLES LIKE %s', $wpdb->esc_like($this->get_tmp_table_prefix()) . '%'))) { if (!$wpdb->query('DROP TABLE ' . esc_sql($table_name = array_pop($table_names)))) { return new WP_Error('drop_tmp_table_fail', sprintf(__('Cannot drop temporary table: %s', 'fw'), $table_name)); } return $state; } else { $state['task'] = 'inspect'; $state['step'] = 0; return $state; } } elseif ($state['task'] === 'inspect') { try { $fo = new SplFileObject($args['dir'] . '/database.json.txt'); } catch (RuntimeException $e) { $fo = null; return new WP_Error('cannot_open_file', __('Cannot open db file', 'fw')); } try { $fo->seek($state['step']); } catch (RuntimeException $e) { $fo = null; return new WP_Error('cannot_move_file_cursor', __('Cannot move cursor in db file', 'fw')); } $started_time = time(); $timeout = fw_ext('backups')->get_timeout() - 7; while (time() - $started_time < $timeout) { if ($line = $fo->current()) { if (is_null($line = json_decode($line, true))) { $fo = null; return new WP_Error('line_decode_fail', sprintf(__('Failed to decode line %d from db file.', 'fw') . ' ' . fw_get_json_last_error_message(), $state['step'] + 1)); } if ($line['type'] === 'row' && $line['data']['table'] === 'options' && isset($line['data']['row']['option_name']) && in_array($line['data']['row']['option_name'], array('siteurl', 'home'))) { $state['params'][$line['data']['row']['option_name']] = $line['data']['row']['option_value']; } elseif ($line['type'] === 'table' && !isset($state['tables'][$line['data']['name']])) { $state['tables'][$line['data']['name']] = true; } elseif ($line['type'] === 'param') { $state['params'][$line['data']['name']] = $line['data']['value']; } } elseif ($line === false && !$fo->eof()) { $fo = null; return new WP_Error('line_read_fail', sprintf(__('Cannot read line %d from db file', 'fw'), $state['step'] + 1)); } else { if (!isset($state['params']['siteurl']) || !isset($state['params']['home'])) { return new WP_Error('params_not_found', __('Required params not found', 'fw')); } $is_full_backup = isset($state['tables']['commentmeta']) && isset($state['tables']['comments']) && isset($state['tables']['links']) && isset($state['tables']['options']) && isset($state['tables']['postmeta']) && isset($state['tables']['posts']) && isset($state['tables']['terms']) && isset($state['tables']['term_relationships']) && isset($state['tables']['term_taxonomy']) && isset($state['tables']['usermeta']) && isset($state['tables']['users']); if (is_multisite()) { /* @link https://codex.wordpress.org/Database_Description */ $is_full_backup = $is_full_backup && (isset($state['tables']['blogs']) && isset($state['tables']['blog_versions']) && isset($state['tables']['registration_log']) && isset($state['tables']['signups']) && isset($state['tables']['site']) && isset($state['tables']['sitemeta'])); } if (is_null($state['full'])) { $state['full'] = $is_full_backup; } elseif ($state['full'] && !$is_full_backup) { return new WP_Error('full_db_restore_impossible', __('Cannot do full db restore because backup is missing some tables', 'fw')); } $skip_tables = array('users' => true, 'usermeta' => true); if (!$state['full']) { $skip_tables = array_merge($skip_tables, array('blogs' => true, 'blog_versions' => true, 'registration_log' => true, 'signups' => true, 'site' => true, 'sitemeta' => true, 'sitecategories' => true)); } foreach (array_keys($skip_tables) as $table_name) { if (isset($state['tables'][$table_name])) { $state['tables'][$table_name] = false; } } unset($skip_tables); $state['step'] = 0; $state['task'] = 'import'; $fo = null; return $state; } $state['step']++; $fo->next(); } $fo = null; } elseif ($state['task'] === 'import') { try { $fo = new SplFileObject($args['dir'] . '/database.json.txt'); } catch (RuntimeException $e) { $fo = null; return new WP_Error('cannot_open_file', __('Cannot open db file', 'fw')); } try { $fo->seek($state['step']); } catch (RuntimeException $e) { $fo = null; return new WP_Error('cannot_move_file_cursor', __('Cannot move cursor in db file', 'fw')); } $params = array('search' => array(), 'replace' => array()); /** * Note: rtrim(..., '/') is used to prevent wrong link replace * 'http://abc.com/img.jpg' -> 'http://def.comimg.jpg' * Note: First links should be the longest, to prevent the short part replace * when the long part must be replaced (thus making wrong replace) */ $search_replace = array(); /** * This parameter (fix) was added after extension release * so some demo installs may not have it */ if (isset($state['params']['wp_upload_dir_baseurl'])) { $wp_upload_dir = wp_upload_dir(); $search_replace[rtrim($state['params']['wp_upload_dir_baseurl'], '/')] = rtrim($wp_upload_dir['baseurl'], '/'); unset($wp_upload_dir); } $search_replace[rtrim($state['params']['siteurl'], '/')] = rtrim(get_option('siteurl'), '/'); $search_replace[rtrim($state['params']['home'], '/')] = rtrim(get_option('home'), '/'); foreach ($search_replace as $search => $replace) { $search_replace[fw_get_url_without_scheme($search)] = fw_get_url_without_scheme($replace); } foreach ($search_replace as $search => $replace) { if ($search === $replace) { continue; } foreach (array($search => $replace, json_encode($search) => json_encode($replace)) as $search => $replace) { $params['search'][] = $search; $params['replace'][] = $replace; $params['search'][] = str_replace('/', '\\/', $search); $params['replace'][] = str_replace('/', '\\/', $replace); $params['search'][] = str_replace('/', '\\\\/', $search); $params['replace'][] = str_replace('/', '\\\\/', $replace); $params['search'][] = str_replace('/', '\\\\\\/', $search); $params['replace'][] = str_replace('/', '\\\\\\/', $replace); } } unset($search_replace, $search, $replace); $utf8mb4_is_supported = defined('DB_CHARSET') && DB_CHARSET === 'utf8mb4'; $started_time = time(); $timeout = fw_ext('backups')->get_timeout() - 7; while (time() - $started_time < $timeout) { if ($line = $fo->current()) { if (is_null($line = json_decode($line, true))) { $fo = null; return new WP_Error('line_decode_fail', sprintf(__('Failed to decode line %d from db file.', 'fw') . ' ' . fw_get_json_last_error_message(), $state['step'] + 1)); } switch ($line['type']) { case 'table': if (!$state['tables'][$line['data']['name']]) { break; // skip } $tmp_table_name = $this->get_tmp_table_prefix() . $line['data']['name']; if (false === $wpdb->query('DROP TABLE IF EXISTS ' . esc_sql($tmp_table_name))) { $fo = null; return new WP_Error('tmp_table_drop_fail', sprintf(__('Failed to drop tmp table %s', 'fw'), $tmp_table_name)); } $sql = 'CREATE TABLE `' . esc_sql($tmp_table_name) . "` (\n"; $cols_sql = array(); foreach ($line['data']['columns'] as $col_name => $col_opts) { $cols_sql[] = '`' . esc_sql($col_name) . '` ' . ($utf8mb4_is_supported ? $col_opts : str_replace('utf8mb4', 'utf8', $col_opts)); } foreach ($line['data']['indexes'] as $index) { $cols_sql[] = $index; } $sql .= implode(", \n", $cols_sql); unset($cols_sql); $sql .= ') ' . ($utf8mb4_is_supported ? $line['data']['opts'] : str_replace('utf8mb4', 'utf8', $line['data']['opts'])); if (false === $wpdb->query($sql)) { $fo = null; return new WP_Error('tmp_table_create_fail', sprintf(__('Failed to create tmp table %s', 'fw'), $tmp_table_name)); } unset($sql); break; case 'row': if (!isset($state['tables'][$line['data']['table']])) { $fo = null; return new WP_Error('invalid_table', sprintf(__('Tried to insert data in table that was not imported %s', 'fw'), $line['data']['table'])); } elseif (!$state['tables'][$line['data']['table']]) { break; // the table was skipped } elseif ('options' === $line['data']['table'] && apply_filters('fw_ext_backups_db_restore_exclude_option', false, $line['data']['row']['option_name'], $state['full'])) { break; } $tmp_table_name = $this->get_tmp_table_prefix() . $line['data']['table']; if (!empty($params['search'])) { $this->array_str_replace_recursive($params['search'], $params['replace'], $line['data']['row']); } if (isset($state['params']['wpdb_prefix'])) { $column = $search = null; switch ($line['data']['table']) { case 'options': $column = 'option_name'; $search = array('user_roles'); break; case 'usermeta': $column = 'meta_key'; $search = array('capabilities', 'user_level', 'dashboard_quick_press_last_post_id', 'user-settings', 'user-settings-time'); break; } if ($column && $search) { foreach ($search as $name) { if (substr($line['data']['row'][$column], -strlen($name)) === $name && substr($line['data']['row'][$column], 0, strlen($state['params']['wpdb_prefix'])) === $state['params']['wpdb_prefix']) { $line['data']['row'][$column] = $wpdb->prefix . substr($line['data']['row'][$column], strlen($state['params']['wpdb_prefix'])); } } } } /** * Insert pieces of rows to prevent mysql error when inserting very big strings. * Do this only if table has index column so we can do 'UPDATE ... WHERE index_column = %s' */ if ($index_column = $this->get_index_column($tmp_table_name)) { /** * Tested, and maximum value which works is 950000 * but may be tables with many columns with big strings * so set this value lower to be sure the limit is not reached. */ $value_max_length = 500000; $update_count = 0; $index_column_value = $line['data']['row'][$index_column]; $row_lengths = array(); while ($line['data']['row']) { $row = array(); foreach (array_keys($line['data']['row']) as $column_name) { $row[$column_name] = mb_substr($line['data']['row'][$column_name], 0, $value_max_length); $row_length = mb_strlen($row[$column_name]); if (!isset($row_lengths[$column_name])) { $row_lengths[$column_name] = mb_strlen($line['data']['row'][$column_name]); } /** * The string was cut between a slashed character, for e.g. \" or \\ * Append next characters until the slashing is closed */ while (($last_char = mb_substr($row[$column_name], -1)) === '\\' && $row_length < $row_lengths[$column_name]) { $row[$column_name] .= mb_substr($line['data']['row'][$column_name], $row_length - 1, 1); $row_length++; // do not call mb_strlen() on every loop } $line['data']['row'][$column_name] = mb_substr($line['data']['row'][$column_name], $row_length); if (empty($line['data']['row'][$column_name])) { unset($line['data']['row'][$column_name]); } } if ($update_count) { $set_sql = array(); foreach (array_keys($row) as $column_name) { $set_sql[] = '`' . esc_sql($column_name) . '` = CONCAT( `' . esc_sql($column_name) . '`' . ' , ' . $wpdb->prepare('%s', $row[$column_name]) . ')'; } $set_sql = implode(', ', $set_sql); $sql = implode(" \n", array("UPDATE {$tmp_table_name} SET", $set_sql, 'WHERE `' . esc_sql($index_column) . '` = ' . $wpdb->prepare('%s', $index_column_value))); } else { $sql = implode(" \n", array("INSERT INTO {$tmp_table_name} (", '`' . implode('`, `', array_map('esc_sql', array_keys($row))) . '`', ") VALUES (", implode(', ', array_map(array($this, '_wpdb_prepare_string'), $row)), ")")); } if (false === $wpdb->query($sql)) { $fo = null; return new WP_Error('insert_fail', sprintf(__('Failed insert row from line %d', 'fw'), $state['step'] + 1)); } unset($sql); $update_count++; } } else { $sql = implode(" \n", array("INSERT INTO {$tmp_table_name} (", '`' . implode('`, `', array_map('esc_sql', array_keys($line['data']['row']))) . '`', ") VALUES (", implode(', ', array_map(array($this, '_wpdb_prepare_string'), $line['data']['row'])), ")")); if (false === $wpdb->query($sql)) { $fo = null; return new WP_Error('insert_fail', sprintf(__('Failed insert row from line %d', 'fw'), $state['step'] + 1)); } unset($sql); } break; case 'param': break; default: $fo = null; return new WP_Error('invalid_json_type', sprintf(__('Invalid json type %s in db file', 'fw'), $line['type'])); } } elseif ($line === false && !$fo->eof()) { $fo = null; return new WP_Error('line_read_fail', __('Cannot read line from db file', 'fw')); } else { $fo = null; $state['step'] = 0; $state['task'] = 'keep:options'; return $state; } $state['step']++; $fo->next(); } $fo = null; } elseif ($state['task'] === 'keep:options') { if ($state['full'] && !isset($state['tables']['options'])) { // on full backup nothing is kept } else { $keep_options = array_merge(fw_ext('backups')->get_config('db.restore.keep_options'), apply_filters('fw_ext_backups_db_restore_keep_options', array(), $state['full'])); $started_time = time(); $timeout = fw_ext('backups')->get_timeout() - 7; // restore array pointer position if ($state['step']) { while (($option_name = key($keep_options)) && $option_name !== $state['step']) { next($keep_options); } if (empty($option_name)) { return new WP_Error('keep_options_continue_fail', __('Failed to restore options keeping step', 'fw')); } } else { $state['step'] = key($keep_options); } do { $tmp_options_table = esc_sql($this->get_tmp_table_prefix() . 'options'); while (time() - $started_time < $timeout) { if ($row = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->options} WHERE option_name = %s LIMIT 1", $state['step']), ARRAY_A)) { $wpdb->query($wpdb->prepare("DELETE FROM {$tmp_options_table} WHERE option_name = %s", $state['step'])); /** * Prevent error: Duplicate entry '90' for key 'PRIMARY' for query INSERT INTO ...options * Option id will be auto incremented on insert */ unset($row['option_id']); if (false === $wpdb->query("INSERT INTO {$tmp_options_table} ( \n" . '`' . implode('`, `', array_map('esc_sql', array_keys($row))) . "` \n" . ") VALUES ( \n" . implode(', ', array_map(array($this, '_wpdb_prepare_string'), $row)) . " \n" . ')')) { return new WP_Error('option_keep_fail', sprintf(__('Failed to keep option: %s', 'fw'), $state['step'])); } } next($keep_options); if (is_null($state['step'] = key($keep_options))) { break 2; } } return $state; } while (false); } $state['step'] = 0; $state['task'] = 'replace'; return $state; } elseif ($state['task'] === 'replace') { /** * P.P. We can't rename tables one by one, that can cause errors on next request (db corrupt) * so the only solution is to rename all table at once * and hope that the execution will not exceed timeout limit * P.P.S. Table rename should be fast http://dba.stackexchange.com/a/53850 */ $current_tables = $this->get_tables(); $rename_sql = array(); $drop_sql = array(); foreach ($state['tables'] as $name => $restored) { if ($restored) { if (isset($current_tables[$name])) { // drop only if exists. to prevent sql error $drop_sql[] = esc_sql($wpdb->prefix . $name); } $rename_sql[] = esc_sql($this->get_tmp_table_prefix() . $name) . ' TO ' . esc_sql($wpdb->prefix . $name); } } if (!empty($rename_sql)) { if (!empty($drop_sql)) { $drop_sql = "DROP TABLE \n" . implode(" , \n", $drop_sql); if (!$wpdb->query($drop_sql)) { return new WP_Error('tables_drop_fail', __('Tables drop failed', 'fw')); } } $rename_sql = "RENAME TABLE \n" . implode(" , \n", $rename_sql); $wpdb->query($rename_sql); // RENAME query doesn't return bool, so use the below method to detect error if ($rename_sql === $wpdb->last_query && $wpdb->last_error) { return new WP_Error('tables_rename_fail', __('Tables rename failed.', 'fw') . ' ' . $wpdb->last_error); } } wp_cache_flush(); return true; } else { return new WP_Error('invalid_sub_task', sprintf(__('Invalid sub task %s', 'fw'), $state['task'])); } return $state; }
/** * Create wp filesystem directory recursive * @param string $wp_filesystem_dir_path * @return bool */ public static final function mkdir_recursive($wp_filesystem_dir_path) { /** @var WP_Filesystem_Base $wp_filesystem */ global $wp_filesystem; if (!$wp_filesystem) { trigger_error('Filesystem is not available', E_USER_ERROR); } elseif (is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code()) { trigger_error('Filesystem: ' . $wp_filesystem->errors->get_error_message(), E_USER_ERROR); } $wp_filesystem_dir_path = fw_fix_path($wp_filesystem_dir_path); $path = false; foreach (self::get_base_dirs_map() as $base_real_path => $base_wp_filesystem_path) { $prefix_regex = '/^' . preg_quote($base_wp_filesystem_path, '/') . '/'; // check if path is inside base path if (!preg_match($prefix_regex, $wp_filesystem_dir_path)) { continue; } $path = $base_wp_filesystem_path; break; } if (!$path) { trigger_error(sprintf(__('Cannot create directory "%s". It must be inside "%s"', 'fw'), $wp_filesystem_dir_path, implode(__('" or "', 'fw'), self::get_base_dirs_map())), E_USER_WARNING); return false; } if ($path === '/') { $rel_path = $wp_filesystem_dir_path; } else { $rel_path = preg_replace('/^' . preg_quote($path, '/') . '/', '', $wp_filesystem_dir_path); } // improvement: do not check directory for existence if it's known that sure it doesn't exist $check_if_exists = true; foreach (explode('/', ltrim($rel_path, '/')) as $dir_name) { $path .= '/' . $dir_name; // When WP FS abspath is '/', $path can be '//wp-content'. Fix it '/wp-content' $path = fw_fix_path($path); if ($check_if_exists) { if ($wp_filesystem->is_dir($path)) { // do nothing if exists continue; } else { // do not check anymore, next directories sure doesn't exist $check_if_exists = false; } } if (!$wp_filesystem->mkdir($path, FS_CHMOD_DIR)) { return false; } } return true; }
/** * All backups (zip) will go in this directory * @return string */ public function get_backups_dir() { $cache_key = $this->get_cache_key('/dir'); try { return FW_Cache::get($cache_key); } catch (FW_Cache_Not_Found_Exception $e) { $uploads = wp_upload_dir(); $dir = fw_fix_path($uploads['basedir']) . '/fw-backup'; FW_Cache::set($cache_key, $dir); return $dir; } }
/** * Download (and activate) extensions * After refresh they should be active, if all dependencies will be met and if parent-extension::_init() will not return false * @param array $extensions {'ext_1' => array(), 'ext_2' => array(), ...} * @param array $opts * @return WP_Error|bool|array * true: when all extensions succeeded * array: when some/all failed */ public function install_extensions(array $extensions, $opts = array()) { $opts = array_merge(array('cancel_on_error' => false, 'activate' => true, 'verbose' => false), $opts); $cancel_on_error = $opts['cancel_on_error']; // fixme: remove successfully installed extensions before error? $activate = $opts['activate']; $verbose = $opts['verbose']; unset($opts); if (!$this->can_install()) { return new WP_Error('access_denied', __('You have no permissions to install extensions', 'fw')); } if (empty($extensions)) { return new WP_Error('no_extensions', __('No extensions provided', 'fw')); } global $wp_filesystem; if (!$wp_filesystem || is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code()) { return new WP_Error('fs_not_initialized', __('WP Filesystem is not initialized', 'fw')); } if (function_exists('ini_get')) { $timeout = intval(ini_get('max_execution_time')); } else { $timeout = false; } $available_extensions = $this->get_available_extensions(); $installed_extensions = $this->get_installed_extensions(); $result = $downloaded_extensions = array(); $has_errors = false; while (!empty($extensions)) { $not_used_var = reset($extensions); $extension_name = key($extensions); unset($extensions[$extension_name]); $extensions_before_install = array_keys($installed_extensions); if (isset($installed_extensions[$extension_name])) { $result[$extension_name] = new WP_Error('extension_installed', sprintf(__('Extension "%s" is already installed.', 'fw'), $this->get_extension_title($extension_name))); $has_errors = true; if ($cancel_on_error) { break; } else { continue; } } if (!isset($available_extensions[$extension_name])) { $result[$extension_name] = new WP_Error('extension_not_available', sprintf(__('Extension "%s" is not available for install.', 'fw'), $this->get_extension_title($extension_name))); $has_errors = true; if ($cancel_on_error) { break; } else { continue; } } $parents = array($extension_name); $current_parent = $extension_name; while (!empty($available_extensions[$current_parent]['parent'])) { $current_parent = $available_extensions[$current_parent]['parent']; if (!isset($available_extensions[$current_parent])) { $result[$extension_name] = new WP_Error('parent_extension_not_available', sprintf(__('Parent extension "%s" not available.', 'fw'), $this->get_extension_title($current_parent))); $has_errors = true; if ($cancel_on_error) { break 2; } else { continue 2; } } $parents[] = $current_parent; } $parents = array_reverse($parents); $destination_path = array('framework' => fw_get_framework_directory(), 'theme' => fw_fix_path(get_template_directory()) . fw_get_framework_customizations_dir_rel_path()); $current_extension_path = ''; foreach ($parents as $parent_extension_name) { $current_extension_path .= '/extensions/' . $parent_extension_name; $destination = isset($available_extensions[$parent_extension_name]['theme']) && $available_extensions[$parent_extension_name]['theme'] ? 'theme' : 'framework'; if (isset($installed_extensions[$parent_extension_name])) { continue; // skip already installed extensions } if ($verbose) { $verbose_message = sprintf(__('Downloading the "%s" extension...', 'fw'), $this->get_extension_title($parent_extension_name)); if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) { $verbose->feedback($verbose_message); } else { echo fw_html_tag('p', array(), $verbose_message); } } // increase timeout if ($timeout !== false && function_exists('set_time_limit')) { $timeout += 30; set_time_limit($timeout); } $wp_fw_downloaded_dir = $this->download($parent_extension_name, $available_extensions[$parent_extension_name]); if (is_wp_error($wp_fw_downloaded_dir)) { if ($verbose) { $verbose_message = $wp_fw_downloaded_dir->get_error_message(); if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) { $verbose->error($verbose_message); } else { echo fw_html_tag('p', array(), $verbose_message); } } $result[$extension_name] = $wp_fw_downloaded_dir; $has_errors = true; if ($cancel_on_error) { break 2; } else { continue 2; } } if ($verbose) { $verbose_message = sprintf(__('Installing the "%s" extension...', 'fw'), $this->get_extension_title($parent_extension_name)); if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) { $verbose->feedback($verbose_message); } else { echo fw_html_tag('p', array(), $verbose_message); } } $merge_result = $this->merge_extension($wp_fw_downloaded_dir, FW_WP_Filesystem::real_path_to_filesystem_path($destination_path[$destination] . $current_extension_path)); if (is_wp_error($merge_result)) { if ($verbose) { $verbose_message = $merge_result->get_error_message(); if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) { $verbose->error($verbose_message); } else { echo fw_html_tag('p', array(), $verbose_message); } } $result[$extension_name] = $merge_result; $has_errors = true; if ($cancel_on_error) { break 2; } else { continue 2; } } if ($verbose) { $verbose_message = sprintf(__('The %s extension has been successfully installed.', 'fw'), $this->get_extension_title($parent_extension_name)); if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) { $verbose->feedback($verbose_message); } else { echo fw_html_tag('p', array(), $verbose_message); } } $downloaded_extensions[$parent_extension_name] = array(); unset($installed_extensions); $installed_extensions = $this->get_installed_extensions(true); } $result[$extension_name] = true; /** * Collect required extensions of the newly installed extensions */ foreach (array_diff(array_keys($installed_extensions), $extensions_before_install) as $new_extension_name) { foreach (array_keys(fw_akg('requirements/extensions', $installed_extensions[$new_extension_name]['manifest'], array())) as $required_extension_name) { if (isset($installed_extensions[$required_extension_name])) { // already installed continue; } $extensions[$required_extension_name] = array(); } } } if ($activate) { $activate_extensions = array(); foreach ($result as $extension_name => $extension_result) { if (!is_wp_error($extension_result)) { $activate_extensions[$extension_name] = array(); } } if (!empty($activate_extensions)) { if ($verbose) { $verbose_message = _n('Activating extension...', 'Activating extensions...', count($activate_extensions), 'fw'); if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) { $verbose->feedback($verbose_message); } else { echo fw_html_tag('p', array(), $verbose_message); } } $activation_result = $this->activate_extensions($activate_extensions); if ($verbose) { if (is_wp_error($activation_result)) { if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) { $verbose->error($activation_result->get_error_message()); } else { echo fw_html_tag('p', array(), $activation_result->get_error_message()); } } elseif (is_array($activation_result)) { $verbose_message = array(); foreach ($activation_result as $extension_name => $extension_result) { if (is_wp_error($extension_result)) { $verbose_message[] = $extension_result->get_error_message(); } } $verbose_message = '<ul><li>' . implode('</li><li>', $verbose_message) . '</li></ul>'; if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) { $verbose->error($verbose_message); } else { echo fw_html_tag('p', array(), $verbose_message); } } elseif ($activation_result === true) { $verbose_message = _n('Extension has been successfully activated.', 'Extensions has been successfully activated.', count($activate_extensions), 'fw'); if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) { $verbose->feedback($verbose_message); } else { echo fw_html_tag('p', array(), $verbose_message); } } } } } do_action('fw_extensions_install', $result); if ($cancel_on_error && $has_errors) { if (($last_result = end($result)) && is_wp_error($last_result)) { return $last_result; } else { // this should not happen, but just to be sure (for the future, if the code above will be changed) return new WP_Error('installation_failed', _n('Cannot install extension', 'Cannot install extensions', count($extensions), 'fw')); } } if ($has_errors) { return $result; } else { return true; } }
/** * Log debug information in ABSPATH/fw-update.log * @param string $message * @return bool|void */ private static function _fw_update_debug_log($message) { /** @var WP_Filesystem_Base $wp_filesystem */ global $wp_filesystem; if (!$wp_filesystem) { return; } $file_fs_path = fw_fix_path($wp_filesystem->abspath()) . '/fw-update.log'; if ($wp_filesystem->exists($file_fs_path)) { $current_log = $wp_filesystem->get_contents($file_fs_path); if ($current_log === false) { return false; } } else { $current_log = ''; } $message = '[' . date('Y-m-d H:i:s') . '] ' . $message; $wp_filesystem->put_contents($file_fs_path, $current_log . $message . "\n"); }
/** * @param string $source_dir path * @param string $destination_dir path * @param bool $fs Use WP_Filesystem or not * @param array $skip_dirs {'path': mixed} * * @return WP_Error|true */ private function copy_dir($source_dir, $destination_dir, $fs, $skip_dirs) { $included_hidden_names = fw_ext('backups')->get_config('included_hidden_names'); if ($fs) { global $wp_filesystem; /** @var WP_Filesystem_Base $wp_filesystem */ $fs_source_dir = fw_fix_path(FW_WP_Filesystem::real_path_to_filesystem_path($source_dir)); if (empty($fs_source_dir)) { return new WP_Error('dir_to_fs_failed', sprintf(__('Cannot convert Filesystem path: %s', 'fw'), $source_dir)); } elseif (false === ($list = $wp_filesystem->dirlist($fs_source_dir, true))) { return new WP_Error('dir_list_failed', sprintf(__('Failed to list dir: %s', 'fw'), $source_dir)); } foreach ($list as $file) { if ($file['name'][0] === '.' && !isset($included_hidden_names[$file['name']])) { continue; } $file_path = $source_dir . '/' . $file['name']; $fs_file_path = $fs_source_dir . '/' . $file['name']; $destination_file_path = $destination_dir . '/' . $file['name']; $fs_destination_file_path = fw_fix_path(FW_WP_Filesystem::real_path_to_filesystem_path($destination_file_path)); if (empty($fs_destination_file_path)) { return new WP_Error('path_to_fs_failed', sprintf(__('Cannot convert Filesystem path: %s', 'fw'), $destination_file_path)); } if ($file['type'] === 'd') { if (isset($skip_dirs[$destination_file_path])) { continue; } else { foreach ($skip_dirs as $skip_dir => $skip_dir_data) { if (strlen(preg_replace('/^' . preg_quote($destination_file_path, '/') . '/', '', $skip_dir)) != strlen($skip_dir)) { continue 2; // skip dir if it's inside current dir } } } if (!$wp_filesystem->mkdir($fs_destination_file_path)) { return new WP_Error('fs_mkdir_fail', sprintf(__('Failed to create dir: %s', 'fw'), $destination_file_path)); } if (is_wp_error($result = copy_dir($fs_file_path, $fs_destination_file_path))) { return $result; } } else { if (!$wp_filesystem->copy($fs_file_path, $fs_destination_file_path)) { return new WP_Error('file_copy_fail', sprintf(__('Failed to copy file: %s', 'fw'), $file_path)); } } } return true; } else { $names = array_diff(($names = scandir($source_dir)) ? $names : array(), array('.', '..')); foreach ($names as $file_name) { $file_path = $source_dir . '/' . $file_name; $destination_file_path = $destination_dir . '/' . $file_name; if ($file_name[0] === '.' && !isset($included_hidden_names[$file_name])) { continue; } if (is_dir($file_path)) { if (isset($skip_dirs[$destination_file_path])) { continue; } else { foreach ($skip_dirs as $skip_dir => $skip_dir_data) { if (strlen(preg_replace('/^' . preg_quote($destination_file_path, '/') . '/', '', $skip_dir)) != strlen($skip_dir)) { continue 2; // skip dir it's inside current dir } } } if (is_dir($destination_file_path)) { /** * Some times empty directories are not deleted ( @see fw_ext_backups_rmdir_recursive() ) * even if rmdir() returns true, the directory remains (I don't know why), * so a workaround is to check if it exists and do not try to created it * (because create will return false) */ } elseif (!mkdir($destination_file_path)) { return new WP_Error('dir_mk_fail', sprintf(__('Failed to create dir: %s', 'fw'), $destination_file_path)); } if (is_wp_error($result = $this->copy_dir($file_path, $destination_file_path, $fs, array()))) { return $result; } } else { if (!copy($file_path, $destination_file_path)) { return new WP_Error('file_copy_fail', sprintf(__('Failed to copy file: %s', 'fw'), $file_path)); } } } return true; } }
/** * {@inheritdoc} * @param array $args * * source_dir - everything from this directory will be added in zip * * destination_dir - where the zip file will be created * * Warning! * Zip can't be executed in steps, it will execute way too long, * because it is impossible to update a zip file, every time you add a file to zip, * a new temp copy of original zip is created with new modifications, it is compressed, * and the original zip is replaced. So when the zip will grow in size, * just adding a single file, will take a very long time. */ public function execute(array $args, array $state = array()) { if (!isset($args['source_dir'])) { return new WP_Error('no_source_dir', __('Source dir not specified', 'fw')); } elseif (!file_exists($args['source_dir'] = fw_fix_path($args['source_dir']))) { return new WP_Error('invalid_source_dir', __('Source dir does not exist', 'fw')); } if (!isset($args['destination_dir'])) { return new WP_Error('no_destination_dir', __('Destination dir not specified', 'fw')); } else { $args['destination_dir'] = fw_fix_path($args['destination_dir']); } if (empty($state)) { $state = array('files_count' => 0); } if (!class_exists('ZipArchive')) { return new WP_Error('zip_ext_missing', __('Zip extension missing', 'fw')); } $zip_path = $args['source_dir'] . '/' . implode('-', array('fw-backup', date('Y_m_d-H_i_s'), fw_ext('backups')->manifest->get_version())) . '.zip'; $zip = new ZipArchive(); if (false === ($zip_error_code = $zip->open($zip_path, ZipArchive::CREATE))) { return new WP_Error('cannot_open_zip', sprintf(__('Cannot open zip (Error code: %s)', 'fw'), $zip_error_code)); } /** @var FW_Extension_Backups $ext */ $ext = fw_ext('backups'); $max_time = time() + min(abs($ext->get_timeout() / 2), 10); // $zip->setCompression*() was introduced in PHP 7.0 $set_compression_is_available = method_exists($zip, 'setCompressionName'); $files_count = 0; $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($args['source_dir']), RecursiveIteratorIterator::LEAVES_ONLY); foreach ($files as $file) { if ($execution_not_finished = time() > $max_time) { break; } if (!$this->file_is_zipable($file, $zip_path)) { continue; } ++$files_count; if ($state['files_count'] > $files_count) { // skip already compressed files in previous step continue; } $zip->addFile($file_path = $file->getRealPath(), $file_zip_path = substr(fw_fix_path($file_path), strlen($args['source_dir']) + 1)); if ($set_compression_is_available) { $zip->setCompressionName($file_zip_path, ZipArchive::CM_STORE); } } // Zip archive will be created only after closing the object if (!$zip->close()) { return new WP_Error('cannot_close_zip', __('Cannot close the zip file', 'fw')); } $state['files_count'] = $files_count; if ($execution_not_finished) { // There are more files to be processed, the execution hasn't finished return $state; } if (!$files_count) { /** * Happens on Content Backup when uploads/ is empty */ return true; } if (!rename($zip_path, $args['destination_dir'] . '/' . basename($zip_path))) { return new WP_Error('cannot_move_zip', __('Cannot move zip in destination dir', 'fw')); } return true; }