| [0] | 1 | <?php |
|---|
| [3731] | 2 | /** |
|---|
| 3 | * @brief importExport, a plugin for Dotclear 2 |
|---|
| 4 | * |
|---|
| 5 | * @package Dotclear |
|---|
| 6 | * @subpackage Plugins |
|---|
| 7 | * |
|---|
| 8 | * @copyright Olivier Meunier & Association Dotclear |
|---|
| 9 | * @copyright GPL-2.0-only |
|---|
| 10 | */ |
|---|
| 11 | |
|---|
| [3699] | 12 | if (!defined('DC_RC_PATH')) {return;} |
|---|
| [0] | 13 | |
|---|
| 14 | class dcImportFlat extends dcIeModule |
|---|
| 15 | { |
|---|
| [3699] | 16 | protected $status = false; |
|---|
| [2485] | 17 | |
|---|
| [3699] | 18 | public function setInfo() |
|---|
| 19 | { |
|---|
| 20 | $this->type = 'import'; |
|---|
| 21 | $this->name = __('Flat file import'); |
|---|
| 22 | $this->description = __('Imports a blog or a full Dotclear installation from flat file.'); |
|---|
| 23 | } |
|---|
| [2485] | 24 | |
|---|
| [3699] | 25 | public function process($do) |
|---|
| 26 | { |
|---|
| 27 | if ($do == 'single' || $do == 'full') { |
|---|
| 28 | $this->status = $do; |
|---|
| 29 | return; |
|---|
| 30 | } |
|---|
| [2485] | 31 | |
|---|
| [3699] | 32 | $to_unlink = false; |
|---|
| [2485] | 33 | |
|---|
| [3699] | 34 | # Single blog import |
|---|
| 35 | $files = $this->getPublicFiles(); |
|---|
| 36 | $single_upl = null; |
|---|
| 37 | if (!empty($_POST['public_single_file']) && in_array($_POST['public_single_file'], $files)) { |
|---|
| 38 | $single_upl = false; |
|---|
| 39 | } elseif (!empty($_FILES['up_single_file'])) { |
|---|
| 40 | $single_upl = true; |
|---|
| 41 | } |
|---|
| [2485] | 42 | |
|---|
| [3699] | 43 | if ($single_upl !== null) { |
|---|
| 44 | if ($single_upl) { |
|---|
| 45 | files::uploadStatus($_FILES['up_single_file']); |
|---|
| 46 | $file = DC_TPL_CACHE . '/' . md5(uniqid()); |
|---|
| 47 | if (!move_uploaded_file($_FILES['up_single_file']['tmp_name'], $file)) { |
|---|
| 48 | throw new Exception(__('Unable to move uploaded file.')); |
|---|
| 49 | } |
|---|
| 50 | $to_unlink = true; |
|---|
| 51 | } else { |
|---|
| 52 | $file = $_POST['public_single_file']; |
|---|
| 53 | } |
|---|
| [2485] | 54 | |
|---|
| [3699] | 55 | try { |
|---|
| 56 | # Try to unzip file |
|---|
| 57 | $unzip_file = $this->unzip($file); |
|---|
| 58 | if (false !== $unzip_file) { |
|---|
| 59 | $bk = new flatImport($this->core, $unzip_file); |
|---|
| 60 | } |
|---|
| 61 | # Else this is a normal file |
|---|
| 62 | else { |
|---|
| 63 | $bk = new flatImport($this->core, $file); |
|---|
| 64 | } |
|---|
| [2485] | 65 | |
|---|
| [3699] | 66 | $bk->importSingle(); |
|---|
| 67 | } catch (Exception $e) { |
|---|
| 68 | @unlink($unzip_file); |
|---|
| 69 | if ($to_unlink) { |
|---|
| 70 | @unlink($file); |
|---|
| 71 | } |
|---|
| 72 | throw $e; |
|---|
| 73 | } |
|---|
| 74 | @unlink($unzip_file); |
|---|
| 75 | if ($to_unlink) { |
|---|
| 76 | @unlink($file); |
|---|
| 77 | } |
|---|
| 78 | http::redirect($this->getURL() . '&do=single'); |
|---|
| 79 | } |
|---|
| [2485] | 80 | |
|---|
| [3699] | 81 | # Full import |
|---|
| 82 | $full_upl = null; |
|---|
| 83 | if (!empty($_POST['public_full_file']) && in_array($_POST['public_full_file'], $files)) { |
|---|
| 84 | $full_upl = false; |
|---|
| 85 | } elseif (!empty($_FILES['up_full_file'])) { |
|---|
| 86 | $full_upl = true; |
|---|
| 87 | } |
|---|
| [2485] | 88 | |
|---|
| [3699] | 89 | if ($full_upl !== null && $this->core->auth->isSuperAdmin()) { |
|---|
| 90 | if (empty($_POST['your_pwd']) || !$this->core->auth->checkPassword($_POST['your_pwd'])) { |
|---|
| 91 | throw new Exception(__('Password verification failed')); |
|---|
| 92 | } |
|---|
| [2485] | 93 | |
|---|
| [3699] | 94 | if ($full_upl) { |
|---|
| 95 | files::uploadStatus($_FILES['up_full_file']); |
|---|
| 96 | $file = DC_TPL_CACHE . '/' . md5(uniqid()); |
|---|
| 97 | if (!move_uploaded_file($_FILES['up_full_file']['tmp_name'], $file)) { |
|---|
| 98 | throw new Exception(__('Unable to move uploaded file.')); |
|---|
| 99 | } |
|---|
| 100 | $to_unlink = true; |
|---|
| 101 | } else { |
|---|
| 102 | $file = $_POST['public_full_file']; |
|---|
| 103 | } |
|---|
| [2485] | 104 | |
|---|
| [3699] | 105 | try { |
|---|
| 106 | # Try to unzip file |
|---|
| 107 | $unzip_file = $this->unzip($file); |
|---|
| 108 | if (false !== $unzip_file) { |
|---|
| 109 | $bk = new flatImport($this->core, $unzip_file); |
|---|
| 110 | } |
|---|
| 111 | # Else this is a normal file |
|---|
| 112 | else { |
|---|
| 113 | $bk = new flatImport($this->core, $file); |
|---|
| 114 | } |
|---|
| [2485] | 115 | |
|---|
| [3699] | 116 | $bk->importFull(); |
|---|
| 117 | } catch (Exception $e) { |
|---|
| 118 | @unlink($unzip_file); |
|---|
| 119 | if ($to_unlink) { |
|---|
| 120 | @unlink($file); |
|---|
| 121 | } |
|---|
| 122 | throw $e; |
|---|
| 123 | } |
|---|
| 124 | @unlink($unzip_file); |
|---|
| 125 | if ($to_unlink) { |
|---|
| 126 | @unlink($file); |
|---|
| 127 | } |
|---|
| 128 | http::redirect($this->getURL() . '&do=full'); |
|---|
| 129 | } |
|---|
| [2485] | 130 | |
|---|
| [3699] | 131 | header('content-type:text/plain'); |
|---|
| 132 | var_dump($_POST); |
|---|
| 133 | exit; |
|---|
| [2485] | 134 | |
|---|
| [3699] | 135 | $this->status = true; |
|---|
| 136 | } |
|---|
| [2485] | 137 | |
|---|
| [3699] | 138 | public function gui() |
|---|
| 139 | { |
|---|
| 140 | if ($this->status == 'single') { |
|---|
| 141 | dcPage::success(__('Single blog successfully imported.')); |
|---|
| 142 | return; |
|---|
| 143 | } |
|---|
| 144 | if ($this->status == 'full') { |
|---|
| 145 | dcPage::success(__('Content successfully imported.')); |
|---|
| 146 | return; |
|---|
| 147 | } |
|---|
| [2485] | 148 | |
|---|
| [3874] | 149 | $public_files = array_merge(['-' => ''], $this->getPublicFiles()); |
|---|
| [3699] | 150 | $has_files = (boolean) (count($public_files) - 1); |
|---|
| [2485] | 151 | |
|---|
| [3699] | 152 | echo |
|---|
| [3993] | 153 | dcPage::jsJson('ie_import_flat_msg', |
|---|
| 154 | ['confirm_full_import' => __('Are you sure you want to import a full backup file?')]) . |
|---|
| 155 | dcPage::jsLoad(dcPage::getPF('importExport/js/import_flat.js')); |
|---|
| [3699] | 156 | echo |
|---|
| 157 | '<form action="' . $this->getURL(true) . '" method="post" enctype="multipart/form-data" class="fieldset">' . |
|---|
| 158 | '<h3>' . __('Single blog') . '</h3>' . |
|---|
| 159 | '<p>' . sprintf(__('This will import a single blog backup as new content in the current blog: <strong>%s</strong>.'), html::escapeHTML($this->core->blog->name)) . '</p>' . |
|---|
| [2485] | 160 | |
|---|
| [3699] | 161 | '<p><label for="up_single_file">' . __('Upload a backup file') . |
|---|
| 162 | ' (' . sprintf(__('maximum size %s'), files::size(DC_MAX_UPLOAD_SIZE)) . ')' . ' </label>' . |
|---|
| 163 | ' <input type="file" id="up_single_file" name="up_single_file" size="20" />' . |
|---|
| 164 | '</p>'; |
|---|
| [2485] | 165 | |
|---|
| [3699] | 166 | if ($has_files) { |
|---|
| 167 | echo |
|---|
| 168 | '<p><label for="public_single_file" class="">' . __('or pick up a local file in your public directory') . ' </label> ' . |
|---|
| 169 | form::combo('public_single_file', $public_files) . |
|---|
| 170 | '</p>'; |
|---|
| 171 | } |
|---|
| [2485] | 172 | |
|---|
| [3699] | 173 | echo |
|---|
| 174 | '<p>' . |
|---|
| 175 | $this->core->formNonce() . |
|---|
| [3874] | 176 | form::hidden(['do'], 1) . |
|---|
| 177 | form::hidden(['MAX_FILE_SIZE'], DC_MAX_UPLOAD_SIZE) . |
|---|
| [3699] | 178 | '<input type="submit" value="' . __('Import') . '" /></p>' . |
|---|
| [2485] | 179 | |
|---|
| [3699] | 180 | '</form>'; |
|---|
| [2485] | 181 | |
|---|
| [3699] | 182 | if ($this->core->auth->isSuperAdmin()) { |
|---|
| 183 | echo |
|---|
| 184 | '<form action="' . $this->getURL(true) . '" method="post" enctype="multipart/form-data" id="formfull" class="fieldset">' . |
|---|
| 185 | '<h3>' . __('Multiple blogs') . '</h3>' . |
|---|
| 186 | '<p class="warning">' . __('This will reset all the content of your database, except users.') . '</p>' . |
|---|
| [2485] | 187 | |
|---|
| [3699] | 188 | '<p><label for="up_full_file">' . __('Upload a backup file') . ' ' . |
|---|
| 189 | ' (' . sprintf(__('maximum size %s'), files::size(DC_MAX_UPLOAD_SIZE)) . ')' . ' </label>' . |
|---|
| 190 | '<input type="file" id="up_full_file" name="up_full_file" size="20" />' . |
|---|
| 191 | '</p>'; |
|---|
| [2485] | 192 | |
|---|
| [3699] | 193 | if ($has_files) { |
|---|
| 194 | echo |
|---|
| 195 | '<p><label for="public_full_file">' . __('or pick up a local file in your public directory') . ' </label>' . |
|---|
| 196 | form::combo('public_full_file', $public_files) . |
|---|
| 197 | '</p>'; |
|---|
| 198 | } |
|---|
| [2485] | 199 | |
|---|
| [3699] | 200 | echo |
|---|
| 201 | '<p><label for="your_pwd" class="required"><abbr title="' . __('Required field') . '">*</abbr> ' . __('Your password:') . '</label>' . |
|---|
| 202 | form::password('your_pwd', 20, 255, |
|---|
| [3874] | 203 | [ |
|---|
| [3699] | 204 | 'extra_html' => 'required placeholder="' . __('Password') . '"', |
|---|
| 205 | 'autocomplete' => 'current-password' |
|---|
| [3874] | 206 | ] |
|---|
| [3699] | 207 | ) . '</p>' . |
|---|
| [2485] | 208 | |
|---|
| [3699] | 209 | '<p>' . |
|---|
| 210 | $this->core->formNonce() . |
|---|
| [3874] | 211 | form::hidden(['do'], 1) . |
|---|
| 212 | form::hidden(['MAX_FILE_SIZE'], DC_MAX_UPLOAD_SIZE) . |
|---|
| [3699] | 213 | '<input type="submit" value="' . __('Import') . '" /></p>' . |
|---|
| [2485] | 214 | |
|---|
| [3699] | 215 | '</form>'; |
|---|
| 216 | } |
|---|
| 217 | } |
|---|
| [2485] | 218 | |
|---|
| [3699] | 219 | protected function getPublicFiles() |
|---|
| 220 | { |
|---|
| [3874] | 221 | $public_files = []; |
|---|
| [3699] | 222 | $dir = @dir($this->core->blog->public_path); |
|---|
| 223 | if ($dir) { |
|---|
| 224 | while (($entry = $dir->read()) !== false) { |
|---|
| 225 | $entry_path = $dir->path . '/' . $entry; |
|---|
| [2485] | 226 | |
|---|
| [3699] | 227 | if (is_file($entry_path) && is_readable($entry_path)) { |
|---|
| 228 | # Do not test each zip file content here, its too long |
|---|
| 229 | if (substr($entry_path, -4) == '.zip') { |
|---|
| 230 | $public_files[$entry] = $entry_path; |
|---|
| 231 | } elseif (self::checkFileContent($entry_path)) { |
|---|
| 232 | $public_files[$entry] = $entry_path; |
|---|
| 233 | } |
|---|
| 234 | } |
|---|
| 235 | } |
|---|
| 236 | } |
|---|
| 237 | return $public_files; |
|---|
| 238 | } |
|---|
| [2485] | 239 | |
|---|
| [3699] | 240 | protected static function checkFileContent($entry_path) |
|---|
| 241 | { |
|---|
| 242 | $ret = false; |
|---|
| [2485] | 243 | |
|---|
| [3699] | 244 | $fp = fopen($entry_path, 'rb'); |
|---|
| 245 | $ret = strpos(fgets($fp), '///DOTCLEAR|') === 0; |
|---|
| 246 | fclose($fp); |
|---|
| [2485] | 247 | |
|---|
| [3699] | 248 | return $ret; |
|---|
| 249 | } |
|---|
| [2485] | 250 | |
|---|
| [3699] | 251 | private function unzip($file) |
|---|
| 252 | { |
|---|
| 253 | $zip = new fileUnzip($file); |
|---|
| [2485] | 254 | |
|---|
| [3699] | 255 | if ($zip->isEmpty()) { |
|---|
| 256 | $zip->close(); |
|---|
| 257 | return false; //throw new Exception(__('File is empty or not a compressed file.')); |
|---|
| 258 | } |
|---|
| [2485] | 259 | |
|---|
| [3699] | 260 | foreach ($zip->getFilesList() as $zip_file) { |
|---|
| 261 | # Check zipped file name |
|---|
| 262 | if (substr($zip_file, -4) != '.txt') { |
|---|
| 263 | continue; |
|---|
| 264 | } |
|---|
| [2485] | 265 | |
|---|
| [3699] | 266 | # Check zipped file contents |
|---|
| 267 | $content = $zip->unzip($zip_file); |
|---|
| 268 | if (strpos($content, '///DOTCLEAR|') !== 0) { |
|---|
| 269 | unset($content); |
|---|
| 270 | continue; |
|---|
| 271 | } |
|---|
| [2485] | 272 | |
|---|
| [3699] | 273 | $target = path::fullFromRoot($zip_file, dirname($file)); |
|---|
| [2485] | 274 | |
|---|
| [3699] | 275 | # Check existing files with same name |
|---|
| 276 | if (file_exists($target)) { |
|---|
| 277 | $zip->close(); |
|---|
| 278 | unset($content); |
|---|
| 279 | throw new Exception(__('Another file with same name exists.')); |
|---|
| 280 | } |
|---|
| [2485] | 281 | |
|---|
| [3699] | 282 | # Extract backup content |
|---|
| 283 | if (file_put_contents($target, $content) === false) { |
|---|
| 284 | $zip->close(); |
|---|
| 285 | unset($content); |
|---|
| 286 | throw new Exception(__('Failed to extract backup file.')); |
|---|
| 287 | } |
|---|
| [2485] | 288 | |
|---|
| [3699] | 289 | $zip->close(); |
|---|
| 290 | unset($content); |
|---|
| [2485] | 291 | |
|---|
| [3699] | 292 | # Return extracted file name |
|---|
| 293 | return $target; |
|---|
| 294 | } |
|---|
| [2485] | 295 | |
|---|
| [3699] | 296 | $zip->close(); |
|---|
| 297 | throw new Exception(__('No backup in compressed file.')); |
|---|
| 298 | } |
|---|
| [0] | 299 | } |
|---|