/** * Recursively synchronize the file system * @param string * @param integer */ protected function execSync($strPath, $intPid = 0) { if (!$this->blnIsDbAssisted) { return; } $arrFiles = array(); $arrFolders = array(); $arrScan = scan(TL_ROOT . '/' . $strPath); // Separate files from folders foreach ($arrScan as $strFile) { if ($strFile == '.svn' || $strFile == '.DS_Store') { continue; } if (is_dir(TL_ROOT . '/' . $strPath . '/' . $strFile)) { $arrFolders[] = $strPath . '/' . $strFile; } else { $arrFiles[] = $strPath . '/' . $strFile; } } // Folders foreach ($arrFolders as $strFolder) { $objFolder = new \Folder($strFolder); $objModel = \FilesModel::findByPath($strFolder); // Create the entry if it does not yet exist if ($objModel === null) { $objModel = new \FilesModel(); $objModel->pid = $intPid; $objModel->tstamp = time(); $objModel->name = basename($strFolder); $objModel->type = 'folder'; $objModel->path = $strFolder; $objModel->hash = $objFolder->hash; $objModel->found = 1; $objModel->save(); $this->arrMessages[] = '<p class="tl_new">' . sprintf($GLOBALS['TL_LANG']['tl_files']['syncFolderC'], $strFolder) . '</p>'; } else { // Update the hash if the folder has changed if ($objModel->hash != $objFolder->hash) { $objModel->hash = $objFolder->hash; $this->arrMessages[] = '<p class="tl_info">' . sprintf($GLOBALS['TL_LANG']['tl_files']['syncHash'], $strFolder) . '</p>'; } $objModel->found = 1; $objModel->save(); $this->arrMessages[] = '<p class="tl_confirm">' . sprintf($GLOBALS['TL_LANG']['tl_files']['syncFolderF'], $strFolder) . '</p>'; } $this->execSync($strFolder, $objModel->id); } // Files foreach ($arrFiles as $strFile) { $objFile = new \File($strFile); $objModel = \FilesModel::findByPath($strFile); // Create the entry if it does not yet exist if ($objModel === null) { $objModel = new \FilesModel(); $objModel->pid = $intPid; $objModel->tstamp = time(); $objModel->name = basename($strFile); $objModel->type = 'file'; $objModel->path = $strFile; $objModel->extension = $objFile->extension; $objModel->hash = $objFile->hash; $objModel->found = 1; $objModel->save(); $this->arrMessages[] = '<p class="tl_new">' . sprintf($GLOBALS['TL_LANG']['tl_files']['syncFileC'], $strFile) . '</p>'; } else { // Update the hash if the file has changed if ($objModel->hash != $objFile->hash) { $objModel->hash = $objFile->hash; $this->arrMessages[] = '<p class="tl_info">' . sprintf($GLOBALS['TL_LANG']['tl_files']['syncHash'], $strFile) . '</p>'; } $objModel->found = 1; $objModel->save(); $this->arrMessages[] = '<p class="tl_confirm">' . sprintf($GLOBALS['TL_LANG']['tl_files']['syncFileF'], $strFile) . '</p>'; } } }
/** * Synchronize the file system with the database * * @return string The path to the synchronization log file * * @throws \Exception If a parent ID entry is missing */ public static function syncFiles() { // Try to raise the limits (see #7035) @ini_set('memory_limit', -1); @ini_set('max_execution_time', 0); $objDatabase = \Database::getInstance(); // Lock the files table $objDatabase->lockTables(array('tl_files')); // Reset the "found" flag $objDatabase->query("UPDATE tl_files SET found=''"); // Get a filtered list of all files $objFiles = new \RecursiveIteratorIterator(new \Dbafs\Filter(new \RecursiveDirectoryIterator(TL_ROOT . '/' . \Config::get('uploadPath'), \FilesystemIterator::UNIX_PATHS | \FilesystemIterator::FOLLOW_SYMLINKS | \FilesystemIterator::SKIP_DOTS)), \RecursiveIteratorIterator::SELF_FIRST); $strLog = 'system/tmp/' . md5(uniqid(mt_rand(), true)); // Open the log file $objLog = new \File($strLog, true); $objLog->truncate(); $arrModels = array(); // Create or update the database entries foreach ($objFiles as $objFile) { $strRelpath = str_replace(TL_ROOT . '/', '', $objFile->getPathname()); // Get all subfiles in a single query if ($objFile->isDir()) { $objSubfiles = \FilesModel::findMultipleFilesByFolder($strRelpath); if ($objSubfiles !== null) { while ($objSubfiles->next()) { $arrModels[$objSubfiles->path] = $objSubfiles->current(); } } } // Get the model if (isset($arrModels[$strRelpath])) { $objModel = $arrModels[$strRelpath]; } else { $objModel = \FilesModel::findByPath($strRelpath); } if ($objModel === null) { // Add a log entry $objLog->append("[Added] {$strRelpath}"); // Get the parent folder $strParent = dirname($strRelpath); // Get the parent ID if ($strParent == \Config::get('uploadPath')) { $strPid = null; } else { $objParent = \FilesModel::findByPath($strParent); if ($objParent === null) { throw new \Exception("No parent entry for {$strParent}"); } $strPid = $objParent->uuid; } // Create the file or folder if (is_file(TL_ROOT . '/' . $strRelpath)) { $objFile = new \File($strRelpath, true); $objModel = new \FilesModel(); $objModel->pid = $strPid; $objModel->tstamp = time(); $objModel->name = $objFile->name; $objModel->type = 'file'; $objModel->path = $objFile->path; $objModel->extension = $objFile->extension; $objModel->found = 2; $objModel->hash = $objFile->hash; $objModel->uuid = $objDatabase->getUuid(); $objModel->save(); } else { $objFolder = new \Folder($strRelpath); $objModel = new \FilesModel(); $objModel->pid = $strPid; $objModel->tstamp = time(); $objModel->name = $objFolder->name; $objModel->type = 'folder'; $objModel->path = $objFolder->path; $objModel->extension = ''; $objModel->found = 2; $objModel->hash = $objFolder->hash; $objModel->uuid = $objDatabase->getUuid(); $objModel->save(); } } else { // Check whether the MD5 hash has changed $objResource = $objFile->isDir() ? new \Folder($strRelpath) : new \File($strRelpath); $strType = $objModel->hash != $objResource->hash ? 'Changed' : 'Unchanged'; // Add a log entry $objLog->append("[{$strType}] {$strRelpath}"); // Update the record $objModel->found = 1; $objModel->hash = $objResource->hash; $objModel->save(); } } // Check for left-over entries in the DB $objFiles = \FilesModel::findByFound(''); if ($objFiles !== null) { $arrMapped = array(); $arrPidUpdate = array(); while ($objFiles->next()) { $objFound = \FilesModel::findBy(array('hash=?', 'found=2'), $objFiles->hash); if ($objFound !== null) { // Check for matching file names if the result is ambiguous (see #5644) if ($objFound->count() > 1) { while ($objFound->next()) { if ($objFound->name == $objFiles->name) { $objFound = $objFound->current(); break; } } } // If another file has been mapped already, delete the entry (see #6008) if (in_array($objFound->path, $arrMapped)) { $objLog->append("[Deleted] {$objFiles->path}"); $objFiles->delete(); continue; } $arrMapped[] = $objFound->path; // Store the PID change if ($objFiles->type == 'folder') { $arrPidUpdate[$objFound->uuid] = $objFiles->uuid; } // Add a log entry BEFORE changing the object $objLog->append("[Moved] {$objFiles->path} to {$objFound->path}"); // Update the original entry $objFiles->pid = $objFound->pid; $objFiles->tstamp = $objFound->tstamp; $objFiles->name = $objFound->name; $objFiles->type = $objFound->type; $objFiles->path = $objFound->path; $objFiles->found = 1; // Delete the newer (duplicate) entry $objFound->delete(); // Then save the modified original entry (prevents duplicate key errors) $objFiles->save(); } else { // Add a log entry BEFORE changing the object $objLog->append("[Deleted] {$objFiles->path}"); // Delete the entry if the resource has gone $objFiles->delete(); } } // Update the PID of the child records if (!empty($arrPidUpdate)) { foreach ($arrPidUpdate as $from => $to) { $objChildren = \FilesModel::findByPid($from); if ($objChildren !== null) { while ($objChildren->next()) { $objChildren->pid = $to; $objChildren->save(); } } } } } // Close the log file $objLog->close(); // Reset the found flag $objDatabase->query("UPDATE tl_files SET found=1 WHERE found=2"); // Unlock the tables $objDatabase->unlockTables(); // Return the path to the log file return $strLog; }
/** * Recursively synchronize the new folder * @param string * @param integer */ protected function syncNewFolder($strPath, $intPid = 0) { $arrFiles = array(); $arrFolders = array(); $arrScan = scan(TL_ROOT . '/' . $strPath); // Separate files from folders foreach ($arrScan as $strFile) { if (is_dir(TL_ROOT . '/' . $strPath . '/' . $strFile)) { $arrFolders[] = $strPath . '/' . $strFile; } else { $arrFiles[] = $strPath . '/' . $strFile; } } // Folders foreach ($arrFolders as $strFolder) { $objFolder = new \Folder($strFolder); $objModel = \FilesModel::findByPath($strFolder); // Create the entry if it does not yet exist if ($objModel === null) { $objModel = new \FilesModel(); $objModel->pid = $intPid; $objModel->tstamp = time(); $objModel->name = basename($strFolder); $objModel->type = 'folder'; $objModel->path = $strFolder; $objModel->hash = $objFolder->hash; $objModel->found = 1; $objModel->save(); } $this->syncNewFolder($strFolder, $objModel->id); } // Files foreach ($arrFiles as $strFile) { $objFile = new \File($strFile); $objModel = \FilesModel::findByPath($strFile); // Create the entry if it does not yet exist if ($objModel === null) { $objModel = new \FilesModel(); $objModel->pid = $intPid; $objModel->tstamp = time(); $objModel->name = basename($strFile); $objModel->type = 'file'; $objModel->path = $strFile; $objModel->extension = $objFile->extension; $objModel->hash = $objFile->hash; $objModel->found = 1; $objModel->save(); } } }