/** * @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; } }
/** * @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_CACHE_DIR . '/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_DIR); // 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'; }
/** * Merge the downloaded extension directory with the existing directory * * @param string $source_wp_fs_dir Downloaded extension directory * @param string $destination_wp_fs_dir * * @return null|WP_Error */ private function merge_extension($source_wp_fs_dir, $destination_wp_fs_dir) { /** @var WP_Filesystem_Base $wp_filesystem */ global $wp_filesystem; $wp_error_id = 'fw_extensions_merge'; $source_files = $wp_filesystem->dirlist($source_wp_fs_dir); if ($source_files === false) { return new WP_Error($wp_error_id, sprintf(__('Cannot read directory "%s".', 'fw'), $source_wp_fs_dir)); } if (empty($source_files)) { // directory is empty, nothing to move return; } /** * Prepare destination directory * Remove everything except the extensions/ directory */ if ($wp_filesystem->exists($destination_wp_fs_dir)) { $destination_files = $wp_filesystem->dirlist($destination_wp_fs_dir); if ($destination_files === false) { return new WP_Error($wp_error_id, sprintf(__('Cannot read directory "%s".', 'fw'), $destination_wp_fs_dir)); } if (!empty($destination_files)) { // the directory contains some files, delete everything foreach ($destination_files as $file) { if ($file['name'] === 'extensions' && $file['type'] === 'd') { // do not touch the extensions/ directory continue; } if (!$wp_filesystem->delete($destination_wp_fs_dir . '/' . $file['name'], true, $file['type'])) { return new WP_Error($wp_error_id, sprintf(__('Cannot delete "%s".', 'fw'), $destination_wp_fs_dir . '/' . $file['name'])); } } unset($destination_files); } } else { if (!FW_WP_Filesystem::mkdir_recursive($destination_wp_fs_dir)) { return new WP_Error($wp_error_id, sprintf(__('Cannot create the "%s" directory.', 'fw'), $destination_wp_fs_dir)); } } $has_sub_extensions = false; foreach ($source_files as $file) { if ($file['name'] === 'extensions' && $file['type'] === 'd') { // do not touch the extensions/ directory $has_sub_extensions = true; continue; } if (!$wp_filesystem->move($source_wp_fs_dir . '/' . $file['name'], $destination_wp_fs_dir . '/' . $file['name'])) { return new WP_Error($wp_error_id, sprintf(__('Cannot move "%s" to "%s".', 'fw'), $source_wp_fs_dir . '/' . $file['name'], $destination_wp_fs_dir . '/' . $file['name'])); } } unset($source_files); if (!$has_sub_extensions) { return; } $sub_extensions = $wp_filesystem->dirlist($source_wp_fs_dir . '/extensions'); if ($sub_extensions === false) { return new WP_Error($wp_error_id, sprintf(__('Cannot read directory "%s".', 'fw'), $source_wp_fs_dir . '/extensions')); } if (empty($sub_extensions)) { // directory is empty, nothing to remove return; } foreach ($sub_extensions as $file) { if ($file['type'] !== 'd') { // wrong, only directories must exist in the extensions/ directory continue; } $merge_result = $this->merge_extension($source_wp_fs_dir . '/extensions/' . $file['name'], $destination_wp_fs_dir . '/extensions/' . $file['name']); if (is_wp_error($merge_result)) { return $merge_result; } } }
/** * @internal */ public function _action_update_extensions() { $nonce_name = '_nonce_fw_ext_update_extensions'; if (!isset($_POST[$nonce_name]) || !wp_verify_nonce($_POST[$nonce_name])) { wp_die(__('Invalid nonce.', 'fw')); } $form_input_name = 'extensions'; $extensions_list = FW_Request::POST($form_input_name); if (empty($extensions_list)) { FW_Flash_Messages::add('fw_ext_update', __('Please check the extensions you want to update.', 'fw'), 'warning'); wp_redirect(self_admin_url('update-core.php')); exit; } if (is_string($extensions_list)) { $extensions_list = json_decode($extensions_list); } else { $extensions_list = array_keys($extensions_list); } if (!class_exists('_FW_Ext_Update_Extensions_Upgrader_Skin')) { fw_include_file_isolated($this->get_declared_path('/includes/classes/class--fw-ext-update-extensions-upgrader-skin.php')); } $skin = new _FW_Ext_Update_Extensions_Upgrader_Skin(array('title' => __('Extensions Update', 'fw'))); require_once ABSPATH . 'wp-admin/admin-header.php'; $skin->header(); do { $original_post_value = $_POST[$form_input_name]; $_POST[$form_input_name] = wp_slash(json_encode($extensions_list)); if (!FW_WP_Filesystem::request_access(fw_get_framework_directory('/extensions'), fw_current_url(), array($nonce_name, $form_input_name))) { // revert hack changes $_POST[$form_input_name] = $original_post_value; unset($original_post_value); break; } // revert hack changes $_POST[$form_input_name] = $original_post_value; unset($original_post_value); $updates = $this->get_extensions_with_updates(); if (empty($updates)) { $skin->error(__('No extensions updates found.', 'fw')); break; } foreach ($extensions_list as $extension_name) { if (!($extension = fw()->extensions->get($extension_name))) { $skin->error(sprintf(__('Extension "%s" does not exist or is disabled.', 'fw'), $extension_name)); continue; } if (!isset($updates[$extension_name])) { $skin->error(sprintf(__('No update found for the "%s" extension.', 'fw'), $extension->manifest->get_name())); continue; } $update = $updates[$extension_name]; if (is_wp_error($update)) { $skin->error($update); continue; } /** @var FW_Ext_Update_Service $service */ $service = $this->get_child($update['service']); $update_result = $this->update(array('wp_fs_destination_dir' => FW_WP_Filesystem::real_path_to_filesystem_path($extension->get_declared_path()), 'download_callback' => array($service, '_download_extension'), 'download_callback_args' => array($extension, $update['latest_version'], $this->get_wp_fs_tmp_dir()), 'skin' => $skin, 'title' => sprintf(__('%s extension', 'fw'), $extension->manifest->get_name())), true); if (is_wp_error($update_result)) { $skin->error($update_result); continue; } $skin->set_result(true); if (!$this->get_config('extensions_as_one_update')) { $skin->decrement_extension_update_count($extension_name); } } if ($this->get_config('extensions_as_one_update')) { $skin->decrement_extension_update_count($extension_name); } $skin->after(); } while (false); $skin->footer(); require_once ABSPATH . 'wp-admin/admin-footer.php'; }
/** * Copy Theme Available Extensions from tmp directory to theme * Used after theme update * @since 2.6.0 * @return null|WP_Error */ public function theme_available_extensions_restore() { /** @var WP_Filesystem_Base $wp_filesystem */ 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 (!$wp_filesystem->exists($wpfs_tmp_dir = FW_WP_Filesystem::real_path_to_filesystem_path($this->get_tmp_dir('/theme-ext')))) { return new WP_Error('no_tmp_dir', sprintf(__('Temporary directory does not exist: %s', 'fw'), $wpfs_tmp_dir)); } /** * Fixes the case when the theme path before update was * wp-content/themes/theme-name/theme-name-parent * but after update it became * wp-content/themes/theme-name-parent * * and at this point get_template_directory() returns old theme directory * so fw_get_template_customizations_directory() also returns old path */ $theme_dir = wp_get_theme()->get_theme_root() . '/' . wp_get_theme()->get_template(); if (!($wpfs_base_dir = FW_WP_Filesystem::real_path_to_filesystem_path($base_dir = $theme_dir . fw_get_framework_customizations_dir_rel_path('/extensions')))) { return new WP_Error('base_dir_to_wpfs_fail', sprintf(__('Cannot obtain WP Filesystem dir for %s', 'fw'), $base_dir)); } if (!($dirlist = $wp_filesystem->dirlist($wpfs_tmp_dir))) { return; } foreach ($dirlist as $filename => $fileinfo) { if ('d' !== $fileinfo['type']) { continue; } if (is_wp_error($merge_result = $this->merge_extension($wpfs_tmp_dir . '/' . $filename, $wpfs_base_dir . '/' . $filename))) { return $merge_result; } } $wp_filesystem->rmdir($wpfs_tmp_dir, true); }
/** * @internal */ public function _render_page() { echo '<div class="wrap">'; $this->render_view('page', array('archives_html' => $this->render_view('archives', array('archives' => $this->get_archives(), 'is_busy' => (bool) $this->tasks()->get_active_task_collection()))), false); echo '</div>'; echo '<div id="fw-ext-backups-filesystem-form" style="display:none;">'; FW_WP_Filesystem::request_access(ABSPATH); echo '</div>'; }
/** * {@inheritdoc} * @internal */ public function _download_framework($version, $wp_filesystem_download_directory) { /** @var WP_Filesystem_Base $wp_filesystem */ global $wp_filesystem; $user_slash_repo = fw()->manifest->get($this->manifest_key); $http = new WP_Http(); $response = $http->get('https://api.github.com/repos/' . $user_slash_repo . '/releases'); unset($http); if (wp_remote_retrieve_response_code($response) !== 200) { return new WP_Error('fw_ext_update_github_framework_download_releases_failed', __('Failed to access Github repository releases.', 'fw')); } $releases = json_decode($response['body'], true); unset($response); if (empty($releases)) { return new WP_Error('fw_ext_update_github_framework_download_no_releases', __('Github repository has no releases.', 'fw')); } $release = false; foreach ($releases as $_release) { if ($_release['tag_name'] === $version) { $release = $_release; } } if (empty($release)) { return new WP_Error('fw_ext_update_github_framework_download_not_existing_release', sprintf(__('Requested version (release) for download does not exists "%s".', 'fw'), $version)); } $http = new WP_Http(); $response = $http->request($release['zipball_url'], array('timeout' => $this->download_timeout)); unset($http); if (wp_remote_retrieve_response_code($response) !== 200) { return new WP_Error('fw_ext_update_github_framework_download_failed', __('Failed to download framework zip.', 'fw')); } $zip_path = $wp_filesystem_download_directory . '/temp.zip'; // save zip to file $wp_filesystem->put_contents($zip_path, $response['body']); unset($response); unzip_file(FW_WP_Filesystem::filesystem_path_to_real_path($zip_path), $wp_filesystem_download_directory); // remove zip file $wp_filesystem->delete($zip_path, false, 'f'); $unzipped_dir = $wp_filesystem->dirlist($wp_filesystem_download_directory); $unzipped_dir = $wp_filesystem_download_directory . '/' . key($unzipped_dir); return $unzipped_dir; }
/** * @param string $user_slash_repo Github 'user/repo' * @param string $version Requested version to download * @param string $wp_filesystem_download_directory Allocated temporary empty directory * @param string $title Used in messages * * @return string|WP_Error Path to the downloaded directory */ private function download($user_slash_repo, $version, $wp_filesystem_download_directory, $title) { $http = new WP_Http(); $response = $http->get($this->get_github_api_url('/repos/' . $user_slash_repo . '/releases/tags/' . $version)); unset($http); $response_code = intval(wp_remote_retrieve_response_code($response)); if ($response_code !== 200) { if ($response_code === 403) { $json_response = json_decode($response['body'], true); if ($json_response) { return new WP_Error('fw_ext_update_github_download_releases_failed', __('Github error:', 'fw') . ' ' . $json_response['message']); } } if ($response_code) { return new WP_Error('fw_ext_update_github_download_releases_failed', sprintf(__('Failed to access Github repository "%s" releases. (Response code: %d)', 'fw'), $user_slash_repo, $response_code)); } else { return new WP_Error('fw_ext_update_github_download_releases_failed', sprintf(__('Failed to access Github repository "%s" releases.', 'fw'), $user_slash_repo)); } } $release = json_decode($response['body'], true); unset($response); if (empty($release)) { return new WP_Error('fw_ext_update_github_download_no_release', sprintf(__('%s github repository "%s" does not have the "%s" release.', 'fw'), $title, $user_slash_repo, $version)); } $http = new WP_Http(); $response = $http->request('https://github.com/' . $user_slash_repo . '/archive/' . $release['tag_name'] . '.zip', array('timeout' => $this->download_timeout)); unset($http); if (intval(wp_remote_retrieve_response_code($response)) !== 200) { return new WP_Error('fw_ext_update_github_download_failed', sprintf(__('Cannot download %s zip.', 'fw'), $title)); } /** @var WP_Filesystem_Base $wp_filesystem */ global $wp_filesystem; $zip_path = $wp_filesystem_download_directory . '/temp.zip'; // save zip to file if (!$wp_filesystem->put_contents($zip_path, $response['body'])) { return new WP_Error('fw_ext_update_github_save_download_failed', sprintf(__('Cannot save %s zip.', 'fw'), $title)); } unset($response); $unzip_result = unzip_file(FW_WP_Filesystem::filesystem_path_to_real_path($zip_path), $wp_filesystem_download_directory); if (is_wp_error($unzip_result)) { return $unzip_result; } // remove zip file if (!$wp_filesystem->delete($zip_path, false, 'f')) { return new WP_Error('fw_ext_update_github_remove_downloaded_zip_failed', sprintf(__('Cannot remove %s zip.', 'fw'), $title)); } $unzipped_dir_files = $wp_filesystem->dirlist($wp_filesystem_download_directory); if (!$unzipped_dir_files) { return new WP_Error('fw_ext_update_github_unzipped_dir_fail', __('Cannot access the unzipped directory files.', 'fw')); } /** * get first found directory * (if everything worked well, there should be only one directory) */ foreach ($unzipped_dir_files as $file) { if ($file['type'] == 'd') { return $wp_filesystem_download_directory . '/' . $file['name']; } } return new WP_Error('fw_ext_update_github_unzipped_dir_not_found', sprintf(__('The unzipped %s directory not found.', 'fw'), $title)); }