diff --git a/interface/modules/custom_modules/oe-module-weno/ModuleManagerListener.php b/interface/modules/custom_modules/oe-module-weno/ModuleManagerListener.php index ec2cee1275e..356849fedd9 100644 --- a/interface/modules/custom_modules/oe-module-weno/ModuleManagerListener.php +++ b/interface/modules/custom_modules/oe-module-weno/ModuleManagerListener.php @@ -88,6 +88,13 @@ public static function initListenerSelf(): ModuleManagerListener */ private function install($modId, $currentActionStatus): mixed { + $modService = new ModuleService(); + /* setting the active ui flag here will allow the config button to show + * before enable. This is a good thing because it allows the user to + * configure the module before enabling it. However, if the module is disabled + * this flag is reset by MM. + */ + $modService::setModuleState($modId, '0', '1'); return $currentActionStatus; } @@ -98,12 +105,6 @@ private function install($modId, $currentActionStatus): mixed */ private function preenable($modId, $currentActionStatus): mixed { - $modService = new ModuleService(); - if ($modService->isWenoConfigured()) { - $modService::setModuleState($modId, '0', '0'); - return $currentActionStatus; - } - $modService::setModuleState($modId, '0', '1'); return $currentActionStatus; } @@ -130,7 +131,8 @@ private function enable($modId, $currentActionStatus): mixed */ private function disable($modId, $currentActionStatus): mixed { - ModuleService::setModuleState($modId, '0', '0'); + // allow config button to show before enable. + ModuleService::setModuleState($modId, '0', '1'); return $currentActionStatus; } diff --git a/interface/modules/custom_modules/oe-module-weno/moduleConfig.php b/interface/modules/custom_modules/oe-module-weno/moduleConfig.php index 60deab49f41..66cb56b0cc6 100644 --- a/interface/modules/custom_modules/oe-module-weno/moduleConfig.php +++ b/interface/modules/custom_modules/oe-module-weno/moduleConfig.php @@ -12,9 +12,15 @@ * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 */ +use OpenEMR\Core\ModulesClassLoader; + require_once dirname(__FILE__, 4) . '/globals.php'; -$module_config = 1; -?> +/* required for config before install */ +$classLoader = new ModulesClassLoader($GLOBALS['fileroot']); +$classLoader->registerNamespaceIfNotExists("OpenEMR\\Modules\\WenoModule\\", __DIR__ . DIRECTORY_SEPARATOR . 'src'); - \ No newline at end of file +$module_config = 1; +/* renders in a Laminas created iframe */ +require_once dirname(__FILE__) . '/templates/weno_setup.php'; +exit; diff --git a/interface/modules/custom_modules/oe-module-weno/src/Services/ModuleService.php b/interface/modules/custom_modules/oe-module-weno/src/Services/ModuleService.php index bc8281155e4..28d9f67fded 100644 --- a/interface/modules/custom_modules/oe-module-weno/src/Services/ModuleService.php +++ b/interface/modules/custom_modules/oe-module-weno/src/Services/ModuleService.php @@ -129,7 +129,12 @@ public function isWenoConfigured(): bool $config = $this->getVendorGlobals(); $keys = array_keys($config); foreach ($keys as $key) { - if ($key === 'weno_rx_enable_test') { + if ( + $key === 'weno_rx_enable_test' + || $key === 'weno_secondary_admin_username' + || $key === 'weno_secondary_admin_password' + || $key === 'weno_secondary_encryption_key' + ) { continue; } $value = $GLOBALS[$key] ?? null; @@ -161,7 +166,7 @@ public static function statusPharmacyDownloadReset(): bool /** * @param $modId string|int module id or directory name * @param $flag string|int 1 or 0 to activate or deactivate module. - * @param $flag_ui string|int custom module ui flag to activate or deactivate Manager UI states. + * @param $flag_ui string|int custom flag to activate or deactivate Manager UI button states. * @return array|bool|null */ public static function setModuleState($modId, $flag, $flag_ui): array|bool|null diff --git a/interface/modules/custom_modules/oe-module-weno/src/Services/TransmitProperties.php b/interface/modules/custom_modules/oe-module-weno/src/Services/TransmitProperties.php index 03f39a8b166..5a18289eea7 100644 --- a/interface/modules/custom_modules/oe-module-weno/src/Services/TransmitProperties.php +++ b/interface/modules/custom_modules/oe-module-weno/src/Services/TransmitProperties.php @@ -512,7 +512,7 @@ public function getWenoProviderId($id = null): mixed // get the weno provider id from the user table (weno_prov_id $provider = sqlQuery("SELECT weno_prov_id FROM users WHERE id = ?", [$id]); if (!empty(trim($provider['weno_prov_id'] ?? ''))) { - $doIt = $GLOBALS['weno_provider_uid'] != trim($provider['weno_prov_id']); + $doIt = ($GLOBALS['weno_provider_uid'] ?? '') != trim($provider['weno_prov_id']); if ($doIt) { $GLOBALS['weno_provider_uid'] = trim($provider['weno_prov_id']); $sql = "UPDATE `user_settings` SET `setting_value` = ? WHERE `setting_user` = ? AND `setting_label` = 'global:weno_provider_uid'"; diff --git a/interface/modules/zend_modules/module/Installer/src/Installer/Controller/InstallerController.php b/interface/modules/zend_modules/module/Installer/src/Installer/Controller/InstallerController.php index 51565ad1ee3..4b5dccd19ae 100644 --- a/interface/modules/zend_modules/module/Installer/src/Installer/Controller/InstallerController.php +++ b/interface/modules/zend_modules/module/Installer/src/Installer/Controller/InstallerController.php @@ -9,7 +9,7 @@ * @author Vipin Kumar * @author Remesh Babu S * @author Jerry Padgett - * @copyright Copyright (c) 2020 Jerry Padgett + * @copyright Copyright (c) 2020-2024 Jerry Padgett * @copyright Copyright (c) 2013 Z&H Consultancy Services Private Limited * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 */ @@ -50,6 +50,9 @@ public function __construct(InstModuleTable $installerTable) $this->dbAdapter = $adapter ?? null; } + /** + * @return ViewModel + */ public function nolayout() { // Turn off the layout, i.e. only render the view script. @@ -58,18 +61,21 @@ public function nolayout() return $viewModel; } + /** + * @return ViewModel + */ public function indexAction() { + $this->scanAndRegisterCustomModules(); //get the list of installed and new modules $result = $this->getInstallerTable()->allModules(); - $allModules = array(); foreach ($result as $dataArray) { $mod = new InstModule(); $mod->exchangeArray($dataArray); $mod = $this->makeButtonForSqlAction($mod); $mod = $this->makeButtonForAClAction($mod); - array_push($allModules, $mod); + $allModules[] = $mod; } return new ViewModel(array( @@ -82,6 +88,67 @@ public function indexAction() )); } + /** + * @return void + */ + private function scanAndRegisterCustomModules(): void + { + $baseModuleDir = $GLOBALS['baseModDir']; + $customDir = $GLOBALS['customModDir']; + $zendModDir = $GLOBALS['zendModDir']; + $coreModules = ['Application', 'Acl', 'Installer', 'FHIR', 'PatientFlowBoard']; + $allModules = array(); + + $result = $this->getInstallerTable()->allModules(); + foreach ($result as $dataArray) { + $mod = new InstModule(); + $mod->exchangeArray($dataArray); + $mod = $this->makeButtonForSqlAction($mod); + $mod = $this->makeButtonForAClAction($mod); + $allModules[] = $mod; + } + + $dir_path = $GLOBALS['srcdir'] . "/../$baseModuleDir$customDir/"; + $dp = opendir($dir_path); + $inDirCustom = array(); + for ($i = 0; false != ($file_name = readdir($dp)); $i++) { + if ($file_name != "." && $file_name != ".." && $file_name != "Application" && is_dir($dir_path . $file_name)) { + $inDirCustom[$i] = $file_name; + } + } + /* Laminas directory Unregistered scan */ + $dir_path = $GLOBALS['srcdir'] . "/../$baseModuleDir$zendModDir/module"; + $dp = opendir($dir_path); + $inDirLaminas = array(); + for ($i = 0; false != ($file_name = readdir($dp)); $i++) { + if ($file_name != "." && $file_name != ".." && (!in_array($file_name, $coreModules)) && is_dir($dir_path . "/" . $file_name)) { + $inDirLaminas[$i] = $file_name; + } + } + // do not show registered modules in the unregistered list + if (sizeof($allModules) > 0) { + foreach ($allModules as $modules) { + $key = array_search($modules->modDirectory, $inDirLaminas); + if ($key !== false) { + unset($inDirLaminas[$key]); + continue; + } + $key = array_search($modules->modDirectory, $inDirCustom); + if ($key !== false) { + unset($inDirCustom[$key]); + } + } + } + foreach ($inDirLaminas as $file_name) { + $rel_path = $file_name . "/index.php"; + $status = $this->getInstallerTable()->register($file_name, $rel_path, 0, $zendModDir); + } + foreach ($inDirCustom as $file_name) { + $rel_path = $file_name . "/index.php"; + $status = $this->getInstallerTable()->register($file_name, $rel_path); + } + } + /** * @return Installer\Model\InstModuleTable */ @@ -90,6 +157,9 @@ public function getInstallerTable(): InstModuleTable return $this->InstallerTable; } + /** + * @return void + */ public function registerAction() { if (!AclMain::aclCheckCore('admin', 'manage_modules')) { @@ -126,6 +196,9 @@ public function registerAction() } } + /** + * @return void + */ public function manageAction() { if (!AclMain::aclCheckCore('admin', 'manage_modules')) { @@ -143,8 +216,13 @@ public function manageAction() // cleanup action. if ($modType == InstModuleTable::MODULE_TYPE_CUSTOM) { $status = $this->callModuleAfterAction("pre" . $request->getPost('modAction'), $modId, $dirModule, $status); - if ($status == 'bypass') { - // future use + if ($status == 'bypass_event') { + $output = ""; + if (!empty($div) && is_array($div)) { + $output = implode("
\n", $div); + } + echo json_encode(["status" => 'Success', "output" => $output]); + exit(0); } } if ($request->getPost('modAction') == "enable") { @@ -283,6 +361,9 @@ private function getContent($data) return $string; } + /** + * @return JsonModel + */ public function SaveHooksAction() { $request = $this->getRequest(); @@ -305,6 +386,9 @@ public function SaveHooksAction() return $arr; } + /** + * @return ViewModel + */ public function configureAction() { $request = $this->getRequest(); @@ -377,6 +461,9 @@ public function configureAction() )); } + /** + * @return JsonModel + */ public function saveConfigAction() { $request = $this->getRequest(); @@ -396,6 +483,9 @@ public function saveConfigAction() return $return; } + /** + * @return JsonModel + */ public function DeleteAclAction() { $request = $this->getRequest(); @@ -405,6 +495,9 @@ public function DeleteAclAction() return $arr; } + /** + * @return JsonModel + */ public function DeleteHooksAction() { $request = $this->getRequest(); @@ -414,6 +507,9 @@ public function DeleteHooksAction() return $arr; } + /** + * @return void + */ public function nickNameAction() { $request = $this->getRequest(); @@ -422,6 +518,10 @@ public function nickNameAction() exit(0); } + /** + * @param $modId + * @return false|string + */ function getModuleVersionFromFile($modId) { //SQL version of Module @@ -442,6 +542,11 @@ function getModuleVersionFromFile($modId) return false; } + /** + * @param $modDirectory + * @param $sqldir + * @return array|false + */ public function getFilesForUpgrade($modDirectory, $sqldir) { $ModulePath = $GLOBALS['srcdir'] . "/../" . $GLOBALS['baseModDir'] . "zend_modules/module/" . $modDirectory; @@ -470,6 +575,10 @@ public function getFilesForUpgrade($modDirectory, $sqldir) return $sortVersions; } + /** + * @param InstModule $mod + * @return InstModule + */ public function makeButtonForSqlAction(InstModule $mod) { $dirModule = $this->getInstallerTable()->getRegistryEntry($mod->modId, "mod_directory"); @@ -506,6 +615,10 @@ public function makeButtonForSqlAction(InstModule $mod) return $mod; } + /** + * @param InstModule $mod + * @return InstModule + */ public function makeButtonForACLAction(InstModule $mod) { $dirModule = $this->getInstallerTable()->getRegistryEntry($mod->modId, "mod_directory"); diff --git a/interface/modules/zend_modules/module/Installer/src/Installer/Model/InstModuleTable.php b/interface/modules/zend_modules/module/Installer/src/Installer/Model/InstModuleTable.php index ead8c553c78..8e1a56f0d81 100644 --- a/interface/modules/zend_modules/module/Installer/src/Installer/Model/InstModuleTable.php +++ b/interface/modules/zend_modules/module/Installer/src/Installer/Model/InstModuleTable.php @@ -9,7 +9,7 @@ * @author Vipin Kumar * @author Remesh Babu S * @author Jerry Padgett - * @copyright Copyright (c) 2020 Jerry Padgett + * @copyright Copyright (c) 2020-2024 Jerry Padgett * @copyright Copyright (c) 2013 Z&H Consultancy Services Private Limited * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 */ @@ -246,10 +246,6 @@ public function saveSettings($fieldName, $fieldValue, $moduleId) /** * this will be used to register a module * - * @param unknown_type $directory - * @param unknown_type $rel_path - * @param unknown_type $state - * @param unknown_type $base * @return boolean */ public function register($directory, $rel_path, $state = 0, $base = "custom_modules") diff --git a/interface/modules/zend_modules/module/Installer/view/installer/installer/configure.phtml b/interface/modules/zend_modules/module/Installer/view/installer/installer/configure.phtml index d3fcfd1d869..4b0d390da2d 100644 --- a/interface/modules/zend_modules/module/Installer/view/installer/installer/configure.phtml +++ b/interface/modules/zend_modules/module/Installer/view/installer/installer/configure.phtml @@ -7,13 +7,15 @@ * @author Jacob T.Paul * @author Vipin Kumar * @author Remesh Babu S + * @author Jerry Padgett + * @copyright Copyright (c) 2020-2024 Jerry Padgett * @copyright Copyright (c) 2013 Z&H Consultancy Services Private Limited * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 */ // Warning : PoC code // TBD - Must handle modules in external namespaces -Use OpenEMR\Modules; +use OpenEMR\Modules; $listener = $this->listenerObject; $hangers = $this->hangers; @@ -27,7 +29,7 @@ $customDir = $GLOBALS['customModDir']; $zendModDir = $GLOBALS['zendModDir']; $confirmationMSG = $listener->z_xlt("Do you really want to delete?"); // TODO: Change all of these magic numbers into constants, set them in the view and use them here so we can know what this all means. -if (count($TabSettings) > 0) { ?> +if (count($TabSettings ?? []) > 0) { ?>
0) { @@ -77,7 +79,7 @@ if (count($TabSettings) > 0) { ?> ?> 0) { + if (count($Hooks ?? []) > 0) { foreach ($Hooks as $obj_hooks) { ?> @@ -168,7 +170,7 @@ if (count($TabSettings) > 0) { ?> - 0) { ?> + 0) { ?>
@@ -180,7 +182,7 @@ if (count($TabSettings) > 0) { ?>
0) { ?> +} elseif (count($setup ?? []) > 0) { ?>
0) { ?> } else { // Final check for config // TBD : clsOeModule - $rs_mod = sqlQuery('select * from modules where mod_id=? and (mod_active=1 or mod_ui_active=1)', [$mod_id]); + $rs_mod = sqlQuery('select * from modules where `mod_id` = ?', [$mod_id]); $fConfig = dirname(realpath('.')) . '/custom_modules/' . $rs_mod['mod_directory'] . '/moduleConfig.php'; - if (($rs_mod['type'] == 0) && file_exists($fConfig)) { - ?> + if (($rs_mod['type'] == 0) && file_exists($fConfig)) { ?>
-
+ } else { ?>
z_xlt('No Configuration Defined for this Module'); ?>
- diff --git a/interface/modules/zend_modules/module/Installer/view/installer/installer/index.phtml b/interface/modules/zend_modules/module/Installer/view/installer/installer/index.phtml index 48cab59ac20..70db4eca6d0 100644 --- a/interface/modules/zend_modules/module/Installer/view/installer/installer/index.phtml +++ b/interface/modules/zend_modules/module/Installer/view/installer/installer/index.phtml @@ -7,7 +7,7 @@ * @author Jacob T.Paul * @author Vipin Kumar * @author Jerry Padgett - * @copyright Copyright (c) 2020 Jerry Padgett + * @copyright Copyright (c) 2020-2024 Jerry Padgett * @copyright Copyright (c) 2013 Z&H Consultancy Services Private Limited * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 */ @@ -31,301 +31,321 @@ $depObj = $this->dependencyObject; $coreModules = $this->coreModules ?? []; ?> -
    + + +
    -
    - -
    -
    - - - - - - - - - - - - - - - - - - 0) - foreach ($InstallersAll as $moduleResult) { - if ($moduleResult->modName == 'Acl') continue; - $count++; - ?> - - - - + + + + + + + + + + + + + + + +
    - z_xlt('Registered Modules'); ?> -
    z_xlt('ID'); ?>z_xlt('Module'); ?> z_xlt('Status'); ?> z_xlt('Menu Text'); ?> z_xlt('Nick Name'); ?> z_xlt('Type'); ?> z_xlt('Dependency Modules'); ?> z_xlt('Action'); ?> z_xlt('Config'); ?>
    escapeHtml($count); ?> escapeHtml($moduleResult->modName); ?> - sqlRun == 0) { - ?> - z_xlt('Not Installed'); ?> - modActive == 1) { ?> - z_xlt('Active'); ?> - - z_xlt('Inactive'); ?> - + + +
    + + + + + + + + + + + + + + + + 0) { + foreach ($InstallersAll as $moduleResult) { + if ($moduleResult->modName == 'Acl') { + continue; + } + if ($moduleResult->type == 0) { + continue; + } + $count++; + ?> + + + + - - - - - - + + + + + + - - - - - -
    z_xlt('ID'); ?>z_xlt('Module'); ?> z_xlt('Status'); ?> z_xlt('Menu Text'); ?> z_xlt('Nick Name'); ?> z_xlt('Type'); ?> z_xlt('Dependency Modules'); ?> z_xlt('Action'); ?> z_xlt('Settings'); ?>
    escapeHtml($count); ?> escapeHtml($moduleResult->modName); ?> + sqlRun == 0) { ?> - - escapeHtml($moduleResult->modUiName); ?> - + z_xlt('Registered'); ?> sqlRun == 0) { - ?> - -
    - escapeHtml($moduleResult->modnickname); - } + } elseif ($moduleResult->modActive == 1) { ?> + z_xlt('Active'); ?> + -
    - escapeHtml(($moduleResult->type == 1) ? "Laminas" : "Custom"); ?> - + z_xlt('Inactive'); ?> getDependencyModules($moduleResult->modId); - echo ($depStr <> "") ? $listener->z_xlt($depStr) : "--"; - ?> - - sqlRun == 0) { ?> - - modActive == 1 && $moduleResult->modUiActive == 0) { ?> - - modName != 'Acl') { ?> - - - sql_action == "install") { ?> - - sql_action == "upgrade") { ?> - - - acl_action == "install") { ?> - - acl_action == "upgrade") { ?> - - - - sqlRun == 0) { ?> - -- - modActive == 1) { ?> - - modUiActive == 1 && $moduleResult->type == 0) { ?> - - - modActive == 0 && $moduleResult->type == 0) { ?> + } + ?> + + escapeHtml($moduleResult->modUiName); ?> + + sqlRun == 0) { ?> + +
    + escapeHtml($moduleResult->modnickname); + } + ?> +
    + escapeHtml(($moduleResult->type == 1) ? "Laminas" : "Custom"); ?> + + getDependencyModules($moduleResult->modId); + echo ($depStr <> "") ? $listener->z_xlt($depStr) : "--"; + ?> + + sqlRun == 0) { ?> + + modActive == 1 && $moduleResult->modUiActive == 0) { ?> + + modName != 'Acl') { ?> + + + sql_action == "install") { ?> + + sql_action == "upgrade") { ?> + + + acl_action == "install") { ?> + + acl_action == "upgrade") { ?> + + + + sqlRun == 0) { ?> + + modActive == 1) { ?> + + modUiActive == 1) { ?> - - -- -
    -
    - - - - - - - -
    escapeHtml($count); ?> escapeHtml($moduleResult->modName); ?> + sqlRun == 0) { + ?> + z_xlt('Registered'); ?> + modActive == 1) { ?> + z_xlt('Active'); ?> + + z_xlt('Inactive'); ?> + + + escapeHtml($moduleResult->modUiName); ?> + + sqlRun == 0) { ?> + +
    + escapeHtml($moduleResult->modnickname); + } + ?> +
    + escapeHtml(($moduleResult->type == 1) ? "Laminas" : "Custom"); ?> + + getDependencyModules($moduleResult->modId); + echo ($depStr <> "") ? $listener->z_xlt($depStr) : "--"; + ?> + + sqlRun == 0) { ?> + + modActive == 1 && $moduleResult->modUiActive == 0) { ?> + + modName != 'Acl') { ?> + + + sql_action == "install") { ?> + + sql_action == "upgrade") { ?> + + + acl_action == "install") { ?> + + acl_action == "upgrade") { ?> + + + + sqlRun == 0 && $moduleResult->modActive == 0) { ?> + + modActive == 1) { ?> + + modUiActive == 1 && $moduleResult->type == 0) { ?> + + + modActive == 0 && $moduleResult->type == 0) { ?> + modUiActive == 1) { + // only allow if module requests by setting the mod_ui_active column + // because module will have to deal with namespace setting for called config script. + ?> + + + + + + -- + +
    +
    + + 0) { - ?> +/*if ($slno > 0) { + */ ?> diff --git a/interface/modules/zend_modules/public/js/installer/action.js b/interface/modules/zend_modules/public/js/installer/action.js index 86b12d7f167..bff38c80f32 100644 --- a/interface/modules/zend_modules/public/js/installer/action.js +++ b/interface/modules/zend_modules/public/js/installer/action.js @@ -19,7 +19,7 @@ function register(status, title, name, method, type) { if (data == "Success") { window.location.reload(); } else { - var resultTranslated = js_xl(data); + const resultTranslated = js_xl(data); $('#err').html(resultTranslated.msg).fadeIn().delay(2000).fadeOut(); } } diff --git a/src/Core/AbstractModuleActionListener.php b/src/Core/AbstractModuleActionListener.php index df9d52fe35d..cd869f25d4f 100644 --- a/src/Core/AbstractModuleActionListener.php +++ b/src/Core/AbstractModuleActionListener.php @@ -155,4 +155,23 @@ function getModuleRegistry($modId, $col = '*'): array return $registry; } + + /** + * Set enable/disable module state. + * If the mod_ui_active flag is set to 1, then the module config button + * is allowed in modules disabled state. In this state calling the config + * script will be in the Laminas namespace and not the module namespace. + * So remember to set namespace in the config script. + * + * @param $modId string|int module id or directory name + * @param $flag string|int 1 or 0 to activate or deactivate module. + * @param $flag_ui string|int custom flag to activate or deactivate Manager UI button states. + * @return array|bool|null + */ + public static function setModuleActiveState($modId, $flag, $flag_ui): array|bool|null + { + // set module state. + $sql = "UPDATE `modules` SET `mod_active` = ?, `mod_ui_active` = ? WHERE `mod_id` = ? OR `mod_directory` = ?"; + return sqlQuery($sql, array($flag, $flag_ui, $modId, $modId)); + } } diff --git a/src/Core/ModulesApplication.php b/src/Core/ModulesApplication.php index 22744a53c03..c671d3e107a 100644 --- a/src/Core/ModulesApplication.php +++ b/src/Core/ModulesApplication.php @@ -92,7 +92,7 @@ public static function checkModuleScriptPathForEnabledModule($modType, $webRootP $folderName = strtok($truncatedPath, '/'); if ($folderName !== false) { $resultSet = sqlStatementNoLog($statement = "SELECT mod_name, mod_directory FROM modules " - . " WHERE mod_active = 1 AND type = ? AND mod_directory = ? ", [$type, $folderName]); + . " WHERE (mod_active = 1 OR mod_ui_active = 1) AND type = ? AND mod_directory = ? ", [$type, $folderName]); $row = sqlFetchArray($resultSet); if (empty($row)) { throw new AccessDeniedException("admin", "super", "Access to module path for disabled module is denied");