* Copies files from a source folder to a destination folder, updating metadata if available. * * @param string $sourceFolder The source folder (e.g. "root" or a subfolder) * @param string $destinationFolder The destination folder. * @param array $files Array of file names to copy. * @return array Result with either "success" or "error" key. */
($sourceFolder, $destinationFolder, $files)
| 239 | * @return array Result with either "success" or "error" key. |
| 240 | */ |
| 241 | public static function copyFiles($sourceFolder, $destinationFolder, $files) |
| 242 | { |
| 243 | $errors = []; |
| 244 | $storage = self::storage(); |
| 245 | $isLocal = $storage->isLocal(); |
| 246 | |
| 247 | list($sourceDir, $err) = self::resolveFolderPath($sourceFolder, false); |
| 248 | if ($err) { |
| 249 | return ["error" => $err]; |
| 250 | } |
| 251 | list($destDir, $err) = self::resolveFolderPath($destinationFolder, true); |
| 252 | if ($err) { |
| 253 | return ["error" => $err]; |
| 254 | } |
| 255 | |
| 256 | $sourceDir .= DIRECTORY_SEPARATOR; |
| 257 | $destDir .= DIRECTORY_SEPARATOR; |
| 258 | |
| 259 | $destEncrypted = false; |
| 260 | try { |
| 261 | $destEncrypted = FolderCrypto::isEncryptedOrAncestor((string)$destinationFolder); |
| 262 | } catch (\Throwable $e) { |
| 263 | $destEncrypted = false; |
| 264 | } |
| 265 | $srcEncrypted = false; |
| 266 | try { |
| 267 | $srcEncrypted = FolderCrypto::isEncryptedOrAncestor((string)$sourceFolder); |
| 268 | } catch (\Throwable $e) { |
| 269 | $srcEncrypted = false; |
| 270 | } |
| 271 | if (!$isLocal && ($srcEncrypted || $destEncrypted)) { |
| 272 | return ["error" => "Encrypted folders are not supported for remote storage."]; |
| 273 | } |
| 274 | |
| 275 | // Metadata paths |
| 276 | $srcMetaFile = self::getMetadataFilePath($sourceFolder); |
| 277 | $destMetaFile = self::getMetadataFilePath($destinationFolder); |
| 278 | |
| 279 | $srcMetadata = file_exists($srcMetaFile) ? (json_decode(file_get_contents($srcMetaFile), true) ?: []) : []; |
| 280 | $destMetadata = file_exists($destMetaFile) ? (json_decode(file_get_contents($destMetaFile), true) ?: []) : []; |
| 281 | |
| 282 | $safeFileNamePattern = REGEX_FILE_NAME; |
| 283 | $actor = $_SESSION['username'] ?? 'Unknown'; |
| 284 | $now = date(DATE_TIME_FORMAT); |
| 285 | |
| 286 | foreach ($files as $fileName) { |
| 287 | $originalName = basename(trim($fileName)); |
| 288 | $basename = $originalName; |
| 289 | |
| 290 | if (!preg_match($safeFileNamePattern, $basename)) { |
| 291 | $errors[] = "$basename has an invalid name."; |
| 292 | continue; |
| 293 | } |
| 294 | |
| 295 | $srcPath = $sourceDir . $originalName; |
| 296 | $destPath = $destDir . $basename; |
| 297 | |
| 298 | clearstatcache(); |
no test coverage detected