. * */ namespace Vvveb\Controller\Tools; use function Vvveb\__; use function Vvveb\camelToUnderscore; use Vvveb\Controller\Base; use function Vvveb\humanReadable; use Vvveb\System\Update as UpdateSys; use Vvveb\System\User\Admin; use function Vvveb\url; class Update extends Base { private $steps = ['checkPermissions', 'download', 'unzip', 'copyInstall', /*'backup',*/ 'copySystem', /* 'copyAdmin',*/ 'copyApp', 'copyCore', 'copyConfig', 'addNewColumns', 'createNewTables', 'copyPublic', 'copyPublicAdmin', 'copyPublicMedia', 'setFilePermissions', 'cleanUp', 'clearCache', 'complete']; function __construct() { $this->update = new UpdateSys(); } private function checkUpdates($type = 'core', $force = false) { $updateInfo = []; $errorMsg = __('Error checking updates!'); try { $updateInfo = $this->update->checkUpdates($type, $force); } catch (\Exception $e) { $this->view->errors[] = $e->getMessage(); } if (! $updateInfo) { $this->view->errors[] = $errorMsg; } return $updateInfo; } function check() { $type = $this->request->get['type'] ?? 'core'; $updateInfo = $this->update->checkUpdates($type, true); if ($updateInfo) { //$this->view->success[] = __('Check succesful!'); if ($updateInfo['hasUpdate']) { $this->view->info[] = __('Updates available!') . ' ' . $updateInfo['version'] . ''; } else { $this->view->info[] = __('No updates available!'); } } return $this->index(); } function updateNext() { ignore_user_abort(true); $step = $this->request->get['step'] ?? false; $next = false; if ($step && (($position = array_search($step, $this->steps)) !== false)) { if (method_exists($this, $step) && $this->$step()) { $position++; $next = $this->steps[$position] ?? false; } else { $errorMsg = __('Failed at ') . $step; $this->view->errors[] = $errorMsg; $this->cleanUp(); } } $url = []; if (isset($this->view->errors[0])) { $url['error'] = $this->view->errors[0]; } if (isset($this->view->success[0])) { $url['success'] = $this->view->success[0]; } if (isset($this->view->info[0])) { $url['success'] = $this->view->info[0]; } if ($next) { $count = count($this->steps); $url['info'] = sprintf(__('Step (%d/%d) %s ...'), $position, $count, humanReadable(camelToUnderscore($next))); $this->view->info[] = $url['info']; $url += ['module'=>'tools/update', 'action' => 'updateNext', 'step' => $next, 'position' => $position, 'count' => $count]; if ($this->request->isAjax()) { die(json_encode($url + ['url' => url($url)])); } else { $this->redirect($url); } } else { if ($position = count($this->steps)) { $message = __('Update finished'); if ($this->request->isAjax()) { die(json_encode($url + ['url' => url($url), 'success' => $message])); } else { $this->view->success[] = $message; return $this->index(); } } } } private function download() { $updateFile = false; $errorDownloadMsg = __('Error downloading update!'); $updateInfo = $this->update->checkUpdates(); //don't load plugins during update to avoid possible conflicts Admin::session(['safemode' => true]); try { $updateFile = $this->update->download($updateInfo['download']); $this->session->set('updateFile', $updateFile); } catch (\Exception $e) { $this->view->errors[] = $e->getMessage(); } if (! $updateInfo || ! $updateFile) { $this->view->errors[] = $errorDownloadMsg; return false; } else { $this->view->info[] = $updateInfo['download'] . ' ' . __('downloaded successfully'); } return true; } function checkPermissions() { $result = false; try { $result = $this->update->checkPermissions(); } catch (\Exception $e) { $this->view->errors[] = $e->getMessage(); } if ($result === true) { $this->view->info[] = __('Permissions ok!'); } else { $this->view->errors[] = sprintf(__('%s not writable!'), $result); return false; } return $result; } function backup() { $result = false; try { $result = $this->update->backup(); } catch (\Exception $e) { $this->view->errors[] = $e->getMessage(); } if ($result) { $this->view->info[] = __('Backup successful!'); } return $result; } function addNewColumns() { $result = false; try { $result = $this->update->addNewColumns(); } catch (\Exception $e) { $this->view->errors[] = $e->getMessage(); } if ($result) { $this->view->info[] = __('Add new columns successful!'); } return $result; } function createNewTables() { $result = false; try { $result = $this->update->createNewTables(); } catch (\Exception $e) { $this->view->errors[] = $e->getMessage(); } if ($result) { $this->view->info[] = __('Create new tables successful!'); } return $result; } function unzip() { $errorInstallMsg = __('Error unzipping update!'); $result = true; try { $updateFile = $this->session->get('updateFile'); $result = $this->update->unzip($updateFile); } catch (\Exception $e) { $this->view->errors[] = $e->getMessage(); } if (! $result) { $this->view->errors[] = $errorInstallMsg; } else { $this->view->success[] = __('Unzip successful!'); } return $result; } private function copy($method) { $name = humanReadable(camelToUnderscore($method)); $errorInstallMsg = sprintf(__('Error updating %s'), $name); $result = false; $method = "copy$method"; try { $result = $this->update->$method(); } catch (\Exception $e) { $this->view->errors[] = $e->getMessage(); } if (! $result) { $this->view->errors[] = $errorInstallMsg; } else { $this->view->success[] = sprintf(__('Update %s successful!'), $name); } return $result; } function copyCore() { return $this->copy('Core'); } function copyConfig() { return $this->copy('Config'); } function copySystem() { //merge system and admin in one step so that the updater does not break if depends on older system code. $return = $this->copy('System'); if ($return) { $return = $this->copy('Admin'); } /* //clear php cache to load new installer code foreach ([DIR_SYSTEM . 'update.php', DIR_APP . 'controller' . DS . 'tools' . DS . 'update.php'] as $file) { if (function_exists('opcache_invalidate')) { opcache_invalidate($file); } clearstatcache(true, $file); } */ return $return; } /* function copyAdmin() { return $this->copy('Admin'); } */ function copyApp() { return $this->copy('App'); } function copyInstall() { return $this->copy('Install'); } function copyPublic() { return $this->copy('Public'); } function copyPublicAdmin() { return $this->copy('PublicAdmin'); } function copyPublicMedia() { return $this->copy('PublicMedia'); } function setFilePermissions() { $errorInstallMsg = __('Error setting permissions'); $result = false; try { $result = $this->update->setPermissions(); } catch (\Exception $e) { $this->view->errors[] = $e->getMessage(); } if (! $result) { $this->view->errors[] = $errorInstallMsg; } else { $this->view->success[] = __('Set permissions successful!'); } return $result; } function cleanUp() { $errorInstallMsg = __('Error cleaning up'); $result = false; try { $result = $this->update->cleanup(); } catch (\Exception $e) { $this->view->errors[] = $e->getMessage(); } if (! $result) { $this->view->errors[] = $errorInstallMsg; } else { $this->view->success[] = __('Cleanup successful!'); } return $result; } function clearCache() { $errorInstallMsg = __('Error clearing cache'); $result = false; try { $result = $this->update->clearCache(); } catch (\Exception $e) { $this->view->errors[] = $e->getMessage(); } if (! $result) { $this->view->errors[] = $errorInstallMsg; } else { $this->view->success[] = __('Clear cache successful!'); } return $result; } function complete() { $this->view->success[] = __('Update complete!'); Admin::session(['safemode' => false]); return true; } function update() { $this->request->get['step'] = $this->steps[0]; $info = __('Update in progress, do not close this page ...'); $this->view->info[] = $info; if ($this->request->isAjax()) { $url = ['module'=>'tools/update', 'action' => 'updateNext', 'step' => $this->steps[0], 'position' => 0, 'count' => count($this->steps), 'info' => $info]; die(json_encode($url + ['url' => url($url)])); } else { $this->updateNext(); } return $this->index(); } function index() { $updateInfo = $this->checkUpdates(); if (isset($updateInfo['error']) && $updateInfo['error']) { $this->view->errors[] = $updateInfo['error']; } $info = [ 'core' => [ 'current' => V_VERSION, 'latest' => $updateInfo['version'] ?? '', 'hasUpdate' => $updateInfo['hasUpdate'] ?? false, ], ]; $this->view->update = $info; } }