diff --git a/src/Classes/Controllers/IndexController.php b/src/Classes/Controllers/IndexController.php index 04093371..01fff0af 100644 --- a/src/Classes/Controllers/IndexController.php +++ b/src/Classes/Controllers/IndexController.php @@ -451,8 +451,14 @@ public function invokeUpdate() $queryParams = $this->serverRequest->getQueryParams(); $archiveName = $queryParams['archiveName'] ?? ''; $version = $queryParams['version'] ?? ''; + $force = $queryParams['force'] ?? ''; + $force = $force === 'true' ? true : false; - $moduleManagerResult = $this->moduleManager->update($archiveName); + if ($force === false) { + $moduleManagerResult = $this->moduleManager->update($archiveName); + } else { + $moduleManagerResult = $this->moduleManager->updateWithoutDependencies($archiveName, true); + } if ($moduleManagerResult->getType() === ModuleManagerResult::TYPE_ERROR) { Notification::pushFlashMessage([ @@ -517,8 +523,10 @@ public function invokeUninstall() $queryParams = $this->serverRequest->getQueryParams(); $archiveName = $queryParams['archiveName'] ?? ''; $version = $queryParams['version'] ?? ''; + $force = $queryParams['force'] ?? ''; + $force = $force === 'true' ? true : false; - $moduleManagerResult = $this->moduleManager->uninstall($archiveName); + $moduleManagerResult = $this->moduleManager->uninstall($archiveName, $force); if ($moduleManagerResult->getType() === ModuleManagerResult::TYPE_ERROR) { Notification::pushFlashMessage([ diff --git a/src/Classes/ModuleManager/ModuleInstaller.php b/src/Classes/ModuleManager/ModuleInstaller.php index 62973313..51635291 100644 --- a/src/Classes/ModuleManager/ModuleInstaller.php +++ b/src/Classes/ModuleManager/ModuleInstaller.php @@ -195,11 +195,6 @@ public function install(Module $module, bool $force = false): void * Set to true to skip the dependency check (default is false). * @param bool $force * Set to true to force the installation even if the module is already installed (default is false). - * - * @throws RuntimeException - * If the module is already installed (and 'force' is false) or if no valid combination of versions for the - * module's dependencies can be found (and 'skipDependencyCheck' is false), a RuntimeException is thrown with - * detailed error messages. Any other errors during the installation process are also reported via exceptions. */ public function installWithoutDependencies( Module $module, @@ -235,11 +230,6 @@ public function installWithoutDependencies( * * @param Module $module The module to be updated. * @param bool $force Set to true to force the update even if the module is not installed (default is false). - * - * @throws RuntimeException - * If the module is not installed (and 'force' is false) or if no valid combination of versions for the - * module's dependencies can be found, a RuntimeException is thrown with detailed error messages. Any other - * errors during the update process are also reported via exceptions. */ public function update(Module $installedModule, Module $newModule, bool $force = false): void { @@ -280,9 +270,9 @@ public function update(Module $installedModule, Module $newModule, bool $force = } /** - * //TODO: Nicht zur Neusten sondern zu höchst möglichsten Version aktualisieren. + * */ - public function updateWithoutMissingDependencies( + public function updateWithoutDependencies( Module $instaledModule, Module $newModule, bool $skipDependencyCheck = false, diff --git a/src/Classes/ModuleManager/ModuleManager.php b/src/Classes/ModuleManager/ModuleManager.php index f6557ced..44a24f0b 100644 --- a/src/Classes/ModuleManager/ModuleManager.php +++ b/src/Classes/ModuleManager/ModuleManager.php @@ -322,7 +322,7 @@ public function installWithoutDependencies( } /** - * Aktuallisiert das Modul auf die neuste Version. Dabei werden keine Abhänggigkeiten + * Aktuallisiert das Modul auf die neuste mögliche Version. Dabei werden keine Abhänggigkeiten * aktualisiert. Kommen durch das Update jedoch neue Abhängigkeiten hinzu, werden diese installt. Können nicht alle * Abhängigkeiten erfüllt werten, wird nicht aktualisiert und eine Exception geworfen. */ @@ -409,23 +409,97 @@ public function update(string $archiveName): ModuleManagerResult } /** - * Aktualiseirt NUR das Modul auf die neuste Version. Es werden keine fehlenden Abhänggigkeiten + * Aktualiseirt NUR das Modul auf die neuste möglche Version. Es werden keine fehlenden Abhänggigkeiten * installiert. Es werden keine Abhänggigkeiten aktualisiert. Können nicht alle Abhängigkeiten erfüllt werten, - * wird nicht aktualisiert und eine Exception geworfen. + * wird nicht aktualisiert, es sei denn, der Abhängigkeits-Check wird mit $skipDependencyCheck deaktiviert. */ - // public function updateWithoutMissingDependencies(string $archvieName, bool $skipDependencyCheck = false): Module - // { - // $loadedNewModul = $this->moduleInstaller->updateWithoutMissingDependencies( - // $module, - // $skipDependencyCheck, - // false - // ); - - // $autoloadFileCreator = new AutoloadFileCreator(); - // $autoloadFileCreator->createAutoloadFile(); - - // return $loadedNewModul; - // } + public function updateWithoutDependencies( + string $archiveName, + bool $skipDependencyCheck = false + ): ModuleManagerResult { + $moduleLoader = LocalModuleLoader::createFromConfig(); + $module = $moduleLoader->loadInstalledVersionByArchiveName($archiveName); + + if (!$module) { + return $this->error( + ModuleManagerMessage::create(ModuleManagerMessage::UPDATE_ERROR_MODULE_NOT_FOUND) + ->setArchiveName($archiveName) + ); + } + + if (!$module->isInstalled()) { + return $this->error( + ModuleManagerMessage::create(ModuleManagerMessage::UPDATE_ERROR_MODULE_NOT_INSTALLED) + ->setModule($module) + ); + } + + if ($module->isChanged()) { + return $this->error( + ModuleManagerMessage::create(ModuleManagerMessage::UPDATE_ERROR_MODULE_IS_CHANGED) + ->setModule($module) + ); + } + + if ($skipDependencyCheck === false) { + $systemSet = $this->systemSetFactory->getSystemSet(); + $versionConstraint = '>' . $module->getVersion(); + $combinationSatisfyerResult = $this->dependencyBuilder->satisfies($archiveName, $versionConstraint, $systemSet); + if ( + $combinationSatisfyerResult->result === CombinationSatisfyerResult::RESULT_COMBINATION_NOT_FOUND + || !$combinationSatisfyerResult->foundCombination + ) { + return $this->error( + ModuleManagerMessage::create(ModuleManagerMessage::UPDATE_ERROR_MODULE_MISSING_REQUIREMENTS) + ->setArchiveName($archiveName) + ->setVersionConstraint($versionConstraint) + ->setCombinationSatisfyerResult($combinationSatisfyerResult) + ); + } + $version = $combinationSatisfyerResult->foundCombination->getVersion($archiveName); + $newModule = $this->moduleLoader->loadByArchiveNameAndVersion($archiveName, $version); + } else { + $newModule = $this->moduleLoader->loadLatestVersionByArchiveName($archiveName); + $version = ''; + } + + if (!$newModule) { + return $this->error( + ModuleManagerMessage::create(ModuleManagerMessage::UPDATE_ERROR_MODULE_NOT_FOUND) + ->setArchiveName($archiveName) + ->setVersion($version) + ); + } + + if (!$newModule->isLoaded()) { + $this->info( + ModuleManagerMessage::create(ModuleManagerMessage::UDPATE_INFO_PULL_MODULE_START) + ->setModule($newModule) + ); + $newModule = $this->moduleInstaller->pull($newModule); + } + + $this->info( + ModuleManagerMessage::create(ModuleManagerMessage::UPDATE_INFO_START) + ->setModule($module) + ); + $this->moduleInstaller->updateWithoutDependencies($module, $newModule, true, true); + + $this->info( + ModuleManagerMessage::create(ModuleManagerMessage::UPDATE_INFO_TO) + ->setModule($newModule) + ); + + $this->info( + ModuleManagerMessage::create(ModuleManagerMessage::UPDATE_INFO_UPDATE_AUTOLOAD_START) + ); + + $autoloadFileCreator = new AutoloadFileCreator(); + $autoloadFileCreator->createAutoloadFile(); + + return ModuleManagerResult::success() + ->setModule($newModule); + } /** * Entfernt alle Änderungen die an den Modul-Dateien im Shop gemacht wurden. Änderungen an Template Dateien werden diff --git a/src/Classes/ModuleStatus.php b/src/Classes/ModuleStatus.php index 02ac83c3..f421514c 100644 --- a/src/Classes/ModuleStatus.php +++ b/src/Classes/ModuleStatus.php @@ -132,7 +132,11 @@ public static function isUpdatable(Module $module): bool return false; } - if (!$newestVersion->isLoadable()) { + if ($installedVersion->getVersion() === 'auto') { + return false; + } + + if (!$newestVersion->isLoadable() && !$newestVersion->isLoaded()) { return false; } diff --git a/src/Classes/ViewModels/ButtonViewModel.php b/src/Classes/ViewModels/ButtonViewModel.php new file mode 100644 index 00000000..cd44e3b4 --- /dev/null +++ b/src/Classes/ViewModels/ButtonViewModel.php @@ -0,0 +1,175 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace RobinTheHood\ModifiedModuleLoaderClient\ViewModels; + +class ButtonViewModel +{ + public const DEFAULT = 'default'; + public const PRIMARY = 'primary'; + public const SUCCESS = 'success'; + public const WARNING = 'warning'; + public const DANGER = 'danger'; + + /** + * @var array $actions + */ + private $actions = []; + + /** @var string */ + private $class = self::DEFAULT; + + public function __construct(string $class) + { + $this->class = $class; + } + + public static function create(string $class): ButtonViewModel + { + $button = new ButtonViewModel($class); + return $button; + } + + public function addAction(string $title, string $url): ButtonViewModel + { + $this->actions[] = [ + 'title' => $title, + 'url' => $url, + 'message' => '' + ]; + return $this; + } + + public function addConfirmAction(string $title, string $url, string $message): ButtonViewModel + { + $this->actions[] = [ + 'title' => $title, + 'url' => $url, + 'message' => $message + ]; + return $this; + } + + public function render(): string + { + if (empty($this->actions)) { + return ''; + } + + $class = $this->mapClassToCss($this->class); + + $title = $this->actions[0]['title'] ?? ''; + $url = $this->actions[0]['url'] ?? ''; + $message = $this->actions[0]['message'] ?? ''; + + $js = $message ? $this->renderConfirmJs($message) : ''; + + if (count($this->actions) > 1) { + $htmlActions = $this->renderActions($this->actions); + + $html = sprintf( + '
+ + + +
', + htmlspecialchars($class), + htmlspecialchars($url), + $js, + htmlspecialchars($title), + htmlspecialchars($class), + $htmlActions + ); + } else { + $html = sprintf( + '%s', + htmlspecialchars($class), + htmlspecialchars($url), + $js, + htmlspecialchars($title) + ); + } + + return $html; + } + + + public function __toString() + { + return $this->render(); + } + + private function mapClassToCss(string $class): string + { + $classMapping = [ + self::DEFAULT => 'btn-outline-default', + self::PRIMARY => 'btn-outline-primary', + self::SUCCESS => 'btn-outline-success', + self::WARNING => 'btn-outline-warning', + self::DANGER => 'btn-outline-danger', + ]; + + return $classMapping[$class] ?? 'btn-outline-default'; + } + + /** + * @param array $actions + */ + private function renderActions(array $actions): string + { + $html = ''; + foreach ($actions as $action) { + if ($action['message'] ?? '') { + $html .= $this->renderAConfirmTag( + $action['title'] ?? '', + $action['url'] ?? '', + $action['message'] ?? '', + 'dropdown-item' + ); + } else { + $html .= $this->renderATag($action['title'], $action['url'], 'dropdown-item'); + } + } + return $html; + } + + private function renderATag(string $title, string $url, string $class = ''): string + { + return sprintf( + '%s', + htmlspecialchars($class), + htmlspecialchars($url), + htmlspecialchars($title) + ); + } + + private function renderAConfirmTag(string $title, string $url, string $message, string $class = ''): string + { + $js = $this->renderConfirmJs($message); + return sprintf( + '%s', + htmlspecialchars($class), + htmlspecialchars($url), + $js, + htmlspecialchars($title, ENT_QUOTES, 'UTF-8') + ); + } + + private function renderConfirmJs(string $message): string + { + return sprintf('onclick="return confirm(\'%s\');"', htmlspecialchars($message, ENT_QUOTES, 'UTF-8')); + } +} diff --git a/src/Classes/ViewModels/ModuleViewModel.php b/src/Classes/ViewModels/ModuleViewModel.php index 6762e508..90ea2f43 100644 --- a/src/Classes/ViewModels/ModuleViewModel.php +++ b/src/Classes/ViewModels/ModuleViewModel.php @@ -33,6 +33,11 @@ public function getUpdateUrl(string $ref = ''): string return $this->getUrl('update', $ref); } + public function getForceUpdateUrl(string $ref = ''): string + { + return $this->getUrl('update', $ref, null, ['force' => 'true']); + } + public function getInstallUrl(string $ref = ''): string { return $this->getUrl('install', $ref); @@ -63,6 +68,11 @@ public function getUninstallUrl(string $ref = ''): string return $this->getUrl('uninstall', $ref); } + public function getForceUninstallUrl(string $ref = ''): string + { + return $this->getUrl('uninstall', $ref, null, ['force' => 'true']); + } + public function getModuleInfoUrl(string $ref = ''): string { return $this->getUrl('moduleInfo', $ref); diff --git a/src/Templates/ModuleInfo.tmpl.php b/src/Templates/ModuleInfo.tmpl.php index 2eea5f9c..1ea60390 100644 --- a/src/Templates/ModuleInfo.tmpl.php +++ b/src/Templates/ModuleInfo.tmpl.php @@ -91,61 +91,7 @@
-
- isUpdatable() && !$moduleView->isRepairable()) { ?> - Update installieren - - - isRepairable()) { ?> - - - - Änderungen verwerfen - - - Änderungen übernehmen (Link-Mode) - - - - - - - - Änderungen verwerfen inkl. Templates - - - Änderungen übernehmen inkl. Templates (Link-Mode) - - - - - isCompatibleLoadableAndInstallable()) { ?> - Download & Install - - isIncompatibleLoadebale()) { ?> - Download (inkompatible Version) - - isUninstallable() && !$moduleView->isRepairable()) { ?> - Deinstallieren - - isCompatibleInstallable()) { ?> - Installieren - - isIncompatibleInstallable()) { ?> - Installieren (inkompatible Version) - - hasInstalledVersion()) { ?> - Zur installierten Version - - - isRemote() && $moduleView->isLoaded() && !$moduleView->isInstalled()) { ?> - Modul löschen - -
+
diff --git a/src/Templates/ModuleInfoButtons.tmpl.php b/src/Templates/ModuleInfoButtons.tmpl.php new file mode 100644 index 00000000..9a5895b5 --- /dev/null +++ b/src/Templates/ModuleInfoButtons.tmpl.php @@ -0,0 +1,105 @@ +addAction('Zur installierten Version', $moduleView->getInstalledUrl('moduleInfo')); + +$buttonPullAndInstall = Button::create(Button::PRIMARY) + ->addAction('Download & Installieren', $moduleView->getLoadAndInstallUrl('moduleInfo')) + ->addAction('Download', $moduleView->getLoadModuleUrl('moduleInfo')); + +$buttonPull = Button::create(Button::PRIMARY) + ->addAction('Download', $moduleView->getLoadModuleUrl('moduleInfo')); + +$buttonInstall = Button::create(Button::SUCCESS) + ->addAction('Installieren', $moduleView->getInstallUrl('moduleInfo')) + //->addAction('Installieren ohne Abhängigkeiten', '#') + ->addAction('Installieren ohne Abhängigkeiten erzwingen', $moduleView->getForceInstallUrl('moduleInfo')); + +$buttonUpdate = Button::create(Button::SUCCESS) + ->addAction('Update installieren', $moduleView->getUpdateUrl('moduleInfo')) + ->addAction('Update installieren ohne Abhängigkeiten erzwingen', $moduleView->getForceUpdateUrl('moduleInfo')); + +$buttonDiscard = Button::create(Button::DANGER) + ->addConfirmAction( + 'Änderungen verwerfen', + $moduleView->getRevertChangesUrl('moduleInfo'), + 'Möchtest du deine Änderungen wirklich rückgängig machen?' + ) + ->addConfirmAction( + 'Änderungen inkl. Templates verwerfen', + $moduleView->getRevertChangesWithTemplateUrl('moduleInfo'), + 'Möchtest du deine Änderungen wirklich rückgängig machen?' + ); + +$buttonDiscardLink = Button::create(Button::WARNING) + ->addAction( + 'Änderungen übernehmen', + $moduleView->getRevertChangesUrl('moduleInfo') + ) + ->addAction( + 'Änderungen inkl. Templates übernehmen', + $moduleView->getRevertChangesWithTemplateUrl('moduleInfo') + ); + +$buttonUninstall = Button::create(Button::DANGER) + ->addAction('Deinstallieren', $moduleView->getUninstallUrl('moduleInfo')) + ->addAction('Deinstallieren erzwingen', $moduleView->getForceUninstallUrl('moduleInfo')); + +$buttonDelete = Button::create(Button::DANGER) + ->addConfirmAction('Löschen', $moduleView->getUnloadModuleUrl('moduleInfo'), 'Möchtest du das Modul wirklich löschen?'); + +$jsGoToFilesTab = " + +"; +?> + +
+ isUpdatable() && !$moduleView->isRepairable()) { + echo $buttonUpdate; + } elseif ($moduleView->hasInstalledVersion()) { + echo $buttonInfo; + } + + if ($moduleView->isRepairable()) { + if (Config::getInstallMode() != 'link') { + echo $buttonDiscard; + } else { + echo $buttonDiscardLink; + echo $jsGoToFilesTab; + } + } + + if ($moduleView->isCompatibleLoadableAndInstallable()) { + echo $buttonPullAndInstall; + } elseif ($moduleView->isIncompatibleLoadebale()) { + echo $buttonPull; + } elseif ($moduleView->isUninstallable() && !$moduleView->isRepairable()) { + echo $buttonUninstall; + } elseif ($moduleView->isCompatibleInstallable()) { + echo $buttonInstall; + } elseif ($moduleView->isIncompatibleInstallable()) { + echo $buttonInstall; + } + + if (!$moduleView->isRemote() && $moduleView->isLoaded() && !$moduleView->isInstalled()) { + echo $buttonDelete; + } + ?> +
\ No newline at end of file diff --git a/src/Templates/Styles/button.css b/src/Templates/Styles/button.css index 1291c49b..79e82908 100644 --- a/src/Templates/Styles/button.css +++ b/src/Templates/Styles/button.css @@ -1,6 +1,14 @@ +.btn { + padding: 10px; +} + +.moduleinfo-buttons > .btn, .moduleinfo-buttons > .btn-group { + margin-right: 10px; +} + .button { display: inline-block; - margin: 0px 10px 0px 10px; + margin: 0px 0px 0px 10px; padding: 10px; text-align: center; text-decoration: none;