| @@ -35,8 +35,13 @@ | ||
| 35 | 35 | continue; |
| 36 | 36 | } |
| 37 | 37 | $entries[] = ['name' => $file->getFilename(), 'relative_path' => $file->getRelativePathname(), 'content' => $file->getContents(), 'handler' => 'internal', 'signature' => wp_create_nonce(sprintf('%s:%s', WIND_PRESS::WP_OPTION, $file->getRelativePathname())), 'readonly' => strpos(wp_normalize_path($file->getPathname()), wp_normalize_path($data_dir)) === \false, 'path_on_disk' => $file->getPathname()]; |
| 38 | 38 | } |
| 39 | + $directory_finder = new Finder(); | |
| 40 | + $directory_finder->ignoreUnreadableDirs()->in($data_dir)->directories()->followLinks(); | |
| 41 | + foreach ($directory_finder as $directory) { | |
| 42 | + $entries[] = ['name' => $directory->getBasename(), 'relative_path' => $directory->getRelativePathname(), 'content' => '', 'handler' => 'internal', 'directory' => \true, 'readonly' => \false, 'path_on_disk' => $directory->getPathname()]; | |
| 43 | + } | |
| 39 | 44 | $tailwindcss_version = \WindPress\WindPress\Core\Runtime::tailwindcss_version(); |
| 40 | 45 | // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Local file |
| 41 | 46 | $stubs_main_css = file_get_contents(sprintf('%s/stubs/tailwindcss-v%d/main.css', dirname(WIND_PRESS::FILE), $tailwindcss_version)); |
| 42 | 47 | // check if 'main.css' already exists and content is not empty, else use the stubs |
| @@ -103,8 +108,39 @@ | ||
| 103 | 108 | } |
| 104 | 109 | // skip the readonly entries |
| 105 | 110 | if (isset($entry['readonly']) && $entry['readonly']) { |
| 106 | 111 | $result['skipped'][] = ['relative_path' => $relative_path, 'reason' => 'readonly_entry', 'message' => __('Read-only entries cannot be saved.', 'windpress')]; |
| 112 | + continue; | |
| 113 | + } | |
| 114 | + if (!empty($entry['directory'])) { | |
| 115 | + if ($entry['handler'] !== 'internal') { | |
| 116 | + $result['skipped'][] = ['relative_path' => $relative_path, 'reason' => 'invalid_directory_handler', 'message' => __('Directories must use the internal handler.', 'windpress')]; | |
| 117 | + continue; | |
| 118 | + } | |
| 119 | + try { | |
| 120 | + $safe_directory_path = static::sanitize_relative_path($entry['relative_path'], $data_dir); | |
| 121 | + if (!empty($entry['hidden'])) { | |
| 122 | + if (is_dir($safe_directory_path) && !rmdir($safe_directory_path)) { | |
| 123 | + throw new \RuntimeException(__('Directory is not empty.', 'windpress')); | |
| 124 | + } | |
| 125 | + $result['deleted'][] = ['relative_path' => $relative_path]; | |
| 126 | + } else { | |
| 127 | + if (file_exists($safe_directory_path) && !is_dir($safe_directory_path)) { | |
| 128 | + throw new \RuntimeException(__('A file already exists at the directory path.', 'windpress')); | |
| 129 | + } | |
| 130 | + if (!is_dir($safe_directory_path) && !wp_mkdir_p($safe_directory_path)) { | |
| 131 | + throw new \RuntimeException(__('The directory could not be created.', 'windpress')); | |
| 132 | + } | |
| 133 | + $result['saved'][] = ['relative_path' => $relative_path]; | |
| 134 | + } | |
| 135 | + } catch (\InvalidArgumentException $th) { | |
| 136 | + $result['skipped'][] = ['relative_path' => $relative_path, 'reason' => 'invalid_path', 'message' => __('Directory path is invalid.', 'windpress')]; | |
| 137 | + } catch (\Throwable $th) { | |
| 138 | + if (\WP_DEBUG_LOG) { | |
| 139 | + error_log($th->__toString()); | |
| 140 | + } | |
| 141 | + $result['errors'][] = ['relative_path' => $relative_path, 'code' => 'filesystem_error', 'message' => $th->getMessage()]; | |
| 142 | + } | |
| 107 | 143 | continue; |
| 108 | 144 | } |
| 109 | 145 | if ($entry['handler'] !== 'internal') { |
| 110 | 146 | // the handler only accept alphanumeric, hyphens, and underscores |