diff --git a/apps/Core/Components/Devtools/Modules/ModulesComponent.php b/apps/Core/Components/Devtools/Modules/ModulesComponent.php index eb3ffc483..bf28933ee 100644 --- a/apps/Core/Components/Devtools/Modules/ModulesComponent.php +++ b/apps/Core/Components/Devtools/Modules/ModulesComponent.php @@ -28,6 +28,19 @@ public function initialize() */ public function viewAction() { + if (isset($this->getData()['repo']) && + isset($this->getData()['id']) + ) { + $this->modulesPackage->getRemoteModules($this->getData()['id']); + + $this->view->remoteModules = $this->modulesPackage->packagesData->responseData['remoteModules'] ?? []; + $this->view->apiId = $this->modulesPackage->packagesData->responseData['api_id'] ?? 0; + + $this->view->pick('modules/repo'); + + return; + } + $this->view->bundles = false; $this->view->bundlesjson = false; if (isset($this->getData()['bundles'])) { @@ -146,7 +159,6 @@ public function viewAction() } } } else if ($this->getData()['type'] === 'packages') { - unset($modules['packages']); unset($modules['views']); unset($modules['bundles']); $this->view->packageSettingsModules = $modules; @@ -490,6 +502,10 @@ public function viewAction() $this->view->pick('modules/view'); } else { + unset($apis[0]);//Remove local + unset($apis[1]);//Remove core + $this->view->apis = $apis; + $this->view->pick('modules/list'); } } @@ -532,6 +548,18 @@ public function removeAction() ); } + public function removeRepoAction() + { + $this->requestIsPost(); + + $this->modulesPackage->removeRepo($this->postData()); + + $this->addResponse( + $this->modulesPackage->packagesData->responseMessage, + $this->modulesPackage->packagesData->responseCode + ); + } + public function formatJsonAction() { $this->requestIsPost(); diff --git a/apps/Core/Components/System/Api/Client/Services/ServicesComponent.php b/apps/Core/Components/System/Api/Client/Services/ServicesComponent.php index 89ef066d3..8c161be78 100644 --- a/apps/Core/Components/System/Api/Client/Services/ServicesComponent.php +++ b/apps/Core/Components/System/Api/Client/Services/ServicesComponent.php @@ -100,10 +100,15 @@ function ($dataArr) { ] ]; + $conditions = + [ + 'conditions' => '-|app_type|equals|' . $this->apps->getAppInfo()['app_type'] . '&' + ]; + $this->generateDTContent( $this->apiPackage, 'system/api/client/services/view', - null, + $conditions, ['name', 'category', 'provider', 'in_use', 'used_by', 'setup'], true, ['name', 'category', 'provider', 'in_use', 'used_by', 'setup'], diff --git a/apps/Core/Packages/Devtools/Migrator/DevtoolsMigrator.php b/apps/Core/Packages/Devtools/Migrator/DevtoolsMigrator.php index fd761d88c..8a2e1bd24 100644 --- a/apps/Core/Packages/Devtools/Migrator/DevtoolsMigrator.php +++ b/apps/Core/Packages/Devtools/Migrator/DevtoolsMigrator.php @@ -665,7 +665,7 @@ public function getAvailableApis($getAll = false, $returnApis = true) } } } else { - $apisArr = $this->basepackages->apiClientServices->getAll()->apiClientServices; + $apisArr = $this->basepackages->apiClientServices->getApiByAppType(); } if (count($apisArr) > 0) { @@ -693,7 +693,7 @@ public function getAvailableApis($getAll = false, $returnApis = true) } if (isset($apis) && $returnApis) { - return $apis; + return $apis ?? []; } return $apisArr; diff --git a/apps/Core/Packages/Devtools/Migrator/Install/Package.php b/apps/Core/Packages/Devtools/Migrator/Install/Package.php index 7d2edc7bd..c04d01e2a 100644 --- a/apps/Core/Packages/Devtools/Migrator/Install/Package.php +++ b/apps/Core/Packages/Devtools/Migrator/Install/Package.php @@ -42,7 +42,11 @@ public function install($redoDB = false) $tableName = $tableClass['model']->getSource(); } - $config = $this->ff->generateConfig($tableName, $tableClass['schema'], $tableClass['model'], $this->db); + $tableConfigParams = []; + if (isset($tableClass['configParams'])) { + $tableConfigParams = $tableClass['configParams']; + } + $config = $this->ff->generateConfig($tableName, $tableClass['schema'], $tableClass['model'], $tableConfigParams); $schema = $this->ff->generateSchema($tableName, $tableClass['schema'], $tableClass['model']); if ($redoDB) { diff --git a/apps/Core/Packages/Devtools/Modules/DevtoolsModules.php b/apps/Core/Packages/Devtools/Modules/DevtoolsModules.php index 808506111..767af93b7 100644 --- a/apps/Core/Packages/Devtools/Modules/DevtoolsModules.php +++ b/apps/Core/Packages/Devtools/Modules/DevtoolsModules.php @@ -6,6 +6,8 @@ use League\Flysystem\FilesystemException; use League\Flysystem\UnableToCheckExistence; use League\Flysystem\UnableToCreateDirectory; +use League\Flysystem\UnableToDeleteDirectory; +use League\Flysystem\UnableToDeleteFile; use League\Flysystem\UnableToReadFile; use League\Flysystem\UnableToRetrieveMetadata; use League\Flysystem\UnableToWriteFile; @@ -70,6 +72,7 @@ public function addModule($data) $data['repo'] = trim(str_replace('(clone)', '', $data['repo'])); $data['installed'] = '1'; $data['updated_by'] = '0'; + $data['version'] = '0.0.0'; if ($data['module_type'] === 'bundles') { if ($this->modules->{$data['module_type']}->add($data)) { @@ -129,18 +132,22 @@ public function addModule($data) if ($data['module_type'] === 'views' && $data['base_view_module_id'] == 0) { $data['view_modules_version'] = '0.0.0'; } + $viewPublic = true; + if ($data['module_type'] === 'views' && $data['is_subview'] == true) { + $viewPublic = false; + } if ($data['apps'] === '') { $data['apps'] = $this->helper->encode([]); } try { - if ($this->modules->{$data['module_type']}->add($data) && - $this->updateModuleJson($data, false, true) && - $this->generateNewFiles($data) + if ($this->generateNewFiles($data) && + $this->updateModuleJson($data, false, $viewPublic) && + $this->modules->{$data['module_type']}->add($data) ) { if ($data['createrepo'] == true) { - if ($data['module_type'] === 'views' && $data['base_view_module_id'] == 0) {//Create public repository as well + if ($data['module_type'] === 'views' && $data['is_subview'] == false) {//Create public repository as well if (!$this->checkRepo($data)) { if (strtolower($data['app_type']) !== 'core') { $newRepo['base'] = $this->createRepo($data); @@ -278,8 +285,12 @@ public function updateModule($data) $module = array_merge($module, $data); - if ($this->modules->{$data['module_type']}->update($module) && - $this->updateModuleJson($data, false, true) + $viewPublic = true; + if ($module['module_type'] === 'views' && $module['is_subview'] == true) { + $viewPublic = false; + } + if ($this->updateModuleJson($data, false, $viewPublic) && + $this->modules->{$data['module_type']}->update($module) ) { if ($data['module_type'] === 'components') { $this->addUpdateComponentMenu($data); @@ -355,10 +366,10 @@ public function updateModule($data) $this->core->update($core); } - if ((isset($data['reinstall_table']) && $data['reinstall_table'] == true) || + if ((isset($data['run_install_uninstall']) && $data['run_install_uninstall'] == true) || (isset($data['truncate_table']) && $data['truncate_table'] == true) ) { - $this->reinstallTruncateTable($data); + $this->runInstallUninstallTruncateTable($data); } $this->addResponse('Module updated'); @@ -377,29 +388,220 @@ public function updateModule($data) public function removeModule($data) { - if ($data['module_type'] !== 'core') { - if (isset($data['module_type']) && $data['module_type'] === 'apptypes') { - $this->apps->types->removeAppType($data); - $this->addResponse('Removed app type from DB. Remove files manually...'); + try { + if ($data['module_type'] !== 'apptypes') { + $module = $this->modules->{$data['module_type']}->getById($data['id']); + } else { + $module = $this->apps->types->getById($data['id']); + } - return true; + if ($data['module_type'] !== 'bundles') { + if ($data['module_type'] !== 'views' && + $data['module_type'] !== 'apptypes' + ) { + $classArr = explode('\\', $module['class']); + + $classArr = array_slice($classArr, 0, -1); + + $class = implode('\\', $classArr) . '\\Install\\Install'; + + $path = lcfirst(str_replace('\\', '/', $class) . '.php'); + + try { + if ($this->localContent->fileExists($path)) { + $class = new $class; + + if (method_exists($class, 'uninstall')) { + $class->init()->uninstall(true); + } + } + } catch (FilesystemException | UnableToCheckExistence | \throwable $e) { + $this->addResponse($e->getMessage(), 1); + + return false; + } + } + + if (isset($data['remove_files']) && + $data['remove_files'] == 'true' && + !$this->cleanup($this->getModuleFilesLocation($module)) + ) { + return false; + } + + if ($data['module_type'] === 'views' && + $module['is_subview'] == false + ) { + if (isset($data['remove_files']) && + $data['remove_files'] == 'true' && + !$this->cleanup($this->getModuleFilesLocation($module, true)) + ) { + return false; + } + } } - $module = $this->modules->{$data['module_type']}->getById($data['id']); - } else { - $this->addResponse('Cannot remove Core!.', 1); + //Remove the module + if ($data['module_type'] === 'apptypes') { + $this->apps->types->removeAppType($data); + } else { + $this->modules->{$module['module_type']}->remove($module['id']); + } + + $this->addResponse('Removed module from DB & files from the system...'); + + return true; + } catch (\throwable $e) { + $this->addResponse($e->getMessage(), 1); return false; } + } + + public function cleanup($modulePath) + { + $files = $this->basepackages->utils->scanDir($modulePath); + + if (count($files['files']) > 0) { + try { + foreach ($files['files'] as $file) { + $this->localContent->delete($file); + } + } catch (FilesystemException | UnableToDeleteFile | \throwable $e) { + $this->addResponse($e->getMessage(), 1); + + return false; + } + } + + if (count($files['dirs']) > 0) { + try { + foreach ($files['dirs'] as $dir) { + $this->localContent->deleteDirectory($dir); + } + + //cleanup path by checking if any of the path directory is empty. If empty, we delete the directory. + $pathArr = explode('/', $modulePath); + + foreach ($pathArr as $path) { + $path = null;//We dont need path as we will be popping it in the end. + + $checkPath = join('/', $pathArr); + + $folders = $this->localContent->listContents($checkPath)->toArray(); + + if (count($folders) === 0) { + $this->localContent->deleteDirectory($checkPath); + } else if (count($folders) === 1) { + $filePath = $folders[0]->path(); + + if (str_contains($filePath, '.gitkeep')) { + $this->localContent->delete($filePath); + $this->localContent->deleteDirectory($checkPath); + } + } - if ($module && $this->modules->{$data['module_type']}->remove($module['id'])) { - $this->addResponse('Removed module from DB. Remove files manually...'); + array_pop($pathArr); + } + } catch (FilesystemException | UnableToDeleteFile | UnableToDeleteDirectory | \throwable $e) { + $this->addResponse($e->getMessage(), 1); + + return false; + } } else { - $this->addResponse('Error removing module.', 1); + try { + $this->localContent->deleteDirectory($modulePath); + } catch (FilesystemException | UnableToDeleteFile | UnableToDeleteDirectory | \throwable $e) { + $this->addResponse($e->getMessage(), 1); + + return false; + } } + + return true; } - protected function reinstallTruncateTable($data) + protected function getModuleFilesLocation($module, $viewPublic = false) + { + if (!isset($module['module_type']) && + ($module['app_type'] === strtolower($module['name'])) + ) { + return 'apps/' . ucfirst($module['app_type']) . '/'; + } else if ($module['module_type'] === 'components') { + $moduleLocation = 'apps/' . ucfirst($module['app_type']) . '/Components/'; + + $routeArr = explode('/', $module['route']); + + foreach ($routeArr as &$path) { + $path = ucfirst($path); + } + + $routePath = implode('/', $routeArr) . '/'; + } else if ($module['module_type'] === 'packages') { + $moduleLocation = 'apps/' . ucfirst($module['app_type']) . '/Packages/'; + + $pathArr = preg_split('/(?=[A-Z])/', ucfirst($module['name']), -1, PREG_SPLIT_NO_EMPTY); + + $routePath = implode('/', $pathArr) . '/'; + } else if ($module['module_type'] === 'middlewares') { + $moduleLocation = 'apps/' . ucfirst($module['app_type']) . '/Middlewares/'; + + $routePath = $module['name'] . '/'; + } else if ($module['module_type'] === 'views') { + $moduleLocation = 'apps/' . ucfirst($module['app_type']) . '/Views/'; + + if ($viewPublic) { + $moduleLocation = 'public/' . $module['app_type'] . '/' . strtolower($module['name']) . '/'; + + return $moduleLocation; + } + + if ($module['is_subview'] == 0) { + $routePath = $module['name'] . '/'; + } else { + if (is_string($module['dependencies'])) { + $module['dependencies'] = $this->helper->decode($module['dependencies'], true); + } + if (!isset($module['dependencies']['views']) || + (isset($module['dependencies']['views']) && count($module['dependencies']['views']) === 0) + ) { + throw new \Exception('Base view dependencies for sub view missing in module dependencies.'); + } + + foreach ($module['dependencies']['views'] as $view) { + $view = $this->modules->views->getViewByRepo($view['repo']); + + if ($view && $view['is_subview'] == false) { + $baseView = $view; + + break; + } + } + + if (!isset($baseView)) { + throw new \Exception('Base view dependencies for sub view not found on the system.'); + } + + $pathArr = preg_split('/(?=[A-Z])/', ucfirst($module['name']), -1, PREG_SPLIT_NO_EMPTY); + + if (count($pathArr) > 1) { + foreach ($pathArr as &$path) { + $path = strtolower($path); + } + } else { + $pathArr[0] = strtolower($pathArr[0]); + } + + $module['route'] = implode('/', $pathArr); + + $routePath = $baseView['name'] . '/html/' . $module['route'] . '/'; + } + } + + return $moduleLocation . $routePath; + } + + protected function runInstallUninstallTruncateTable($data) { $moduleToReinstall = $this->modules->manager->getModuleInfo( [ @@ -431,7 +633,7 @@ protected function reinstallTruncateTable($data) $coreInstall = new CoreInstall; if ($data['type'] === 'core') { - if (isset($data['reinstall_table']) && $data['reinstall_table'] == true) { + if (isset($data['run_install_uninstall']) && $data['run_install_uninstall'] == true) { $coreInstall->init()->install(); } } else if ($data['type'] === 'packages') { @@ -439,16 +641,25 @@ protected function reinstallTruncateTable($data) if (isset($data['truncate_table']) && $data['truncate_table'] == true) { $coreInstall->init([$moduleModel->getSource()])->truncate(); - } else if (isset($data['reinstall_table']) && $data['reinstall_table'] == true) { - $coreInstall->init([$moduleModel->getSource()])->install(); + } else if (isset($data['run_install_uninstall']) && $data['run_install_uninstall'] == true) { + if ($data['installed'] == true) { + $coreInstall->init([$moduleModel->getSource()])->install(); + } else { + $coreInstall->init([$moduleModel->getSource()])->uninstall(); + } } } } else { $module = new $class(); + if (isset($data['truncate_table']) && $data['truncate_table'] == true && method_exists($module, 'truncate')) { $module->init()->truncate(); - } else if (isset($data['reinstall_table']) && $data['reinstall_table'] == true) { - $module->init()->install(); + } else if (isset($data['run_install_uninstall']) && $data['run_install_uninstall'] == true) { + if ($data['installed'] == true) { + $module->init()->install(); + } else { + $module->init()->uninstall(); + } } } } @@ -709,6 +920,7 @@ public function getDefaultDependencies($type, $isSubView = false) $defaultDependencies['views'] = []; $defaultDependencies['externals'] = $externalDependencies; } else if ($type === 'packages') { + $defaultDependencies['packages'] = []; $defaultDependencies['middlewares'] = []; $defaultDependencies['externals'] = $externalDependencies; } else if ($type === 'middlewares') { @@ -725,7 +937,6 @@ public function getDefaultDependencies($type, $isSubView = false) $defaultDependencies['middlewares'] = []; $defaultDependencies['views'] = []; $defaultDependencies['bundles'] = []; - $defaultDependencies['externals'] = $externalDependencies; } $this->addResponse('Generated default dependencies', 0, ['defaultDependencies' => $defaultDependencies]); @@ -1009,7 +1220,7 @@ protected function getModuleJsonFileLocation(&$data) } } - protected function generateNewFiles($data) + protected function generateNewFiles(&$data) { $moduleFilesLocation = $this->getNewFilesLocation($data); @@ -1110,7 +1321,7 @@ protected function getNewFilesLocation($data, $viewPublic = false) } } - protected function generateNewComponentsFiles($moduleFilesLocation, $data) + protected function generateNewComponentsFiles($moduleFilesLocation, &$data) { $this->addUpdateComponentMenu($data); @@ -1124,9 +1335,9 @@ protected function generateNewComponentsFiles($moduleFilesLocation, $data) return false; } - $data['class'] = explode('\\', $data['class']); - unset($data['class'][$this->helper->lastKey($data['class'])]); - $namespaceClass = implode('\\', $data['class']); + $dataClass = explode('\\', $data['class']); + unset($dataClass[$this->helper->lastKey($dataClass)]); + $namespaceClass = implode('\\', $dataClass); $file = str_replace('"NAMESPACE"', 'namespace ' . $namespaceClass, $file); $file = str_replace('"COMPONENTNAME"', $componentName, $file); @@ -1141,7 +1352,7 @@ protected function generateNewComponentsFiles($moduleFilesLocation, $data) } if (isset($data['widgets']) && $data['widgets'] !== '') { - $data['widgets'] = $this->helper->decode($data['widgets'], true); + $dataWidgets = $this->helper->decode($data['widgets'], true); try { $file = $this->localContent->read('apps/Core/Packages/Devtools/Modules/Files/ComponentWidget.txt'); @@ -1154,7 +1365,7 @@ protected function generateNewComponentsFiles($moduleFilesLocation, $data) $file = str_replace('"NAMESPACE"', 'namespace ' . $namespaceClass, $file); - foreach ($data['widgets'] as $widget) { + foreach ($dataWidgets as $widget) { try { $methodFile = $this->localContent->read('apps/Core/Packages/Devtools/Modules/Files/ComponentWidgetMethod.txt'); } catch (FilesystemException | UnableToReadFile $exception) { @@ -1448,7 +1659,7 @@ protected function generateNewPackagesModelFiles($data, $moduleFilesLocation) protected function generateNewViewsFiles($moduleFilesLocation, $data) { - if ($data['base_view_module_id'] == 0) { + if ($data['is_subview'] == false) { $modulePublicFilesLocation = $this->getNewFilesLocation($data, true); try { @@ -1514,7 +1725,7 @@ protected function generateNewViewsFiles($moduleFilesLocation, $data) return true; } - protected function addUpdateComponentMenu($data) + protected function addUpdateComponentMenu(&$data) { if ($data['menu_id'] != '' && $data['menu_id'] != '0') { if (!isset($data['is_clone']) || @@ -1550,12 +1761,13 @@ protected function addUpdateComponentMenu($data) $menu = $this->basepackages->menus->addMenu($data); if ($menu) { - $module = $this->modules->{$data['module_type']}->packagesData->last; + // $module = $this->modules->{$data['module_type']}->packagesData->last; - $module['menu_id'] = $menu['id']; + // $module['menu_id'] = $menu['id']; + $data['menu_id'] = $menu['id']; } - $this->modules->{$data['module_type']}->update($module); + // $this->modules->{$data['module_type']}->update($module); } } } @@ -1774,7 +1986,7 @@ protected function initApi($data) return true; } - if (!isset($data['api_id']) || !isset($data['name'])) { + if (!isset($data['api_id'])) { $this->addResponse('API information not provided', 1, []); return false; @@ -1787,7 +1999,6 @@ protected function initApi($data) } $this->apiClient = $this->basepackages->apiClientServices->useApi($data['api_id'], true); - $this->apiClientConfig = $this->apiClient->getApiConfig(); if ($this->apiClientConfig['auth_type'] === 'auth' && @@ -3212,7 +3423,7 @@ public function getAvailableApis($getAll = false, $returnApis = true) } } } else { - $apisArr = $this->basepackages->apiClientServices->getAll()->apiClientServices; + $apisArr = $this->basepackages->apiClientServices->getApiByAppType(); } if (count($apisArr) > 0) { @@ -3240,7 +3451,7 @@ public function getAvailableApis($getAll = false, $returnApis = true) } if ($returnApis) { - return $apis; + return $apis ?? []; } return $apisArr; @@ -3316,4 +3527,151 @@ public function getAvailableReleaseTypes() ] ]; } + + public function getRemoteModules($id) + { + if (!$this->initApi(['api_id' => $id])) { + return false; + } + + $this->apiClientConfig['repo_url'] = rtrim($this->apiClientConfig['repo_url'], '/'); + //Get one or core repo details, no need to loop through all repos in the org. + if (!str_ends_with($this->apiClientConfig['repo_url'], '/' . $this->apiClientConfig['org_user'])) { + $repoUrlArr = explode('/', $this->apiClientConfig['repo_url']); + + $remoteModule = $this->getRemoteModule($this->helper->last($repoUrlArr)); + + if (!$remoteModule) { + return false; + } + + $modulesArr = [$remoteModule]; + } else { + //Process all repositories in the org as repourl and org name is the same + if (strtolower($this->apiClientConfig['provider']) === 'gitea') { + $collection = 'OrganizationApi'; + $method = 'orgListRepos'; + $args = [$this->apiClientConfig['org_user']]; + } else if (strtolower($this->apiClientConfig['provider']) === 'github') { + $collection = 'ReposApi'; + $method = 'reposListForOrg'; + $args = [$this->apiClientConfig['org_user']]; + } + + try { + $modulesArr = $this->apiClient->useMethod($collection, $method, $args)->getResponse(true); + } catch (\throwable | ClientException $e) { + $this->addResponse($e->getMessage(), 1); + + if ($e->getCode() === 401) { + $this->addResponse('API Authentication failed.', 1); + } else if (str_contains($e->getMessage(), 'Connection timed out')) { + $this->addResponse('Error connecting to the repository.', 1); + } + + return false; + } + } + + if ($modulesArr) { + $modulesTypeArr = ['components', 'packages', 'middlewares', 'views', 'bundles']; + + foreach ($modulesArr as $key => &$module) { + $names = explode('-', $module['name']); + + if (count($names) === 1) {//Only Core and Apptype has no module type set + $module['module_type'] = 'apptypes'; + } else { + if (in_array(strtolower($names[1]), $modulesTypeArr)) { + $module['module_type'] = $names[1]; + } else { + $module['module_type'] = '-'; + } + } + + $localModule = false; + $module['synced'] = false; + if ($module['module_type'] !== '-') { + if ($module['module_type'] === 'apptypes') { + $localModule = $this->apps->types->getAppTypeByRepo($module['html_url']); + } else { + $repoUrl = $module['html_url']; + if ($module['module_type'] === 'views') { + if (str_ends_with($repoUrl, '-public')) { + $repoUrl = str_replace('-public', '', $repoUrl); + } + } + $moduleMethod = 'get' . ucfirst(substr($module['module_type'], 0, -1)) . 'ByRepo'; + $localModule = $this->modules->{$module['module_type']}->$moduleMethod($repoUrl); + } + } + + if ($localModule) { + $module['synced'] = $localModule['id']; + } + } + + $this->addResponse('Sync complete', 0, ['remoteModules' => $modulesArr, 'api_id' => $id]); + + return true; + } + + $this->addResponse('Unable to sync with remote or remote has no repositories!', 1); + + return false; + } + + public function removeRepo($data) + { + if (!isset($data['repo'])) { + $this->addResponse('Repo not set', 1); + + return false; + } + + if (!$this->initApi($data)) { + return false; + } + + $this->apiClientConfig['repo_url'] = rtrim($this->apiClientConfig['repo_url'], '/'); + //Get one or core repo details, no need to loop through all repos in the org. + if (!str_ends_with($this->apiClientConfig['repo_url'], '/' . $this->apiClientConfig['org_user'])) { + $repoUrlArr = explode('/', $this->apiClientConfig['repo_url']); + + $remoteModule = $this->getRemoteModule($this->helper->last($repoUrlArr)); + + if (!$remoteModule) { + return false; + } + + $modulesArr = [$remoteModule]; + } else { + //Process all repositories in the org as repourl and org name is the same + if (strtolower($this->apiClientConfig['provider']) === 'gitea') { + $collection = 'RepositoryApi'; + $method = 'repoDelete'; + $args = [$this->apiClientConfig['org_user']]; + } else if (strtolower($this->apiClientConfig['provider']) === 'github') { + $collection = 'ReposApi'; + $method = 'reposDelete'; + $args = [$this->apiClientConfig['org_user'], $data['repo']]; + } + + try { + $this->apiClient->useMethod($collection, $method, $args)->getResponse(true); + + $this->addResponse('Repo ' . $data['repo'] . ' deleted from remote'); + } catch (\throwable | ClientException $e) { + $this->addResponse($e->getMessage(), 1); + + if ($e->getCode() === 401) { + $this->addResponse('API Authentication failed.', 1); + } else if (str_contains($e->getMessage(), 'Connection timed out')) { + $this->addResponse('Error connecting to the repository.', 1); + } + + return false; + } + } + } } \ No newline at end of file diff --git a/apps/Core/Views/Default/html/devtools/modules/bundle/modules.html b/apps/Core/Views/Default/html/devtools/modules/bundle/modules.html index a50faf8b8..2ebd65f5a 100644 --- a/apps/Core/Views/Default/html/devtools/modules/bundle/modules.html +++ b/apps/Core/Views/Default/html/devtools/modules/bundle/modules.html @@ -212,13 +212,13 @@ 'fieldId' : 'bundle_modules', 'fieldLabel' : 'Bundle Modules', 'fieldType' : 'textarea', - 'fieldDataMaxLength' : 4096, + 'fieldDataMaxLength' : 32768, 'fieldHelp' : true, 'fieldHelpTooltipContent' : 'Bundle Modules', 'fieldBazScan' : true, 'fieldBazPostOnCreate' : true, 'fieldBazPostOnUpdate' : true, - 'fieldDataInputMaxLength' : 4096, + 'fieldDataInputMaxLength' : 32768, 'fieldTextareaRows' : 15, 'fieldValue' : bundleModules ] diff --git a/apps/Core/Views/Default/html/devtools/modules/module.html b/apps/Core/Views/Default/html/devtools/modules/module.html index ce0195edd..44de2b47b 100644 --- a/apps/Core/Views/Default/html/devtools/modules/module.html +++ b/apps/Core/Views/Default/html/devtools/modules/module.html @@ -1007,7 +1007,7 @@ '#{{componentId}}-{{sectionId}}-modules-external-version' ).on('keyup', function() { if ($(this).val() !== '') { - label = 'external'; + label = 'externals'; $('#{{componentId}}-{{sectionId}}-modules').attr('disabled', true); $('#{{componentId}}-{{sectionId}}-modules-external-add, ' + '#{{componentId}}-{{sectionId}}-modules-external-remove').attr('disabled', false); @@ -1079,9 +1079,9 @@ dataCollectionSectionForm['vars']['bundleModules'][label]['version'] = moduleData['version']; dataCollectionSectionForm['vars']['bundleModules'][label]['repo'] = moduleData['repo']; - } else if (label === 'external') { - if (dataCollectionSectionForm['vars']['bundleModules']['external']['composer']['require'].length === 0) { - dataCollectionSectionForm['vars']['bundleModules']['external']['composer']['require'] = { }; + } else if (label === 'externals') { + if (dataCollectionSectionForm['vars']['bundleModules']['externals']['composer']['require'].length === 0) { + dataCollectionSectionForm['vars']['bundleModules']['externals']['composer']['require'] = { }; } dataCollectionSectionForm['vars']['bundleModules'][label]['composer']['require'][$('#{{componentId}}-{{sectionId}}-modules-external').val()] @@ -1126,7 +1126,7 @@ label = 'apptype'; } dataCollectionSectionForm['vars']['bundleModules'][label] = { }; - } else if (label === 'external') { + } else if (label === 'externals') { delete dataCollectionSectionForm['vars']['bundleModules'][label]['composer']['require'][$('#{{componentId}}-{{sectionId}}-modules-external').val()]; } else { for (var modules in dataCollectionSectionForm['vars']['bundleModules']) { @@ -1531,9 +1531,9 @@ } if (dataCollectionSectionForm['vars']['module_type'] !== 'views' && dataCollectionSectionForm['vars']['module_type'] !== 'core' && - !dataCollectionSectionForm['vars']['moduleDependencies']['external'] + !dataCollectionSectionForm['vars']['moduleDependencies']['externals'] ) { - dataCollectionSectionForm['vars']['moduleDependencies']['external'] = { }; + dataCollectionSectionForm['vars']['moduleDependencies']['externals'] = { }; } dataCollectionSection = @@ -1692,7 +1692,7 @@ '{{componentId}}-{{sectionId}}-createrepo' : { }, '{{componentId}}-{{sectionId}}-installed' : { }, '{{componentId}}-{{sectionId}}-truncate_table' : { }, - '{{componentId}}-{{sectionId}}-reinstall_table' : { }, + '{{componentId}}-{{sectionId}}-run_install_uninstall' : { }, '{{componentId}}-{{sectionId}}-class' : { afterInit: function() { $('#{{componentId}}-{{sectionId}}-generate-module-class').click(function(e) { @@ -3093,7 +3093,7 @@ '#{{componentId}}-{{sectionId}}-dependencies-external-version' ).on('keyup', function() { if ($(this).val() !== '') { - label = 'external'; + label = 'externals'; $('#{{componentId}}-{{sectionId}}-dependencies-modules').attr('disabled', true); $('#{{componentId}}-{{sectionId}}-dependencies-external-add, ' + '#{{componentId}}-{{sectionId}}-dependencies-external-remove').attr('disabled', false); @@ -3196,7 +3196,7 @@ dataCollectionSectionForm['vars']['moduleDependencies']['apptype']['name'] = $('#{{componentId}}-{{sectionId}}-dependencies-modulename').val(); dataCollectionSectionForm['vars']['moduleDependencies']['apptype']['version'] = $('#{{componentId}}-{{sectionId}}-dependencies-moduleversion').val(); dataCollectionSectionForm['vars']['moduleDependencies']['apptype']['repo'] = $('#{{componentId}}-{{sectionId}}-dependencies-modulerepo').val(); - } else if (label === 'external') { + } else if (label === 'externals') { if (dataCollectionSectionForm['vars']['module_type'] === 'core') { if (!dataCollectionSectionForm['vars']['moduleDependencies']['composer']) { dataCollectionSectionForm['vars']['moduleDependencies']['composer'] = { }; @@ -3265,7 +3265,7 @@ dataCollectionSectionForm['vars']['moduleDependencies'][label] = { }; } else if (label === 'apptypes') { dataCollectionSectionForm['vars']['moduleDependencies']['apptype'] = { }; - } else if (label === 'external') { + } else if (label === 'externals') { if (dataCollectionSectionForm['vars']['module_type'] === 'core') { if (dataCollectionSectionForm['vars']['moduleDependencies']['composer']['require'][$('#{{componentId}}-{{sectionId}}-dependencies-external').val()]) { delete dataCollectionSectionForm['vars']['moduleDependencies']['composer']['require'][$('#{{componentId}}-{{sectionId}}-dependencies-external').val()]; diff --git a/apps/Core/Views/Default/html/devtools/modules/module/info.html b/apps/Core/Views/Default/html/devtools/modules/module/info.html index 8227ec87b..53b36e522 100644 --- a/apps/Core/Views/Default/html/devtools/modules/module/info.html +++ b/apps/Core/Views/Default/html/devtools/modules/module/info.html @@ -463,7 +463,7 @@ )}} {% endif %} - {% if type === 'packages' or type === 'core' %} + {% if type != 'views' %}
{{adminltetags.useTag('fields', [ @@ -471,11 +471,11 @@ 'componentName' : componentName, 'componentId' : componentId, 'sectionId' : sectionId, - 'fieldId' : 'reinstall_table', - 'fieldLabel' : 'Reinstall Table?', + 'fieldId' : 'run_install_uninstall', + 'fieldLabel' : 'Run Install/Uninstall script?', 'fieldHelp' : true, 'fieldDisabled' : false, - 'fieldHelpTooltipContent' : 'This will reinstall the table structure.', + 'fieldHelpTooltipContent' : 'If installed is checked, install method of the Install.php script will be executed. If installed is unchecked, uninstall method of the Install.php script will be executed.', 'fieldType' : 'checkbox', 'fieldCheckboxType' : 'danger', 'fieldCheckboxChecked' : false, diff --git a/apps/Core/Views/Default/html/devtools/modules/repo.html b/apps/Core/Views/Default/html/devtools/modules/repo.html new file mode 100644 index 000000000..2782bda31 --- /dev/null +++ b/apps/Core/Views/Default/html/devtools/modules/repo.html @@ -0,0 +1,18 @@ +{{adminltetags.useTag('content', + [ + 'component' : component, + 'componentName' : componentName, + 'componentId' : componentId, + 'parentComponentId' : parent, + 'sectionId' : 'repo', + 'contentType' : 'section', + 'cardHeader' : true, + 'cardFooter' : false, + 'cardType' : 'primary', + 'cardIcon' : 'th', + 'cardTitle' : 'Remote Repo', + 'cardAdditionalClass' : 'rounded-0', + 'cardShowTools' : ['packageSettings'], + 'cardBodyInclude' : 'modules/repo/view' + ] +)}} \ No newline at end of file diff --git a/apps/Core/Views/Default/html/devtools/modules/repo/view.html b/apps/Core/Views/Default/html/devtools/modules/repo/view.html new file mode 100644 index 000000000..f53050b51 --- /dev/null +++ b/apps/Core/Views/Default/html/devtools/modules/repo/view.html @@ -0,0 +1,209 @@ + +{% set repoHtml = '' %} +{% if remoteModules|length > 0 %} + {% for remoteModule in remoteModules %} + {% set moduleSynced = '-' %} + {% if remoteModule['synced'] != false %} + {% if remoteModule['module_type'] == 'bundles' %} + {% set url = links.url('devtools/modules/q/bundles/true/' ~ '/id/' ~ remoteModule['synced']) %} + {% else %} + {% set url = links.url('devtools/modules/q/module/true/type/' ~ remoteModule['module_type'] ~ '/id/' ~ remoteModule['synced']) %} + {% endif %} + {% set moduleSynced = + '' + %} + {% endif %} + {% set repoHtml = repoHtml ~ '' ~ + ' ' ~ remoteModule['name'] ~ '' ~ + ' ' ~ remoteModule['module_type'] ~ '' ~ + ' ' ~ remoteModule['html_url'] ~ '' ~ + ' ' ~ moduleSynced ~ '' ~ + ' ' ~ + ' ' ~ + ' ' ~ + ' ' %} + {% endfor %} +{% endif %} +{% if repoHtml != '' %} +
+
+ {{adminltetags.useTag('buttons', + [ + 'component' : component, + 'componentName' : componentName, + 'componentId' : componentId, + 'sectionId' : sectionId, + 'buttonType' : 'button', + 'buttons' : + [ + 'modules-link' : [ + 'title' : 'link', + 'size' : 'xs', + 'disabled' : true, + 'hidden' : true, + 'url' : '#' + ] + ] + ] + )}} +
+
+
+
+ + + + + + + + + + + + {{repoHtml}} + +
NameModule TypeRepositorySyncedOptions
+
+
+
+
+
+ +
+
+{% endif %} +{% set remoteModules = json_encode(remoteModules, 16) %} + + \ No newline at end of file diff --git a/apps/Core/Views/Default/html/devtools/modules/select.html b/apps/Core/Views/Default/html/devtools/modules/select.html index 6fbb4c888..85b8eb608 100644 --- a/apps/Core/Views/Default/html/devtools/modules/select.html +++ b/apps/Core/Views/Default/html/devtools/modules/select.html @@ -11,197 +11,292 @@ {% set middlewaresURL = middlewaresURL ~ '/includecoremodules/true' %} {% set viewsURL = viewsURL ~ '/includecoremodules/true' %} {% endif %} -
+
- {% set includeCoreModulesChecked = false %} - {% if includecoremodules is defined and includecoremodules == true %} - {% set includeCoreModulesChecked = true %} - {% endif %} - {{adminltetags.useTag('fields', - [ - 'component' : component, - 'componentName' : componentName, - 'componentId' : componentId, - 'sectionId' : sectionId, - 'fieldId' : 'include_core_modules', - 'fieldLabel' : 'Include Core Modules?', - 'fieldHelp' : true, - 'fieldHelpTooltipContent' : 'Include Core modules in the list of modules?', - 'fieldType' : 'checkbox', - 'fieldCheckboxType' : 'info', - 'fieldCheckboxChecked' : includeCoreModulesChecked, - 'fieldCheckboxAdditionClass' : 'text-sm text-uppercase', - 'fieldBazPostOnCreate' : true, - 'fieldBazPostOnUpdate' : true, - 'fieldBazJstreeSearch' : true, - 'fieldBazScan' : true - ] - )}} -
-
- {{adminltetags.useTag('buttons', - [ - 'componentId' : componentId, - 'sectionId' : sectionId, - 'buttonType' : 'DropdownSplitButtons', - 'buttonLabel' : false, - 'dropdownButtonId' : 'main', - 'dropdownButtonTitle' : 'Add new', - 'dropdownButtonIcon' : 'plus', - 'dropdownButtonAdditionalClass' : 'text-uppercase', - 'dropdownAlign' : 'right', - 'buttonPosition' : 'right', - 'dropdowns' : - { - 'apptypes' : { - 'title' : 'App Type', - 'type' : 'primary', - 'additionalClass' : 'contentAjaxLink', - 'url' : apptypesURL - }, - 'components' : { - 'title' : 'Component', - 'type' : 'primary', - 'additionalClass' : 'contentAjaxLink', - 'url' : componentsURL - }, - 'packages' : { - 'title' : 'Package', - 'type' : 'primary', - 'additionalClass' : 'contentAjaxLink', - 'url' : packagesURL - }, - 'middlewares' : { - 'title' : 'Middleware', - 'type' : 'primary', - 'additionalClass' : 'contentAjaxLink', - 'url' : middlewaresURL - }, - 'views' : { - 'title' : 'View', - 'type' : 'primary', - 'additionalClass' : 'contentAjaxLink', - 'url' : viewsURL - }, - 'subviews' : { - 'title' : 'Sub View', - 'type' : 'primary', - 'additionalClass' : 'contentAjaxLink', - 'url' : subViewsURL - }, - 'divider' : 'divider', - 'bundles' : { - 'title' : 'Modules Bundle', - 'type' : 'primary', - 'additionalClass' : 'contentAjaxLink', - 'url' : bundlesURL - } - } - ] - )}} -
-
-
-
- {{adminltetags.useTag('fields', - [ - 'component' : component, - 'componentName' : componentName, - 'componentId' : componentId, - 'sectionId' : sectionId, - 'fieldId' : 'modules', - 'fieldLabel' : 'Select Module', - 'fieldType' : 'select2', - 'fieldHelp' : true, - 'fieldHelpTooltipContent' : 'Module', - 'fieldRequired' : true, - 'fieldBazScan' : true, - 'fieldBazPostOnCreate' : true, - 'fieldBazPostOnUpdate' : true, - 'fieldDataSelect2AddDataAttrFromData' : ['installed', 'is_subview'], - 'fieldDataSelect2Options' : modules, - 'fieldDataSelect2OptionsKey' : 'id', - 'fieldDataSelect2OptionsValue' : 'display_name:app_type|name:app_type', - 'fieldDataSelect2OptionsSelected' : '' - ] - )}} - {{adminltetags.useTag('buttons', - [ - 'component' : component, - 'componentName' : componentName, - 'componentId' : componentId, - 'sectionId' : sectionId, - 'buttonType' : 'button', - 'buttons' : +
+
+ {{adminltetags.useTag('fields', + [ + 'component' : component, + 'componentName' : componentName, + 'componentId' : componentId, + 'sectionId' : sectionId, + 'fieldId' : 'local_modules', + 'fieldLabel' : 'Manage Local Modules', + 'fieldLabelLegend' : true, + 'fieldHidden' : false, + 'fieldType' : 'html', + 'fieldBazScan' : false, + 'fieldBazPostOnCreate' : false, + 'fieldBazPostOnUpdate' : false, + 'fieldBazJstreeSearch' : true, + 'fieldHtmlContent' : '' + ] + )}} +
+
+
+
+ {% set includeCoreModulesChecked = false %} + {% if includecoremodules is defined and includecoremodules == true %} + {% set includeCoreModulesChecked = true %} + {% endif %} + {{adminltetags.useTag('fields', [ - 'modules-link' : [ - 'title' : 'link', - 'size' : 'xs', - 'disabled' : true, - 'hidden' : true, - 'url' : '#' - ] + 'component' : component, + 'componentName' : componentName, + 'componentId' : componentId, + 'sectionId' : sectionId, + 'fieldId' : 'include_core_modules', + 'fieldLabel' : 'Include Core Modules?', + 'fieldHelp' : true, + 'fieldHelpTooltipContent' : 'Include Core modules in the list of modules?', + 'fieldType' : 'checkbox', + 'fieldCheckboxType' : 'info', + 'fieldCheckboxChecked' : includeCoreModulesChecked, + 'fieldCheckboxAdditionClass' : 'text-sm text-uppercase', + 'fieldBazPostOnCreate' : true, + 'fieldBazPostOnUpdate' : true, + 'fieldBazJstreeSearch' : true, + 'fieldBazScan' : true + ] + )}} +
+
+ {{adminltetags.useTag('buttons', + [ + 'componentId' : componentId, + 'sectionId' : sectionId, + 'buttonType' : 'DropdownSplitButtons', + 'buttonLabel' : false, + 'dropdownButtonId' : 'main', + 'dropdownButtonTitle' : 'Add new', + 'dropdownButtonIcon' : 'plus', + 'dropdownButtonAdditionalClass' : 'text-uppercase', + 'dropdownAlign' : 'right', + 'buttonPosition' : 'right', + 'dropdowns' : + { + 'apptypes' : { + 'title' : 'App Type', + 'type' : 'primary', + 'additionalClass' : 'contentAjaxLink', + 'url' : apptypesURL + }, + 'components' : { + 'title' : 'Component', + 'type' : 'primary', + 'additionalClass' : 'contentAjaxLink', + 'url' : componentsURL + }, + 'packages' : { + 'title' : 'Package', + 'type' : 'primary', + 'additionalClass' : 'contentAjaxLink', + 'url' : packagesURL + }, + 'middlewares' : { + 'title' : 'Middleware', + 'type' : 'primary', + 'additionalClass' : 'contentAjaxLink', + 'url' : middlewaresURL + }, + 'views' : { + 'title' : 'View', + 'type' : 'primary', + 'additionalClass' : 'contentAjaxLink', + 'url' : viewsURL + }, + 'subviews' : { + 'title' : 'Sub View', + 'type' : 'primary', + 'additionalClass' : 'contentAjaxLink', + 'url' : subViewsURL + }, + 'divider' : 'divider', + 'bundles' : { + 'title' : 'Modules Bundle', + 'type' : 'primary', + 'additionalClass' : 'contentAjaxLink', + 'url' : bundlesURL + } + } ] - ] - )}} + )}} +
+
+
+
+ {{adminltetags.useTag('fields', + [ + 'component' : component, + 'componentName' : componentName, + 'componentId' : componentId, + 'sectionId' : sectionId, + 'fieldId' : 'modules', + 'fieldLabel' : 'Select Module', + 'fieldType' : 'select2', + 'fieldHelp' : true, + 'fieldHelpTooltipContent' : 'Module', + 'fieldRequired' : true, + 'fieldBazScan' : true, + 'fieldBazPostOnCreate' : true, + 'fieldBazPostOnUpdate' : true, + 'fieldDataSelect2AddDataAttrFromData' : ['installed', 'is_subview'], + 'fieldDataSelect2Options' : modules, + 'fieldDataSelect2OptionsKey' : 'id', + 'fieldDataSelect2OptionsValue' : 'display_name:app_type|name:app_type', + 'fieldDataSelect2OptionsSelected' : '' + ] + )}} + {{adminltetags.useTag('buttons', + [ + 'component' : component, + 'componentName' : componentName, + 'componentId' : componentId, + 'sectionId' : sectionId, + 'buttonType' : 'button', + 'buttons' : + [ + 'modules-link' : [ + 'title' : 'link', + 'size' : 'xs', + 'disabled' : true, + 'hidden' : true, + 'url' : '#' + ] + ] + ] + )}} +
+
+
+
+ {{adminltetags.useTag('buttons', + [ + 'componentId' : componentId, + 'sectionId' : sectionId, + 'buttonType' : 'button', + 'buttons' : + [ + 'edit-module' : [ + 'title' : 'Edit', + 'size' : 'xs', + 'type' : 'primary', + 'icon' : 'edit', + 'position' : 'left', + 'disabled' : true + ], + 'clone-module' : [ + 'title' : 'Clone', + 'size' : 'xs', + 'type' : 'primary', + 'icon' : 'copy', + 'position' : 'left', + 'disabled' : true + ], + 'remove-module' : [ + 'title' : 'Remove', + 'size' : 'xs', + 'type' : 'danger', + 'icon' : 'trash', + 'position' : 'left', + 'disabled' : true + ], + 'generate-new-release' : [ + 'title' : 'Generate New Release', + 'size' : 'xs', + 'type' : 'success', + 'position' : 'right', + 'icon' : 'wand-magic-sparkles', + 'disabled' : true + ], + 'commit-bundle-json' : [ + 'title' : 'Commit Bundle Json to repo', + 'size' : 'xs', + 'type' : 'primary', + 'position' : 'right', + 'icon' : 'code', + 'disabled' : true + ] + ] + ] + )}} +
+
-
-
- {{adminltetags.useTag('buttons', - [ - 'componentId' : componentId, - 'sectionId' : sectionId, - 'buttonType' : 'button', - 'buttons' : +
+
+ {{adminltetags.useTag('fields', + [ + 'component' : component, + 'componentName' : componentName, + 'componentId' : componentId, + 'sectionId' : sectionId, + 'fieldId' : 'remote_modules', + 'fieldLabel' : 'Manage Remote Modules', + 'fieldLabelLegend' : true, + 'fieldHidden' : false, + 'fieldType' : 'html', + 'fieldBazScan' : false, + 'fieldBazPostOnCreate' : false, + 'fieldBazPostOnUpdate' : false, + 'fieldBazJstreeSearch' : true, + 'fieldHtmlContent' : '' + ] + )}} +
+
+
+
+ {{adminltetags.useTag('fields', + [ + 'component' : component, + 'componentName' : componentName, + 'componentId' : componentId, + 'sectionId' : sectionId, + 'fieldId' : 'api_id', + 'fieldLabel' : 'Remote Repo', + 'fieldType' : 'select2', + 'fieldHelp' : true, + 'fieldHelpTooltipContent' : 'Select repo to manage.', + 'fieldRequired' : true, + 'fieldBazScan' : true, + 'fieldBazPostOnCreate' : false, + 'fieldBazPostOnUpdate' : false, + 'fieldDataSelect2OptionsArray' : true, + 'fieldDataSelect2Options' : apis, + 'fieldDataSelect2OptionsKey' : 'id', + 'fieldDataSelect2OptionsValue' : 'name', + 'fieldDataSelect2OptionsSelected' : '' + ] + )}} +
+
+
+
+ {{adminltetags.useTag('buttons', [ - 'edit-module' : [ - 'title' : 'Edit Module', - 'size' : 'xs', - 'type' : 'primary', - 'icon' : 'edit', - 'position' : 'left', - 'disabled' : true - ], - 'clone-module' : [ - 'title' : 'Clone Module', - 'size' : 'xs', - 'type' : 'primary', - 'icon' : 'copy', - 'position' : 'left', - 'disabled' : true - ], - 'remove-module' : [ - 'title' : 'Remove Module', - 'size' : 'xs', - 'type' : 'danger', - 'icon' : 'trash', - 'position' : 'left', - 'disabled' : true - ], - 'generate-new-release' : [ - 'title' : 'Generate New Release', - 'size' : 'xs', - 'type' : 'success', - 'position' : 'right', - 'icon' : 'wand-magic-sparkles', - 'disabled' : true - ], - 'commit-bundle-json' : [ - 'title' : 'Commit Bundle Json to repo', - 'size' : 'xs', - 'type' : 'primary', - 'position' : 'left', - 'icon' : 'code', - 'disabled' : true - ] + 'componentId' : componentId, + 'sectionId' : sectionId, + 'buttonType' : 'button', + 'buttons' : + [ + 'browse-remote-repo' : [ + 'title' : 'Browse Repo', + 'position' : 'right', + 'disabled' : true + ] + ] ] - ] - )}} + )}} +
+