From 0621077296e58ad7cca2371f4e1d5b5688a30415 Mon Sep 17 00:00:00 2001 From: Luis Maas Date: Sun, 10 Feb 2013 21:13:34 -0800 Subject: [PATCH] Implement direct message receive and background services manager, take 1. 1. Background Service Manager Framework --Documentation provided in the main library/ajax/execute_background_services.php script. --Report at Administration->Reports->Services->Background Services 2. Direct Message Receive --Service built on top of the Background Service Manager Framework --Can be set/modified in Administration->Globals->Connectors (bottom section) --Log can be viewed at Administration->Reports->Services->Direct Message Log --Added support for importing of documents with no patient mapping. GUI for this is at Miscellaneous->New Documents. --Added support for sending pnotes with no patient mapping. --Added support for service bots to enable storage of documents, sending of messages and accurate logging by services. Miscellaneous mods: Changed the Administration->Services to Administration->Codes in left_nav menu. (to avoid confusion with the Services reports) --- ccr/transmitCCD.php | 104 ++++--- controllers/C_Document.class.php | 9 +- interface/main/left_nav.php | 19 +- interface/main/messages/messages.php | 40 ++- interface/reports/background_services.php | 196 ++++++++++++ interface/reports/direct_message_log.php | 184 ++++++++++++ interface/super/edit_globals.php | 39 +++ library/ajax/execute_background_services.php | 168 +++++++++++ library/classes/Controller.class.php | 3 +- library/classes/Installer.class.php | 65 ++-- library/direct_message_check.inc | 425 +++++++++++++++++++++++++++ library/globals.inc.php | 15 + library/pnotes.inc | 41 ++- sql/4_1_1-to-4_1_2_upgrade.sql | 48 +++ sql/database.sql | 59 ++++ sql/official_additional_users.sql | 3 + templates/documents/general_upload.html | 8 + version.php | 2 +- 18 files changed, 1339 insertions(+), 89 deletions(-) create mode 100644 interface/reports/background_services.php create mode 100644 interface/reports/direct_message_log.php create mode 100644 library/ajax/execute_background_services.php create mode 100644 library/direct_message_check.inc create mode 100644 sql/official_additional_users.sql diff --git a/ccr/transmitCCD.php b/ccr/transmitCCD.php index f9b5d390f..4d42b8f9d 100644 --- a/ccr/transmitCCD.php +++ b/ccr/transmitCCD.php @@ -25,6 +25,7 @@ */ require_once(dirname(__FILE__) . "/../library/log.inc"); +require_once(dirname(__FILE__) . "/../library/sql.inc"); /* * Connect to a phiMail Direct Messaging server and transmit @@ -54,22 +55,22 @@ function transmitCCD($ccd,$recipient,$requested_by) { $fp=@fsockopen($server,$phimail_server['port']); if ($fp===false) return("$config_err 3"); @fwrite($fp,"AUTH $phimail_username $phimail_password\n"); - @fflush($fp); - $ret=@fgets($fp,256); + fflush($fp); + $ret=fgets($fp,256); if($ret!="OK\n") { - @fwrite($fp,"BYE\n"); - @fclose($fp); + fwrite($fp,"BYE\n"); + fclose($fp); return("$config_err 4"); } - @fwrite($fp,"TO $recipient\n"); - @fflush($fp); - $ret=@fgets($fp,256); + fwrite($fp,"TO $recipient\n"); + fflush($fp); + $ret=fgets($fp,256); if($ret!="OK\n") { - @fwrite($fp,"BYE\n"); - @fclose($fp); + fwrite($fp,"BYE\n"); + fclose($fp); return( xl("Delivery is not currently permitted to the specified Direct Address.") ); } - $ret=@fgets($fp,1024); //ignore extra server data + $ret=fgets($fp,1024); //ignore extra server data if($requested_by=="patient") $text_out = xl("Delivery of the attached clinical document was requested by the patient."); @@ -77,61 +78,86 @@ function transmitCCD($ccd,$recipient,$requested_by) { $text_out = xl("A clinical document is attached."); $text_len=strlen($text_out); - @fwrite($fp,"TEXT $text_len\n"); - @fflush($fp); + fwrite($fp,"TEXT $text_len\n"); + fflush($fp); $ret=@fgets($fp,256); if($ret!="BEGIN\n") { - @fwrite($fp,"BYE\n"); - @fclose($fp); + fwrite($fp,"BYE\n"); + fclose($fp); return("$config_err 5"); } - @fwrite($fp,$text_out); - @fflush($fp); + fwrite($fp,$text_out); + fflush($fp); $ret=@fgets($fp,256); if($ret!="OK\n") { - @fwrite($fp,"BYE\n"); - @fclose($fp); + fwrite($fp,"BYE\n"); + fclose($fp); return("$config_err 6"); } $ccd_out=$ccd->saveXml(); $ccd_len=strlen($ccd_out); - @fwrite($fp,"CDA $ccd_len\n"); - @fflush($fp); - $ret=@fgets($fp,256); + fwrite($fp,"CDA $ccd_len\n"); + fflush($fp); + $ret=fgets($fp,256); if($ret!="BEGIN\n") { - @fwrite($fp,"BYE\n"); - @fclose($fp); + fwrite($fp,"BYE\n"); + fclose($fp); return("$config_err 7"); } - @fwrite($fp,$ccd_out); - @fflush($fp); - $ret=@fgets($fp,256); + fwrite($fp,$ccd_out); + fflush($fp); + $ret=fgets($fp,256); if($ret!="OK\n") { - @fwrite($fp,"BYE\n"); - @fclose($fp); + fwrite($fp,"BYE\n"); + fclose($fp); return("$config_err 8"); } - @fwrite($fp,"SEND\n"); - @fflush($fp); - $ret=@fgets($fp,256); - @fwrite($fp,"BYE\n"); - @fclose($fp); + fwrite($fp,"SEND\n"); + fflush($fp); + $ret=fgets($fp,256); + fwrite($fp,"BYE\n"); + fclose($fp); + + if($requested_by=="patient") { + $reqBy="portal-user"; + $sql = "SELECT id FROM users WHERE username='portal-user'"; + if (($r = sqlStatementNoLog($sql)) === FALSE || + ($u = sqlFetchArray($r)) === FALSE) { + $reqID = 1; //default if we don't have a service user + } else { + $reqID = $u['id']; + } - if(substr($ret,5)=="ERROR") + } else { + $reqBy=$_SESSION['authUser']; + $reqID=$_SESSION['authUserID']; + } + + if(substr($ret,5)=="ERROR") { + //log the failure + newEvent("transmit-ccd",$reqBy,$_SESSION['authProvider'],0,$ret,$pid); return( xl("The message could not be sent at this time.")); + } /** * If we get here, the message was successfully sent and the return * value $ret is of the form "QUEUED recipient message-id" which * is suitable for logging. - * */ - if($requested_by=="patient") - newEvent("transmit-ccd","portal-user",$_SESSION['authProvider'],1,$ret,$pid); - else - newEvent("transmit-ccd",$_SESSION['authUser'],$_SESSION['authProvider'],1,$ret,$pid); + $msg_id=explode(" ",trim($ret),4); + if($msg_id[0]!="QUEUED" || !isset($msg_id[2])) { //unexpected response + $ret = "UNEXPECTED RESPONSE: " . $ret; + newEvent("transmit-ccd",$reqBy,$_SESSION['authProvider'],0,$ret,$pid); + return( xl("There was a problem sending the message.")); + } + newEvent("transmit-ccd",$reqBy,$_SESSION['authProvider'],1,$ret,$pid); + $adodb=$GLOBALS['adodb']['db']; + $sql="INSERT INTO direct_message_log (msg_type,msg_id,sender,recipient,status,status_ts,patient_id,user_id) " . + "VALUES ('S', ?, ?, ?, 'S', NOW(), ?, ?)"; + $res=@sqlStatementNoLog($sql,array($msg_id[2],$phimail_username,$recipient,$pid,$reqID)); + return("SUCCESS"); } diff --git a/controllers/C_Document.class.php b/controllers/C_Document.class.php index b5d404970..993c034c1 100644 --- a/controllers/C_Document.class.php +++ b/controllers/C_Document.class.php @@ -56,7 +56,9 @@ class C_Document extends Controller { } //Upload multiple files on single click - function upload_action_process() { + //2013-02-10 EMR Direct: added $non_HTTP_owner to allow storage of Direct Message attachments + //through this mechanism, and is set to the user_id for the background process adding the document + function upload_action_process($non_HTTP_owner=false) { $couchDB = false; $harddisk = false; if($GLOBALS['document_storage_method']==0){ @@ -178,7 +180,8 @@ class C_Document extends Controller { if($harddisk == true){ $uploadSuccess = false; - if(move_uploaded_file($_FILES['file']['tmp_name'][$key],$this->file_path.$fname)){ + $move_cmd = ($non_HTTP_owner ? "rename" : "move_uploaded_file"); + if($move_cmd($_FILES['file']['tmp_name'][$key],$this->file_path.$fname)){ $uploadSuccess = true; } else{ @@ -204,7 +207,7 @@ class C_Document extends Controller { $d->mimetype = $_FILES['file']['type'][$key]; } $d->size = $_FILES['file']['size'][$key]; - $d->owner = $_SESSION['authUserID']; + $d->owner = $non_HTTP_owner ? $non_HTTP_owner : $_SESSION['authUserID']; $sha1Hash = sha1_file( $this->file_path.$fname ); if($couchDB == true){ //Removing the temporary file which is used to create the hash diff --git a/interface/main/left_nav.php b/interface/main/left_nav.php index 3f821480c..99b2a6022 100644 --- a/interface/main/left_nav.php +++ b/interface/main/left_nav.php @@ -372,6 +372,10 @@ function genFindBlock() { // run updater every 60 seconds var repeater = setTimeout("getReminderCount()", 60000); }); + //piggy-back on this repeater to run other background-services + //this is a silent task manager that returns no output + $.post("/library/ajax/execute_background_services.php", + { skip_timeout_reset: "1", ajax: "1" }); } $(document).ready(function (){ @@ -1094,7 +1098,7 @@ if ($GLOBALS['athletic_team']) { - + @@ -1122,6 +1126,7 @@ if ($GLOBALS['athletic_team']) { + @@ -1247,7 +1252,7 @@ if (!empty($reg)) { Practice Settings - Pharmacy... Dec 09,09 .. Visolve ... This replaces empty frame with Pharmacy window if (acl_check('admin', 'practice' )) genMiscLink('RTop','adm','0',xl('Practice'),'../controller.php?practice_settings&pharmacy&action=list'); ?> - + @@ -1380,6 +1385,15 @@ if (!empty($reg)) { ?> + +
  • + +
  • + + @@ -1395,6 +1409,7 @@ if (!empty($reg)) { + diff --git a/interface/main/messages/messages.php b/interface/main/messages/messages.php index c714220d7..335b5b139 100644 --- a/interface/main/messages/messages.php +++ b/interface/main/messages/messages.php @@ -5,14 +5,17 @@ * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. + * + * 2013/02/08 Minor tweaks by EMR Direct to allow integration with Direct messaging */ + //SANITIZE ALL ESCAPES $sanitize_all_escapes=true; //STOP FAKE REGISTER GLOBALS $fake_register_globals=false; -require_once('../../globals.php'); +require_once("../../globals.php"); require_once("$srcdir/pnotes.inc"); require_once("$srcdir/patient.inc"); require_once("$srcdir/acl.inc"); @@ -141,16 +144,20 @@ switch($task) { } } } break; + case "savePatient": case "save" : { // Update alert. $noteid = $_POST['noteid']; $form_message_status = $_POST['form_message_status']; - updatePnoteMessageStatus($noteid,$form_message_status); + $reply_to = $_POST['reply_to']; + if ($task=="save") + updatePnoteMessageStatus($noteid,$form_message_status); + else + updatePnotePatient($noteid,$reply_to); $task = "edit"; $note = $_POST['note']; $title = $_POST['form_note_type']; $assigned_to = $_POST['assigned_to']; - $reply_to = $_POST['reply_to']; } case "edit" : { if ($noteid == "") { @@ -232,7 +239,7 @@ $ures = sqlStatement("SELECT username, fname, lname FROM users " . - + : : @@ -246,7 +253,11 @@ $ures = sqlStatement("SELECT username, fname, lname FROM users " . if ($patientname == '') { $patientname = xl('Click to select'); } ?> - title='' /> + title='' />     : @@ -302,7 +313,7 @@ $(document).ready(function(){ var NewNote = function () { top.restoreSession(); - if (document.forms[0].reply_to.value.length == 0) { + if (document.forms[0].reply_to.value.length == 0 || document.forms[0].reply_to.value == '0') { alert(''); } else if (document.forms[0].assigned_to.value.length == 0) { @@ -338,6 +349,13 @@ $(document).ready(function(){ var f = document.forms[0]; f.form_patient.value = lname + ', ' + fname; f.reply_to.value = pid; + + //used when direct messaging service inserts a pnote with indeterminate patient + //to allow the user to assign the message to a patient. + top.restoreSession(); + $("#task").val("savePatient"); + $("#new_note").submit(); + } // This invokes the find-patient popup. @@ -439,9 +457,13 @@ else { $name .= ", " . $myrow['users_fname']; } $patient = $myrow['pid']; - $patient = $myrow['patient_data_lname']; - if ($myrow['patient_data_fname']) { - $patient .= ", " . $myrow['patient_data_fname']; + if ($patient>0) { + $patient = $myrow['patient_data_lname']; + if ($myrow['patient_data_fname']) { + $patient .= ", " . $myrow['patient_data_fname']; + } + } else { + $patient = "* Patient must be set manually *"; } $count++; echo " diff --git a/interface/reports/background_services.php b/interface/reports/background_services.php new file mode 100644 index 000000000..dc5d5bc20 --- /dev/null +++ b/interface/reports/background_services.php @@ -0,0 +1,196 @@ + + * + * LICENSE: This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see ;. + * + * @package OpenEMR + * @author Brady Miller + * @link http://www.open-emr.org + */ + +//SANITIZE ALL ESCAPES +$sanitize_all_escapes=true; +// + +//STOP FAKE REGISTER GLOBALS +$fake_register_globals=false; +// + +require_once("../globals.php"); +?> + + + + + + + + +<?php echo xlt('Background Services'); ?> + + + + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0)) { ?> + + + + + + + + -1) { ?> + + + + + + 0) ) { ?> + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + +   +
    0) ? xlt("Yes") : xlt("No"); ?>0) ? xlt("Yes") : xlt("No"); ?> 
    +
    + +
    + + + + diff --git a/interface/reports/direct_message_log.php b/interface/reports/direct_message_log.php new file mode 100644 index 000000000..c4df0f13e --- /dev/null +++ b/interface/reports/direct_message_log.php @@ -0,0 +1,184 @@ + + * + * LICENSE: This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see ;. + * + * @package OpenEMR + * @author Brady Miller + * @link http://www.open-emr.org + */ + +//SANITIZE ALL ESCAPES +$sanitize_all_escapes=true; +// + +//STOP FAKE REGISTER GLOBALS +$fake_register_globals=false; +// + +require_once("../globals.php"); +?> + + + + + + + + +<?php echo xlt('Direct Message Log'); ?> + + + + + + + + + + +
    + +
    + + + + +
    +
    + + + +
    +
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
      
    +
    + +
    + + + + diff --git a/interface/super/edit_globals.php b/interface/super/edit_globals.php index 62aa5f2f1..1957913bc 100644 --- a/interface/super/edit_globals.php +++ b/interface/super/edit_globals.php @@ -47,6 +47,44 @@ function checkCreateCDB(){ } return true; } + +/** + * Update background_services table for a specific service following globals save. + * @author EMR Direct + */ +function updateBackgroundService($name,$active,$interval) { + //order important here: next_run change dependent on _old_ value of execute_interval so it comes first + $sql = 'UPDATE background_services SET active=?, ' + . 'next_run = next_run + INTERVAL (? - execute_interval) MINUTE, execute_interval=? WHERE name=?'; + return sqlStatement($sql,array($active,$interval,$interval,$name)); +} + +/** + * Make any necessary changes to background_services table when globals are saved. + * To prevent an unexpected service call during startup or shutdown, follow these rules: + * 1. Any "startup" operations should occur _before_ the updateBackgroundService() call. + * 2. Any "shutdown" operations should occur _after_ the updateBackgroundService() call. If these operations + * would cause errors in a running service call, it would be best to make the shutdown function itself + * a background service that is activated here, does nothing if active=1 or running=1 for the + * parent service, then deactivates itself by setting active=0 when it is done shutting the parent service + * down. This will prevent nonresponsiveness to the user by waiting for a service to finish. + * 3. If any "previous" values for globals are required for startup/shutdown logic, they need to be + * copied to a temp variable before the while($globalsrow...) loop. + * @author EMR Direct + */ +function checkBackgroundServices(){ + //load up any necessary globals + $bgservices = sqlStatement("SELECT gl_name, gl_index, gl_value FROM globals WHERE gl_name IN + ('phimail_enable','phimail_interval')"); + while($globalsrow = sqlFetchArray($bgservices)){ + $GLOBALS[$globalsrow['gl_name']] = $globalsrow['gl_value']; + } + + //Set up phimail service + $phimail_active = $GLOBALS['phimail_enable'] ? '1' : '0'; + $phimail_interval = max(0,(int)$GLOBALS['phimail_interval']); + updateBackgroundService('phimail',$phimail_active,$phimail_interval); +} ?> @@ -132,6 +170,7 @@ if ($_POST['form_save'] && $_GET['mode'] != "user") { } } checkCreateCDB(); + checkBackgroundServices(); echo "