/** * Attempt to backup each of the update manifest files by copying them to a file with the same name with a .bak * extension. If there is an exception thrown, we attempt to roll back all of the changes. * * @param string $unzipFolder * @param string $handle * * @return bool */ private function _backupFiles($unzipFolder, $handle) { $manifestData = UpdateHelper::getManifestData($unzipFolder, $handle); try { foreach ($manifestData as $row) { if (UpdateHelper::isManifestVersionInfoLine($row)) { continue; } // No need to back up migration files. if (UpdateHelper::isManifestMigrationLine($row)) { continue; } $rowData = explode(';', $row); $filePath = IOHelper::normalizePathSeparators(($handle == 'craft' ? craft()->path->getAppPath() : craft()->path->getPluginsPath() . $handle . '/') . $rowData[0]); // It's a folder if (UpdateHelper::isManifestLineAFolder($filePath)) { $folderPath = UpdateHelper::cleanManifestFolderLine($filePath); if (IOHelper::folderExists($folderPath)) { Craft::log('Backing up folder ' . $folderPath, LogLevel::Info, true); IOHelper::createFolder($folderPath . '.bak'); IOHelper::copyFolder($folderPath . '/', $folderPath . '.bak/'); } } else { // If the file doesn't exist, it's probably a new file. if (IOHelper::fileExists($filePath)) { Craft::log('Backing up file ' . $filePath, LogLevel::Info, true); IOHelper::copyFile($filePath, $filePath . '.bak'); } } } } catch (\Exception $e) { Craft::log('Error updating files: ' . $e->getMessage(), LogLevel::Error); UpdateHelper::rollBackFileChanges($manifestData, $handle); return false; } return true; }
/** * @param $uid * @param bool $dbBackupPath * @return array */ public function rollbackUpdate($uid, $dbBackupPath = false) { try { craft()->config->maxPowerCaptain(); if ($dbBackupPath && craft()->config->get('backupDbOnUpdate') && craft()->config->get('restoreDbOnUpdateFailure')) { Craft::log('Rolling back any database changes.', LogLevel::Info, true); UpdateHelper::rollBackDatabaseChanges($dbBackupPath); Craft::log('Done rolling back any database changes.', LogLevel::Info, true); } // If uid !== false, it's an auto-update. if ($uid !== false) { Craft::log('Rolling back any file changes.', LogLevel::Info, true); $manifestData = UpdateHelper::getManifestData(UpdateHelper::getUnzipFolderFromUID($uid)); if ($manifestData) { UpdateHelper::rollBackFileChanges($manifestData); } Craft::log('Done rolling back any file changes.', LogLevel::Info, true); } Craft::log('Finished rolling back changes.', LogLevel::Info, true); return array('success' => true); } catch (\Exception $e) { return array('success' => false, 'message' => $e->getMessage()); } }
/** * @param $manifestData * @param $sourceTempFolder * @param $handle * * @return bool */ public static function doFileUpdate($manifestData, $sourceTempFolder, $handle) { if ($handle == 'craft') { $destDirectory = craft()->path->getAppPath(); $sourceFileDirectory = 'app/'; } else { $destDirectory = craft()->path->getPluginsPath() . $handle . '/'; $sourceFileDirectory = ''; } try { foreach ($manifestData as $row) { if (static::isManifestVersionInfoLine($row)) { continue; } $folder = false; $rowData = explode(';', $row); if (static::isManifestLineAFolder($rowData[0])) { $folder = true; $tempPath = static::cleanManifestFolderLine($rowData[0]); } else { $tempPath = $rowData[0]; } $destFile = IOHelper::normalizePathSeparators($destDirectory . $tempPath); $sourceFile = IOHelper::getRealPath(IOHelper::normalizePathSeparators($sourceTempFolder . '/' . $sourceFileDirectory . $tempPath)); switch (trim($rowData[1])) { // update the file case PatchManifestFileAction::Add: if ($folder) { Craft::log('Updating folder: ' . $destFile, LogLevel::Info, true); $tempFolder = rtrim($destFile, '/') . StringHelper::UUID() . '/'; $tempTempFolder = rtrim($destFile, '/') . '-tmp/'; IOHelper::createFolder($tempFolder); IOHelper::copyFolder($sourceFile, $tempFolder); IOHelper::rename($destFile, $tempTempFolder); IOHelper::rename($tempFolder, $destFile); IOHelper::clearFolder($tempTempFolder); IOHelper::deleteFolder($tempTempFolder); } else { Craft::log('Updating file: ' . $destFile, LogLevel::Info, true); IOHelper::copyFile($sourceFile, $destFile); } break; } } } catch (\Exception $e) { Craft::log('Error updating files: ' . $e->getMessage(), LogLevel::Error); UpdateHelper::rollBackFileChanges($manifestData, $handle); return false; } return true; }
/** * @param string $uid * @param string $handle * @param bool $dbBackupPath * * @return array */ public function rollbackUpdate($uid, $handle, $dbBackupPath = false) { try { // Fire an 'onEndUpdate' event and pass in that the update failed. $this->onEndUpdate(new Event($this, array('success' => false))); craft()->config->maxPowerCaptain(); if ($dbBackupPath && craft()->config->get('backupDbOnUpdate') && craft()->config->get('restoreDbOnUpdateFailure')) { Craft::log('Rolling back any database changes.', LogLevel::Info, true); UpdateHelper::rollBackDatabaseChanges($dbBackupPath); Craft::log('Done rolling back any database changes.', LogLevel::Info, true); } // If uid !== false, it's an auto-update. if ($uid !== false) { Craft::log('Rolling back any file changes.', LogLevel::Info, true); $manifestData = UpdateHelper::getManifestData(UpdateHelper::getUnzipFolderFromUID($uid), $handle); if ($manifestData) { UpdateHelper::rollBackFileChanges($manifestData, $handle); } Craft::log('Done rolling back any file changes.', LogLevel::Info, true); } Craft::log('Finished rolling back changes.', LogLevel::Info, true); Craft::log('Taking the site out of maintenance mode.', LogLevel::Info, true); craft()->disableMaintenanceMode(); return array('success' => true); } catch (\Exception $e) { return array('success' => false, 'message' => $e->getMessage()); } }