From cff87f8c7e6adc5837bcb3d87cc2fa0de4bad706 Mon Sep 17 00:00:00 2001 From: mdsupport <399047+mdsupport@users.noreply.github.com> Date: Mon, 10 Jan 2022 14:30:23 -0800 Subject: [PATCH] Initial commit --- .gitignore | 9 + LICENSE | 21 +++ README.md | 2 + composer.json | 17 ++ oemods/apps/README.md | 28 ++++ oemods/apps/apps.0.0.sql | 13 ++ ...face.login.login.php.shutdown_function.php | 1 + ...tabs.main_screen.php.shutdown_function.php | 1 + src/DevObj/DevDB.php | 141 ++++++++++++++++ src/DevObj/DevFile.php | 149 +++++++++++++++++ src/DevObj/DevObject.php | 144 ++++++++++++++++ src/DevObj/devobject.create.polyfill.sql | 158 ++++++++++++++++++ src/DevObj/devobject.create.sql | 39 +++++ 13 files changed, 723 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 composer.json create mode 100644 oemods/apps/README.md create mode 100644 oemods/apps/apps.0.0.sql create mode 100644 oemods/apps/interface.login.login.php.shutdown_function.php create mode 100644 oemods/apps/interface.main.tabs.main_screen.php.shutdown_function.php create mode 100644 src/DevObj/DevDB.php create mode 100644 src/DevObj/DevFile.php create mode 100644 src/DevObj/DevObject.php create mode 100644 src/DevObj/devobject.create.polyfill.sql create mode 100644 src/DevObj/devobject.create.sql diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..12ad406 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +composer.phar +/vendor/ + +# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control +# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file +# composer.lock +.settings/org.eclipse.wst.validation.prefs +.project +.buildpath diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9d15d15 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 mdsupport + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..19b27e7 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# mdpub +Additions to OpenEMR diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..7e74ac7 --- /dev/null +++ b/composer.json @@ -0,0 +1,17 @@ +{ + "name" : "mdsupport/mdpub", + "description" : "Changes to standard OpenEMR", + "keywords": ["openemr"], + "type": "library", + "license": "MIT", + + "require" : { + "php" : ">=7.4" + }, + + "autoload" : { + "psr-4" : { + "Mdsupport\\Mdpub\\" : "src/" + } + } +} diff --git a/oemods/apps/README.md b/oemods/apps/README.md new file mode 100644 index 0000000..2b19556 --- /dev/null +++ b/oemods/apps/README.md @@ -0,0 +1,28 @@ +#### Sample Modification - apps +This sample code illustrates modification to standard application using php register_shutdown_function. + +##### Changes to standard code +This project cannot include a patch for standard code. But globals.php should be modified to enable changes using DevObj using a sample fragment listed below : + +``` +// DevObj hook +if (class_exists('Mdsupport\Mdpub\DevObj\DevFile')) { + $objFileScript = new Mdsupport\Mdpub\DevObj\DevFile(); +} +``` +##### Setup +Import apps.0.0.sql which sets up following : +* DevFile entries in dev_obj table for + - _ /interface/login/login.php _ + - _ /interface/main/tabs/main_screen.php _ + - _ /interface/main/calendar/index.php _ + - _ /vendor/mdsupport/oemod/apps/interface.login.login.php.shutdown_function.php _ + - _ /vendor/mdsupport/oemod/apps/interface.main.tabs.main_screen.php.shutdown_function.php _ + + +* Specify shutdown_function components in dev_component table as : + - _ /interface/login/login.php _ ** -> ** /vendor/mdsupport/oemod/apps/interface.login.login.php.shutdown_function.php + - _ /interface/main/tabs/main_screen.php _ ** -> ** _ /vendor/mdsupport/oemod/apps/interface.main.tabs.main_screen.php.shutdown_function.php _ + + +* Specify html_select component in dev_component for building apps select box. diff --git a/oemods/apps/apps.0.0.sql b/oemods/apps/apps.0.0.sql new file mode 100644 index 0000000..675ca77 --- /dev/null +++ b/oemods/apps/apps.0.0.sql @@ -0,0 +1,13 @@ +INSERT INTO `dev_obj`(`obj_type`,`obj_id`,`obj_desc`) VALUES + ('DevFile','/interface/login/login.php','EMR Login') +,('DevFile','/interface/main/tabs/main_screen.php','EMR Main Screen') +,('DevFile','/interface/main/calendar/index.php','EMR Calendar') +,('DevFile','/vendor/mdsupport/oemods/apps/interface.login.login.php.shutdown_function.php','Login shutdown function for apps') +,('DevFile','/vendor/mdsupport/oemods/apps/interface.main.tabs.main_screen.php.shutdown_function.php','Main screen shutdown function for apps') +; + +INSERT INTO `dev_component` (`obj_type`,`obj_id`,`comp_obj_type`,`comp_obj_id`,`comp_type`,`comp_seq`,`comp_json`) VALUES + ('DevFile','/interface/login/login.php','DevFile','/vendor/mdsupport/oemods/apps/interface.login.login.php.shutdown_function.php','resource',0,JSON_OBJECT('type','shutdown_function','fn','inject_apps')) +,('DevFile','/interface/main/tabs/main_screen.php','DevFile','/vendor/mdsupport/oemods/apps/interface.main.tabs.main_screen.php.shutdown_function.php','resource',0,JSON_OBJECT('type','shutdown_function','fn','set_iframe')) +,('DevFile','/vendor/mdsupport/oemods/apps/interface.login.login.php.shutdown_function.php','DevFile','/interface/main/calendar/index.php','resource',1,JSON_OBJECT('type','html_select','id','strUuid','text','obj_desc')) +; diff --git a/oemods/apps/interface.login.login.php.shutdown_function.php b/oemods/apps/interface.login.login.php.shutdown_function.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/oemods/apps/interface.login.login.php.shutdown_function.php @@ -0,0 +1 @@ + +* +* @package Mdpub +* @author MD Support +* @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 + */ + +namespace Mdsupport\Mdpub\DevObj; + +class DevDB +{ + private $adb = null; + + /** + * DevDB constructor caches EMR's connection object + * Constuctors do not permit returning a value. So results of $objInitActs will not be available directly. + * + * @param object $objInitActs - Optional parameter calls method(s) saving additional method calls. + */ + function __construct($objInitActs) + { + // Store properties + // TBD : Remove reliance on OpenEMR $GLOBALS + $this->adb = $GLOBALS['adodb']['db']; + + foreach ($objInitActs as $iniMethod => $iniMethodParams) { + if (method_exists($this, $iniMethod)) { + call_user_func_array($this[$iniMethod], $iniMethodParams); + } + } + } + + private function getExecParams($aaValues, $sqlGlue="=?", $sqlSfx="=?") + { + if ((empty($aaValues)) || (!is_array($aaValues)) || (count($aaValues) == 0)) { + return [ + 'bind' => [], + 'str' => '', + 'params' => false + ]; + } + + $sqlFields = array_keys($aaValues); + $sql = [ + 'bind' => array_values($aaValues), + 'str' => implode($sqlGlue,$sqlFields).$sqlSfx, + 'params' => true + ]; + return $sql; + } + // TBD - implement equivalent of sqlstatement, sqlquery + // Add - Use adodb standard way of create/update records for generic table + + /** + * Execute a sql statement + * Optionally create a dev_obj record with specified keys + * + * @param array $aaQuery - Assoc array as + * sql => single execute statement, + * where => [assoc array of column values], + * bind => [array] for passthru binds, + * sfx => optional string to add after WHERE, + * return => 'array', 'assoc' + * @return - array(of values), object or return value provided by adodb for execute. + */ + function execSql($aaExec) + { + if ( (!is_array($aaExec)) || (empty($aaExec['sql'])) ) return false; + + if (array_key_exists('where', $aaExec)) { + $aaExec = array_merge($aaExec, $this->getExecParams($aaExec['where'], '=? AND ', '=?')); + } elseif (array_key_exists('bind', $aaExec)) { + $aaExec = array_merge($aaExec, [ + 'str' => '', + 'params' => false, + ]); + } + // Define defaults to make sure nothing is undefined + $sqlFrag = [ + '' + ]; + // Construct sql to locate full record as SELECT * FROM tbl WHERE col=? AND col=' + $sqStmt = sprintf( + '%s %s %s', + $aaExec['sql'], + ($sqlFrag['params'] ? 'WHERE '.$sqlFrag['str'] : ''), + $aaExec['sfx'] + ); + + if (empty($aaExec['return'])) { + return $this->adb->execute($sqStmt, $sqlFrag['bind']); + } elseif ($aaExec['return'] == 'array') { + return $this->adb->getArray($sqStmt, $sqlFrag['bind']); + } elseif ($aaExec['return'] == 'assoc') { + return $this->adb->getAssoc($sqStmt, $sqlFrag['bind']); + } + + return false; + } + + /** + * Perform table update + * @param array $aaUpdate - Assoc array as tblName => [where => [assoc array of column values], set => [assoc array of column values]] + * @return boolean true if all updates were successful, false if transaction had to be rolled back. + */ + function execUpdates($aaUpdate) + { + if (!is_array($aaUpdate)) return false; + + $this->adb->beginTrans(); + $transOk = true; + foreach ($aaUpdate as $tbl => $upSpecs) { + if ((!is_array($upSpecs)) + || (empty($upSpecs['where'])) + || (empty($upSpecs['set'])) + || (!is_array($upSpecs['where'])) + || (!is_array($upSpecs['set'])) + || (!$transOk) + ) { + continue; + } + // Construct sql to locate full record as SELECT * FROM tbl WHERE col=? AND col=?' + $rsSel = $this->execSql([ + 'sql' => "SELECT * FROM $tbl", + 'where' => $upSpecs['where'], + ]); + $sqlUpdate = $this->adb->getUpdateSql($rsSel, $upSpecs['set']); + // Helper function will filter out unchanged fields + if (strlen($sqlUpdate)) { + $transOk = $this->adb->execute($sqlUpdate); + } + } + // if $transOk is false, commitTrans should call rollbackTrans + $transOk = $this->adb->commitTrans($transOk); + return $transOk; + } +} diff --git a/src/DevObj/DevFile.php b/src/DevObj/DevFile.php new file mode 100644 index 0000000..4265866 --- /dev/null +++ b/src/DevObj/DevFile.php @@ -0,0 +1,149 @@ + +* +* @package OpenEMR +* @author MD Support +* @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 + */ + +namespace Mdsupport\Mdpub\DevObj; + +class DevFile extends DevObject +{ + private $ws = []; + + /** + * File object constructor + * @param string $strFile - Filename or URL + * @param string $context - Context to apply for the object + */ + function __construct($strFile = null, $context = '', $insert = false) + { + // Store properties + $this->ws['obj_id'] = $strFile; + $this->ws['context'] = $context; + + $strFile = $this->makeObjId($strFile); + // printf('%s will be constructed as %s. ', $this->ws['obj_id'], $strFile); + parent::__construct(null, $strFile, 'DevFile', '', $insert); + + // TBD - Confirm the object exists and is active in storage + // Remove functionality from zsfx.interface.globals.php + + // printf('%s constructed as %s. ', $strFile, print_r($rs, true)); + } + + /** + * Utility function to convert __FILE__ style input to string ready to be used as obj_id + * This includes converting paths in windows environment to unix + * @param string $str_file - Handles strings as full os file path, url + * @return string ready for use as obj_id + */ + private function makeObjId($str_file) + { + // Set default as a call to globals.php(level 0) by main script(level 1) + if (empty($str_file)) { + $call_stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + $str_file = $call_stack[count($call_stack)-1]['file']; + } + $ws = []; + $ws['osmount'] = dirname(__DIR__, 5); + $ws['urlpfx'] = str_replace(dirname(__DIR__, 6), '', $ws['osmount']); + $ws['urlpfx'] = str_replace(DIRECTORY_SEPARATOR, "/", $ws['urlpfx']); + $this->ws = array_merge($this->ws, $ws); + // Strip os or url to application root + foreach ($ws as $pfxtype => $pfx) { + $pfxlen = strlen($pfx); + if (substr($str_file, 0, $pfxlen) == $pfx) { + $str_file = substr($str_file, $pfxlen); + } + } + // Replace with PHP_OS_FAMILY == Windows in 7.2 + if (stripos(PHP_OS, 'WIN') === 0) { + $str_file = str_replace(DIRECTORY_SEPARATOR, "/", $str_file); + } + + return $str_file; + } + + public function debug() + { + // Based on __FILE__, locate emr root + printf('%s will load %s', print_r($this->get(), true), print_r($this->load(), true)); + } + + /** + * Get effective URL for the object + * @return string - For file or empty object types, returns URL without webserver root.
+ * Currently returns false for all other objects. + */ + public function getURL() + { + if (!($this->isActive())) return false; + + $rs = $this->get(); + $obj_id = $rs['obj_id']; + // Check if there is any 'redirect' mapping for this devobject + // TBD : Remove EMR sql function. getComponents returns full records as assoc array + $rs = sqlQuery( + 'SELECT dt.* FROM dev_component dc + INNER JOIN dev_obj dt ON dc.comp_obj_id=dt.obj_id AND dc.comp_obj_version=dt.obj_version + WHERE dc.obj_id=? AND dc.obj_version=? AND dc.comp_type=? AND dt.active=?', + [$rs['obj_id'], $rs['obj_version'], 'redirect', 1] + ); + if ($rs) { + $obj_id = $rs['obj_id']; + } + // ** Experimental : return original directory separator(?) ** + return str_replace($rs['obj_id'], $obj_id, $this->ws['obj_id']); + } + + /** + * Loads component scripts for the file + */ + public function load($comp_type) { + $rs = $this->getComponents($comp_type); + if (!$rs) return false; + + $retHtml = false; + // TBD : Remove EMR sql function. getComponents returns full records as assoc array + while ($recComp = sqlFetchArray($rs)) { + switch ($comp_type) { + case 'script': + $retHtml .= sprintf( + '