Direct Messaging update: add CCR transmit, more meaningful attachment filenames
authorLuis Maas <lcmaas@emrdirect.com>
Wed, 31 Jul 2013 04:09:20 +0000 (30 21:09 -0700)
committerbradymiller <bradymiller@users.sourceforge.net>
Thu, 1 Aug 2013 04:23:10 +0000 (31 21:23 -0700)
Documentation/Direct_Messaging_README.txt
ccr/createCCR.php
ccr/transmitCCD.php
interface/patient_file/report/patient_report.php
library/globals.inc.php
patients/summary_pat_portal.php

index 13400cb..3e9e696 100644 (file)
@@ -1,5 +1,5 @@
 Direct Messaging with OpenEMR and EMR Direct phiMail(TM)
-Version 1.1, 22 Feb 2013
+Version 1.2, 29 Jul 2013
 
 Purpose:
 To provide a secure method from within OpenEMR for sending/receiving protected health 
@@ -9,17 +9,16 @@ requiring the use of Direct messaging.  (For general information about Direct me
 see http://www.emrdirect.com/about-directed-exchange-and-secure-direct-messaging.html)
 
 IMPORTANT:
-EMR Direct currently considers the OpenEMR Direct Messaging features to be in test-mode 
-and not ready for use with real PHI. Some known limitations include:
+Please be aware of the following limitations when using the OpenEMR Direct Messaging 
+features with PHI in a production environment:
 a. the current code only supports a single shared "group" Direct Address for each OpenEMR 
 installation. Note that this trust model is fully compliant with the Direct Project 
 requirements for Direct messaging, but we may add additional trust models in the future 
 should we determine that doing so would provide a higher degree of interoperability for 
 OpenEMR users.
-b. the current code only sends the CCD XML data that was already available in OpenEMR; 
-these CDA files as currently generated by existing OpenEMR code do not meet the requirements 
-of the MU2 criteria; MU2 does require, however, that other systems be able to receive valid 
-CCD files.
+b. the current code only sends the CCR or CCD XML data that is already available in OpenEMR; 
+these files as currently generated by existing OpenEMR code do not meet the requirements 
+of the MU2 criteria, and the current CCD files do not pass strict CDA validation tests.
 
 Problems Solved:
 1. Patient-initiated transmission of CCDA data from the Report section of the Patient Portal 
@@ -58,8 +57,11 @@ OpenEMR's background service manager will be suppressed. (This would be appropri
 checking is managed through another mechanism, such as a system cron job.)
 4. Specify the OpenEMR user who will receive notification of new incoming Direct messages. 
 Enter the username in the field.
-5. Click the "Save" button.
-6. Confirm that a valid Notification Email Address is set in the Administration::
+5. Optionally check "phiMail Allow CCD Send" and/or "phiMail Allow CCR Send" to enable
+the Transmit feature for these data types. If you do not select at least one of these,
+OpenEMR will operate in a receive only mode.
+6. Click the "Save" button.
+7. Confirm that a valid Notification Email Address is set in the Administration::
 Globals::Notifications tab to receive error notifications from the Direct Messaging service.
 
 Checking the status and history of the Direct Messaging Service in OpenEMR:
@@ -79,12 +81,13 @@ final delivery confirmation for that message.
 How to use the Direct Messaging Features in OpenEMR:
 Sending:
 When the phiMail Direct Messaging service is enabled, an additional "Transmit" button will
-appear in the Continuity of Care Document (CCD) section of the Reports section in both the
-Patient Portal and the Patient pane of the main provider interface. 
+appear in the Continuity of Care Record (CCR) and/or Continuity of Care Document (CCD) block 
+of the Reports section in both the Patient Portal and the Patient pane of the main provider 
+interface. 
 
-To transmit a CCD, first click the "Transmit" button. This will open a small dialog immediately 
-below the button with a form field to enter the intended recipient's Direct Address. Clicking
-"Transmit" again will hide the dialog.
+To transmit a CCR or CCD, first click the "Transmit" button. This will open a small dialog 
+immediately below the button with a form field to enter the intended recipient's Direct Address. 
+Clicking "Transmit" again will hide the dialog.
 
 A Direct Address should have the same form as a regular email address, e.g. 
 jonesclinic@direct.example.com. Enter the address in the field and click the "Send" button 
@@ -92,7 +95,9 @@ immediately to the right of the field. Only a single recipient may be specified
 The Send button will be temporarily disabled while OpenEMR is communicating with the phiMail 
 server. This will only work for properly-configured Direct addresses. Attempts to send to a 
 regular email address or Direct address outside of our test mode "trust sandbox" will fail
-during testing.
+during testing. Production accounts have wide interoperability with other Direct service
+providers. Should you encounter a trust community with which OpenEMR does not interoperate,
+please let us know at support@emrdirect.com.
 
 OpenEMR will then display a status message immediately below the Address field, the 
 success or failure of the message transmission, or an error message. If the message is
index f006fed..fe75a74 100644 (file)
@@ -128,7 +128,7 @@ function createCCR($action,$raw="no",$requested_by=""){
           $e_ccr->appendChild($e_Actors);
           
           if ($action=="generate"){
-               gnrtCCR($ccr,$raw);
+               gnrtCCR($ccr,$raw,$requested_by);
           }
           
           if($action == "viewccd"){
@@ -136,7 +136,7 @@ function createCCR($action,$raw="no",$requested_by=""){
           }
        }
        
-       function gnrtCCR($ccr,$raw="no"){
+       function gnrtCCR($ccr,$raw="no",$requested_by=""){
                global $pid;
 
                $ccr->preserveWhiteSpace = false;
@@ -196,6 +196,13 @@ function createCCR($action,$raw="no",$requested_by=""){
                                displayError(xl("ERROR: Unable to Create Zip Archive."));
                                return;
                        }
+                } 
+
+                else if (substr($raw,0,4)=="send") {
+                   $recipient = trim(stripslashes(substr($raw,5)));
+                   $result=transmitCCD($ccr,$recipient,$requested_by,"CCR");
+                   echo htmlspecialchars($result,ENT_NOQUOTES);
+                   return;
                 }
 
                else {
index 4d42b8f..fae7847 100644 (file)
@@ -26,6 +26,7 @@
 
 require_once(dirname(__FILE__) . "/../library/log.inc");
 require_once(dirname(__FILE__) . "/../library/sql.inc");
+require_once(dirname(__FILE__) . "/../library/patient.inc");
 
 /*
  * Connect to a phiMail Direct Messaging server and transmit
@@ -37,9 +38,23 @@ require_once(dirname(__FILE__) . "/../library/sql.inc");
  * @return string result of operation
  */
 
-function transmitCCD($ccd,$recipient,$requested_by) {
+function transmitCCD($ccd,$recipient,$requested_by,$xml_type="CCD") {
    global $pid;
 
+   //get patient name in Last_First format (used for CCDA filename) and
+   //First Last for the message text.
+   $patientData = getPatientPID(array("pid"=>$pid));
+   if (empty($patientData[0]['lname'])) {
+      $att_filename = "";
+      $patientName2 = "";
+   } else {
+      //spaces are the argument delimiter for the phiMail API calls and must be removed
+      $att_filename = " " . 
+         str_replace(" ", "_", $xml_type . "_" . $patientData[0]['lname'] 
+         . "_" . $patientData[0]['fname']) . ".xml";
+      $patientName2 = $patientData[0]['fname'] . " " . $patientData[0]['lname'];
+   }
+
    $config_err = xl("Direct messaging is currently unavailable.")." EC:";
    if ($GLOBALS['phimail_enable']==false) return("$config_err 1");
    $phimail_server=@parse_url($GLOBALS['phimail_server_address']);
@@ -73,9 +88,11 @@ function transmitCCD($ccd,$recipient,$requested_by) {
    $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.");
+       $text_out = xl("Delivery of the attached clinical document was requested by the patient") . 
+            ($patientName2=="" ? "." : ", " . $patientName2 . ".");
    else
-       $text_out = xl("A clinical document is attached.");
+       $text_out = xl("A clinical document is attached") . 
+            ($patientName2=="" ? "." : " " . xl("for patient") . " " . $patientName2 . ".");
 
    $text_len=strlen($text_out);
    fwrite($fp,"TEXT $text_len\n");
@@ -98,7 +115,7 @@ function transmitCCD($ccd,$recipient,$requested_by) {
    $ccd_out=$ccd->saveXml();
    $ccd_len=strlen($ccd_out);
 
-   fwrite($fp,"CDA $ccd_len\n");
+   fwrite($fp,"ADD " . ($xml_type=="CCR" ? "CCR " : "CDA ") . $ccd_len . $att_filename . "\n");
    fflush($fp);
    $ret=fgets($fp,256);
    if($ret!="BEGIN\n") {
@@ -114,6 +131,7 @@ function transmitCCD($ccd,$recipient,$requested_by) {
        fclose($fp);
        return("$config_err 8");
    }
+
    fwrite($fp,"SEND\n");
    fflush($fp);
    $ret=fgets($fp,256);
@@ -152,7 +170,7 @@ function transmitCCD($ccd,$recipient,$requested_by) {
        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);
+   newEvent("transmit-".$xml_type,$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(), ?, ?)";
index f83cf84..1575c08 100644 (file)
@@ -109,6 +109,26 @@ function show_date_fun(){
 <!-- <input type="button" class="generateCCR_download_h" value="<?php echo xl('Download')." (Hybrid)"; ?>" /> -->
 <input type="button" class="generateCCR_download_p" value="<?php echo xl('Download'); ?>" />
 <!-- <input type="button" class="generateCCR_raw" value="<?php xl('Raw Report','e'); ?>" /> -->
+<?php if ($GLOBALS['phimail_enable']==true && $GLOBALS['phimail_ccr_enable']==true) { ?>
+<input type="button" class="viewCCR_send_dialog" value="<?php echo htmlspecialchars( xl('Transmit', ENT_QUOTES)); ?>" />
+             <br>
+             <div id="ccr_send_dialog" style="display:none" >
+              <br>
+              <table border="0" cellpadding="0" cellspacing="0" >
+               <tr>
+                <td>
+                 <span class='bold'><?php echo htmlspecialchars( xl('Enter Recipient\'s Direct Address'), ENT_NOQUOTES);?>: </span>
+                <input type="text" size="64" name="ccr_send_to" id="ccr_send_to" value="">
+                <input type="hidden" name="ccr_sent_by" id="ccr_sent_by" value="user">
+                <input type="button" class="viewCCR_transmit" value="<?php echo htmlspecialchars( xl('Send', ENT_QUOTES)); ?>" />
+                <div id="ccr_send_result" style="display:none" >
+                 <span class="text" id="ccr_send_message"></span>
+                </div>
+                </td>
+              </tr>
+              </table>
+             </div>
+<?php } ?>
 <hr/>
 <span class='title'><?php xl('Continuity of Care Document (CCD)','e'); ?></span>&nbsp;&nbsp;
 <br/>
@@ -118,7 +138,7 @@ function show_date_fun(){
 <input type="button" class="viewCCD" value="<?php xl('View/Print','e'); ?>" />
 <input type="button" class="viewCCD_download" value="<?php echo htmlspecialchars( xl('Download', ENT_QUOTES)); ?>" />
 <!-- <input type="button" class="viewCCD_raw" value="<?php xl('Raw Report','e'); ?>" /> -->
-<?php if ($GLOBALS['phimail_enable']==true) { ?>
+<?php if ($GLOBALS['phimail_enable']==true && $GLOBALS['phimail_ccd_enable']==true) { ?>
 <input type="button" class="viewCCD_send_dialog" value="<?php echo htmlspecialchars( xl('Transmit', ENT_QUOTES)); ?>" />
              <br>
              <div id="ccd_send_dialog" style="display:none" >
@@ -496,7 +516,45 @@ $(document).ready(function(){
                 raw[0].value = 'pure';
                 $("#ccr_form").submit();
         });
-<?php if ($GLOBALS['phimail_enable']==true) { ?>
+<?php if ($GLOBALS['phimail_enable']==true && $GLOBALS['phimail_ccr_enable']==true) { ?>
+        $(".viewCCR_send_dialog").click(
+        function() {
+                $("#ccr_send_dialog").toggle();
+        });
+        $(".viewCCR_transmit").click(
+        function() {
+                $(".viewCCR_transmit").attr('disabled','disabled');
+                var ccrAction = document.getElementsByName('ccrAction');
+                ccrAction[0].value = 'generate';
+                var ccrRecipient = $("#ccr_send_to").val();
+                var raw = document.getElementsByName('raw');
+                raw[0].value = 'send '+ccrRecipient;
+                if(ccrRecipient=="") {
+                  $("#ccr_send_message").html("<?php
+       echo htmlspecialchars(xl('Please enter a valid Direct Address above.'), ENT_QUOTES);?>");
+                  $("#ccr_send_result").show();
+                } else {
+                  $(".viewCCR_transmit").attr('disabled','disabled');
+                  $("#ccr_send_message").html("<?php
+       echo htmlspecialchars(xl('Working... this may take a minute.'), ENT_QUOTES);?>");
+                  $("#ccr_send_result").show();
+                  var action=$("#ccr_form").attr('action');
+                  $.post(action, {ccrAction:'generate',raw:'send '+ccrRecipient,requested_by:'user'},
+                     function(data) {
+                       if(data=="SUCCESS") {
+                         $("#ccr_send_message").html("<?php
+       echo htmlspecialchars(xl('Your message was submitted for delivery to'), ENT_QUOTES);
+                           ?> "+ccrRecipient);
+                         $("#ccr_send_to").val("");
+                       } else {
+                         $("#ccr_send_message").html(data);
+                       }
+                       $(".viewCCR_transmit").removeAttr('disabled');
+                  });
+                }
+        });
+<?php }
+      if ($GLOBALS['phimail_enable']==true && $GLOBALS['phimail_ccd_enable']==true) { ?>
         $(".viewCCD_send_dialog").click(
         function() {
                 $("#ccd_send_dialog").toggle();
index 184b9e2..017a997 100644 (file)
@@ -1588,6 +1588,20 @@ $GLOBALS_METADATA = array(
       'num',                           // data type
       '5',
       xl('Interval between message checks (set to zero for manual checks only)')
+    ),
+
+    'phimail_ccd_enable' => array(
+      xl('phiMail Allow CCD Send'),
+      'bool',                           // data type
+      '0',
+      xl('Enable phiMail Direct Messaging Service')
+    ),
+
+    'phimail_ccr_enable' => array(
+      xl('phiMail Allow CCR Send'),
+      'bool',                           // data type
+      '0',
+      xl('Enable phiMail Direct Messaging Service')
     )
 
   ),
index 58ac844..98f8cc0 100644 (file)
@@ -274,13 +274,52 @@ $(document).ready(function(){
                 raw[0].value = 'pure';
                 $("#ccr_form").submit();
         });
-<?php if ($GLOBALS['phimail_enable']==true) { ?>
+<?php if ($GLOBALS['phimail_enable']==true && $GLOBALS['phimail_ccr_enable']==true) { ?>
+        $(".viewCCR_send_dialog").click(
+        function() {
+                $("#ccr_send_dialog").toggle();
+        });
+        $(".viewCCR_transmit").click(
+        function() {
+                $(".viewCCR_transmit").attr('disabled','disabled');
+                var ccrAction = document.getElementsByName('ccrAction');
+                ccrAction[0].value = 'generate';
+                var ccrRecipient = $("#ccr_send_to").val();
+                var raw = document.getElementsByName('raw');
+                raw[0].value = 'send '+ccrRecipient;
+                if(ccrRecipient=="") {
+                  $("#ccr_send_message").html("<?php
+       echo htmlspecialchars(xl('Please enter a valid Direct Address above.'), ENT_QUOTES);?>");
+                  $("#ccr_send_result").show();
+                } else {
+                  $(".viewCCR_transmit").attr('disabled','disabled');
+                  $("#ccr_send_message").html("<?php
+       echo htmlspecialchars(xl('Working... this may take a minute.'), ENT_QUOTES);?>");
+                  $("#ccr_send_result").show();
+                  var action=$("#ccr_form").attr('action');
+                  $.post(action, {ccrAction:'generate',raw:'send '+ccrRecipient,requested_by:'patient'},
+                     function(data) {
+                       if(data=="SUCCESS") {
+                         $("#ccr_send_message").html("<?php
+       echo htmlspecialchars(xl('Your message was submitted for delivery to'), ENT_QUOTES);
+                           ?> "+ccrRecipient);
+                         $("#ccr_send_to").val("");
+                       } else {
+                         $("#ccr_send_message").html(data);
+                       }
+                       $(".viewCCR_transmit").removeAttr('disabled');
+                  });
+                }
+        });
+<?php }
+      if ($GLOBALS['phimail_enable']==true && $GLOBALS['phimail_ccd_enable']==true) { ?>
         $(".viewCCD_send_dialog").click(
         function() {
                 $("#ccd_send_dialog").toggle();
         });
         $(".viewCCD_transmit").click(
         function() {
+                $(".viewCCD_transmit").attr('disabled','disabled');
                 var ccrAction = document.getElementsByName('ccrAction');
                 ccrAction[0].value = 'viewccd';
                 var ccdRecipient = $("#ccd_send_to").val();
@@ -406,6 +445,25 @@ $(document).ready(function(){
              <!-- <input type="button" class="generateCCR_download_h" value="<?php echo htmlspecialchars( xl('Download'), ENT_QUOTES); ?>" /> -->
              <input type="button" class="generateCCR_download_p" value="<?php echo htmlspecialchars( xl('Download'), ENT_QUOTES); ?>" />
              <!-- <input type="button" class="generateCCR_raw" value="<?php echo htmlspecialchars( xl('Raw Report'), ENT_QUOTES); ?>" /> -->
+<?php if ($GLOBALS['phimail_enable']==true && $GLOBALS['phimail_ccr_enable']==true) { ?>
+             <input type="button" class="viewCCR_send_dialog" value="<?php echo htmlspecialchars( xl('Transmit', ENT_QUOTES)); ?>" />
+             <br>
+             <div id="ccr_send_dialog" style="display:none" >
+              <br>
+              <table border="0" cellpadding="0" cellspacing="0" >
+               <tr>
+                <td>
+                 <span class='bold'><?php echo htmlspecialchars( xl('Enter Recipient\'s Direct Address'), ENT_NOQUOTES);?>: </span>
+                <input type="text" size="64" name="ccr_send_to" id="ccr_send_to" value="">
+                <input type="button" class="viewCCR_transmit" value="<?php echo htmlspecialchars( xl('Send', ENT_QUOTES)); ?>" />
+                <div id="ccr_send_result" style="display:none" >
+                 <span class="text" id="ccr_send_message"></span>
+                </div>
+                </td>
+              </tr>
+              </table>
+             </div>
+<?php } ?>
              <hr/>
              <span class='text'><b><?php echo htmlspecialchars( xl('Continuity of Care Document (CCD)'), ENT_NOQUOTES); ?></b></span>&nbsp;&nbsp;
              <br/>
@@ -415,7 +473,7 @@ $(document).ready(function(){
              <input type="button" class="viewCCD" value="<?php echo htmlspecialchars( xl('View/Print', ENT_QUOTES)); ?>" />
              <input type="button" class="viewCCD_download" value="<?php echo htmlspecialchars( xl('Download', ENT_QUOTES)); ?>" />
              <!-- <input type="button" class="viewCCD_raw" value="<?php echo htmlspecialchars( xl('Raw Report', ENT_QUOTES)); ?>" /> -->
-<?php if ($GLOBALS['phimail_enable']==true) { ?>
+<?php if ($GLOBALS['phimail_enable']==true && $GLOBALS['phimail_ccd_enable']==true) { ?>
              <input type="button" class="viewCCD_send_dialog" value="<?php echo htmlspecialchars( xl('Transmit', ENT_QUOTES)); ?>" />
              <br>
              <div id="ccd_send_dialog" style="display:none" >