CMS Portal Modification for CF7

cravaus wrote on Tuesday, November 03, 2015:

I am in the process of modifying the CMS Portal to work with CF7 rather than Ninja Forms. I have made some progresss and am hitting a wall, so perhaps someone can point me in the right direction.

What is Required:

In /openemr/interface/cmsportal/upload_form.php line 46 I changed intval to floatval:
$postid = empty($_REQUEST[‘postid’ ]) ? 0 : floatval($_REQUEST[‘postid’ ]);

In WordPress
Contact Form 7 plugin
Contact Form DB plugin (currently configured to just work with CF7)
Forms must be made in CF7 with fields that match openemr fields. I just copied from Rod’s Jinja Forms.

In wordpress/wp-content/plugins/sunset-patient-portal/webserve.php

I deleted the following lines 14-24

$tmp  = $wpdb->prefix . "nf_objects";
$tmp2 = $wpdb->prefix . "ninja_forms";
if($wpdb->get_var("SHOW TABLES LIKE '$tmp2'") != $tmp2) {
  define('FORMS_METHOD', 'NINJA3');
}
else if($wpdb->get_var("SHOW TABLES LIKE '$tmp'") == $tmp) {
  define('FORMS_METHOD', 'NINJA2');
}
else {
  define('FORMS_METHOD', 'NINJA');
}

I modified the action_list function replacing all the Ninja Forms code with this:

function action_list($date_from='', $date_to='') {
  global $wpdb, $out, $admin_user_login;
  $out['list'] = array();
  $out['messages'] = array();
  date_default_timezone_set('UTC');
  $from_date=strtotime($date_from." 00:00:00");
  $to_date=strtotime($date_to." 23:59:59");
	$query =
	"SELECT submit_time, field_value, form_name " .
	"FROM {$wpdb->prefix}cf7dbplugin_submits " .
	"WHERE field_order = 9999";

    $qparms = array();
    if ($date_from) {
      $query .= " AND submit_time >= %d";
      $qparms[] = "$from_date";
    }
    if ($date_to) {
      $query .= " AND submit_time <= %d";
      $qparms[] = "$to_date";
    }
    $query .= " ORDER BY submit_time";

    $query = $wpdb->prepare($query, $qparms);
    if (empty($query)) {
      $out['errmsg'] = "Internal error: wpdb prepare() failed.";
      return;
    }
	
    $rows = $wpdb->get_results($query, ARRAY_A);
    foreach ($rows as $row) {
      $out['list'][] = array(
        'postid'   => $row['submit_time'],
        'user'     => (isset($row['field_value']) ? $row['field_value'] : ''),
        'datetime' => gmdate("Y-m-d H:i:s",$row['submit_time']),
        'type'     => $row['form_name'],
      );
    }

This works.
I did not touch the messaging code.

Now I am working on the action_getpost function. Again deleting all the Ninja code. This is not complete but much of it works:

function action_getpost($postid) {
  global $wpdb, $out;
  $out['post'] = array();
  $out['uploads'] = array();

    // wp_posts has one row for each submitted form.
    // wp_nf_objectmeta includes a set of rows for each defined form.
    $query =
	"SELECT submit_time, form_name, field_value " .
	"FROM {$wpdb->prefix}cf7dbplugin_submits " .
	"WHERE field_order = 9999 AND submit_time = %f";

    $queryp = $wpdb->prepare($query, $postid);
    if (empty($queryp)) {
      $out['errmsg'] = "Internal error: \"$query\" \"$postid\"";
      return;
    }

    $row = $wpdb->get_row($queryp, ARRAY_A);
    if (empty($row)) {
      $out['errmsg'] = "No rows matching: \"$postid\"";
	  echo $queryp;
      return;
    }
    $formid = $row['submit_time'];
    $out['post'] = array(
      'postid'   => $row['submit_time'],
      'user'     => (isset($row['field_value']) ? $row['field_value'] : ''),
      'datetime' => gmdate("Y-m-d H:i:s",$row['submit_time']),
      'type'     => $row['form_name'],
    );
    $out['fields'] = array();
    $out['labels'] = array();

This all works. The following is not working, field names are not interpreted–

// wp_cf7dbplugin_submits has one row for each defined form field.
    $query2 =	  
		"SELECT field_name, field_value " . 
		"FROM {$wpdb->prefix}cf7dbplugin_submits " .  
		"WHERE field_order < 9999 AND submit_time= %f " . 
		"ORDER BY field_order ";
    $query2p = $wpdb->prepare($query2, $postid);
    $rows = $wpdb->get_results($query2p, ARRAY_A);
    foreach ($rows as $fldrow) {
      $flddata = unserialize($fldrow['field_name']);
      // Report uploads, if any.
      if (isset($flddata['upload_location']) && !empty($fldrow['field_value'])) {
        $selval = unserialize($fldrow['field_value']);
        if (is_array($selval)) { // should always be true
          foreach ($selval as $uparr) {
            if (empty($uparr['upload_id'])) continue;
            $filepath = $uparr['file_path'] . $uparr['file_name'];
            // Put the info into the uploads array.
            $out['uploads'][] = array(
              'filename' => $uparr['user_file_name'],
              'mimetype' => get_mime_type($filepath),
              'id'       => $uparr['upload_id'],
            );
          }
        }
      }
      // Each field that matches with a field name in OpenEMR must have that name in
      // its description text. Normally this is in the form of an HTML comment at the
      // beginning of this text, e.g. "<!-- field_name -->".  The regular expression
      // below picks out the name as the first "word" of the description.
      if (!preg_match('/([a-zA-Z0-9_:]+)/', $flddata['desc_text'], $matches)) continue;
      $fldname = $matches[1];
      if (is_string($fldrow['field_value'])) {
        // Ninja perversely stores values encoded for HTML output.
        $out['fields'][$fldname] = htmlspecialchars_decode($fldrow['field_value'], ENT_QUOTES | ENT_HTML401);
      }
      else {
        $out['fields'][$fldname] = $fldrow['field_value'];
      }
      $out['labels'][$fldname] = $flddata['label'];
    }
}

I know Rod is stripping out all the extra stuff that Ninja puts in their fields (what a mess). Much of this is not necessary. I am not sure what to take out and what to leave in. The CF7 table is very simple. My Query produces this simple data set:

field_name	field_value
lname	    Lasname
fname	    Firstname
mname	    Middlename
email	    email@emailaddress.com
sex	        Male
status	    Married
street	    543 Street St.
city	    Mytown
state	    California
postal_code	92222
phone_home	9994446666
phone_biz	9994446666
phone_cell	9994446666
guardiansname	Mom
contact_relationship	Wife
phone_contact	9994446666
ref_providerID	Referring Doctor

Much more simple than Ninja. But it is not processing as is. That is, the field names are not being interpreted. Any tips would be welcome. At the very least I think, htmlspecialchars_decode($fldrow[‘field_value’], ENT_QUOTES | ENT_HTML401); could go.

cravaus wrote on Tuesday, November 03, 2015:

I thought that perhaps adding the tags may make the rest of the code work such as this:

		"SELECT CONCAT('<!-- ',field_name,' -->') AS field_name, field_value " .
		"FROM {$wpdb->prefix}cf7dbplugin_submits " .  
		"WHERE field_order < 9999 AND submit_time= %f " . 
		"ORDER BY field_order ";

But no.

cravaus wrote on Tuesday, November 03, 2015:

Ok, this works. Sorry for the long rant.

    $query2 =	  
		"SELECT field_name, field_value " .
		"FROM {$wpdb->prefix}cf7dbplugin_submits " .  
		"WHERE field_order < 9999 AND submit_time= %f " . 
		"ORDER BY field_order";
    $query2p = $wpdb->prepare($query2, $postid);
    $rows = $wpdb->get_results($query2p, ARRAY_A);
    foreach ($rows as $fldrow) {
      $flddata = unserialize($fldrow['field_name']);
      // Report uploads, if any.
      if (isset($flddata['upload_location']) && !empty($fldrow['field_value'])) {
        $selval = unserialize($fldrow['field_value']);
        if (is_array($selval)) { // should always be true
          foreach ($selval as $uparr) {
            if (empty($uparr['upload_id'])) continue;
            $filepath = $uparr['file_path'] . $uparr['file_name'];
            // Put the info into the uploads array.
            $out['uploads'][] = array(
              'filename' => $uparr['user_file_name'],
              'mimetype' => get_mime_type($filepath),
              'id'       => $uparr['upload_id'],
            );
          }
        }
      }
      // Each field that matches with a field name in OpenEMR must have that name in
      // its description text. 
      if (is_string($fldrow['field_value'])) {
      $out['fields'][$fldrow['field_name']] = $fldrow['field_value'];
      }
      $out['labels'][$fldrow['field_name']] = $flddata['label'];
    }

Now on to the next step.

cravaus wrote on Wednesday, November 04, 2015:

The delete function was modified thus:

function action_delpost($postid) {
  global $wpdb, $out;
    // If this form instance includes any file uploads, then delete the
    // uploaded files as well as the rows in wp_ninja_forms_uploads.
    action_getpost($postid);
    if ($out['errmsg']) return;

    $result = $wpdb->delete("{$wpdb->prefix}cf7dbplugin_submits",
        array('submit_time' => $postid), array('%f'));
	if ($result) {
		$out = array('errmsg' => '');
	} else {
		$out['errmsg'] = "Delete failed for post '$postid'";
	}
}

In CFDB the file uploads are handled just like any other post and are actully part of the same table with the post so there is no need to have separate handling for delete as in Ninja. Uploading of the files from CFDB is another thing and new functions are needed for this as well as a re-written upload_form.php. But with this CF7 forms with out file upload work in the CMS portal. Data is gathered and transfered just as with Ninja.

cravaus wrote on Wednesday, November 04, 2015:

I am confused by this function:

function action_checkptform($patient, $form)

I am not seeing where it is called. I have ran through all the files looking for checkptform and am finding nothing. Rod’s notes are:
// Logic to process the “checkptform” action to determine if a form is pending for
// the given patient login and form name. If it is its request ID is returned.

I am running the query and comming up with nothing. I am not sure if this function is really being used or what it is actually accomplishing. So, this is hard for me to duplicate. I am not sure what is pending about the form? And I am not sure if I should spend time on it if nothing is really calling it.

I will move on to the file upload function for now.

sunsetsystems wrote on Wednesday, November 04, 2015:

Try this:

$ grep -r checkptform *
interface/forms/LBF/new.php:  $portalres = cms_portal_call(array('action' => 'checkptform', 'form' => $formname, 'patient' => $cmsportal_login));

You’ll find this comment:

// New form and this patient has a portal login and we have not loaded portal data.
// Check if there is portal data pending for this patient and form type.

Rod
http://www.sunsetsystems.com/

cravaus wrote on Wednesday, November 04, 2015:

Thanks. That gets me back on track.

cravaus wrote on Tuesday, December 01, 2015:

I have been side tracked with integrating Easy!Appointments with WordPress to combine Scheduling into Rod’s plug in.

I have made some improvements to Easy!Appointments that make it more useable for clinicians:
https://groups.google.com/d/msg/easy-appointments/ly-ZV68NJHU/MOuj6-mnCQAJ
I have been using this live for over two months now and it works great.

I have also made a little tutorial on how to integrate it with Wordpress using some unfinished tools from the author:
https://groups.google.com/d/msg/easy-appointments/WWGps8g0jvI/YvQDNfZVCQAJ

Now with the appointments database in Wordpress it will be easier to import appointment data into OpenEMR with Rod’s portal. So, I will try to develop code for that.

The first step is to associate WP login with EA user and that project is here
https://groups.google.com/d/msg/easy-appointments/dn7Ff--5NRQ/UKtsMW9XCQAJ
This should not be too difficult.

The next step is to write some code to pull out daily appointments for import into OpenEMR.

I have not finished Contact Form 7 support for LBF due to this side track but that is next. Also a modification is needed to support uploading files with CF7. This should be very easy compared to Ninjaforms. It just requires time and know how. I have little of both but I am motivated. Demographics, insurance, and history all work fine with the above modifications.

cravaus wrote on Wednesday, February 10, 2016:

I think I have success but I have some questions for Rod to be sure.

What is Required to make this work:

In /openemr/interface/cmsportal/upload_form.php line 46 I changed intval to floatval:

$postid = empty($_REQUEST['postid' ]) ? 0 : floatval($_REQUEST['postid' ]);

In WordPress

  • Contact Form 7 plugin
  • Contact Form DB plugin (currently configured to just work with CF7, but it may work with Gravity, or even Ninja if CFDB is set up for those form plugins, Someone else should try that.)

Optional and very useful plugins:

  • Contact Form 7 - Dynamic Text Extension (helpful to auto fil fields with WP user login info).
  • Contact Form 7 Datepicker (themes the date picker)
  • Contact Form 7 Modules: Hidden Fields (useful for programming flexibility)
  • Contact Form 7 Multi-Step Forms (for long forms that need forward and back buttons)

Forms must be made in CF7 with fields that match openemr fields. I just copied from Rod’s Jinja Forms. But I left out the html tags that are required for Ninja. In Rod’s original method you would use for each field, but in this case it is just field_id. An example of what I did for Demographics is here:

In wordpress/wp-content/plugins/sunset-patient-portal/webserve.php. Replace the webserve.php file with my modification here:

ISSUES:
I see the same thing as was noted here
https://sourceforge.net/p/openemr/discussion/202506/thread/b9228bad/#0f86
So it appears that importing of the LBF is only partially implimented in Rod’s plugin. So Rod if you can give me some insight here I would appreciate that.

Another issue: When importing a patient that is not already in the openemr database I get a dialogue with possible matches. But when the patient is in the openemr database there is no dialogue and if I click to open the form It opens to load into what ever patient is active in openemr unless I hit the clear active patient button. Then it will load the form with the correct patient. I am wondering if I missed something or is this the way the code works for now?

Anyway as it is I think it is a success.

cravaus wrote on Friday, February 12, 2016:

I have also modified list_requests.php to automatically load the appropriate patient in OpenEMR when a form is loaded.

cravaus wrote on Friday, February 12, 2016:

Regarding LBF, I do not see any insert function built into the plug in so it appears that we have to enter the data by hand. What would be helpful is to have a download button to download the page as pdf to the patient’s file.

sunsetsystems wrote on Friday, February 12, 2016:

I can’t donate much time to troubleshooting code I didn’t write. It will work better if you can demonstrate any problems or questions using the unmodified CMS portal on one of the demo sites.

Rod
http://www.sunsetsystems.com/

cravaus wrote on Friday, February 12, 2016:

Thanks Rod, I do not expect you to trouble shoot my code.

Regrarding the LBF issue, I am refering to the problem identified in this post:
https://sourceforge.net/p/openemr/discussion/202506/thread/b9228bad/#0f86

I am seeing this as an issue too. There is just no insert button on the OpenEMR side. And, looking at your code from sunsetsystems, I do not find an insert button there either. That is all I am referring to. LBF imports fine into OpenEMR portal but not into the LBF form within the OpenEMR. It is difficult to demonstrate on the demo given that there are no LBF examples there. What I do see is this:

So there is a button to open patient and to go back but not one for insert into the specific LBF within OpenEMR. So, I would like that but I do not expect you to do it. An easy way to start for me is to just capture the page as a PDF to download to the patient’s file. That is what I am thinking at least. It is not the ideal solution. I would rather directly import it into the LBF with a button. But, for now a PDF wiil do and I will figure that out tonight.

Regarding the failure to load the current active patiient I can demonstrate that in the demo:

If the active patient is Jane:

And I open the CMS portal and click on Joe’s insurance information which he wants to update in the portal:

Janes information is on the left side: it will import into Jane and not Joe.

So I made a slight modification that would lode Joe as the active patient before loading the insurance form:

It works. I used portions of your code elsware to make it happen. I am not expectiing you to do anything with that. I really appreciate you providing your code. It is fantastic. The CF7 mod works just as the Ninja did. I am happy with it except for the LBF issue.

sunsetsystems wrote on Friday, February 12, 2016:

Regarding the LBF issue, it’s been a while since I worked on this but I believe you’re supposed to open the patient and then create a new instance of the indicated form in the encounter that you want to put it in. The LBF code in new.php is supposed to notice that there’s a pending portal form of that type and offer to import it.

However in checking this I noticed a necessary piece of code to support that has gotten zapped somewhere along the line. I’ll check into that hopefully this weekend.

Rod

sunsetsystems wrote on Saturday, February 13, 2016:

Craig, please try this fix:

I have not tested it yet but it should take care of the failure to import portal data when a LBF is created with that data pending. You will of course need to open the desired encounter and open the new LBF. Let me know how it goes.

Rod

cravaus wrote on Sunday, February 14, 2016:

Hello Rod,

I have tried the fix and have noticed several things. For your information I am running both the Ninja Forms and the CF7 versions and checking them against eachother. In this post I am just speaking in regard to your Ninja version there is an error in the MySQL within webserve.php line 604 and 627 which reads

  "pm.meta_key = 'form_id' AND " .

It should read:

  "pm.meta_key = '_form_id' AND " .

This is important.

This with your fix then allows the notification box to appear stating:
“The portal has data for this patient and form. Load it now?”

Clicking OK appears to refresh the page but no data is imported. I think that is the intention. Correctme if I am wrong.

Scanning through new.php I do not see any function to import an array of ‘labels’ and ‘fields’ from the CMS portal. I would expect it to be near your patch and the cms_field_to_lbf function, and I would expect to see a foreach loop to make an array of the fields and lables but I do not see that. What I do see is:

 $portalres = cms_portal_call(array('action' => 'getpost', 'postid' => $portalid));

and it is filling with the CMS data as it should. I have varified that. But I do not see any function to move it out into an array to match it and import it to an LBF in OpenEMR. I do see this at about line 472:

        else if ($source == 'E') {
          // Visit attribute, get most recent value as of this visit.
          // Even if the form already exists for this visit it may have a readonly value that only
          // exists in a previous visit and was created from a different form.
          $tmp = sqlQuery("SELECT sa.field_value FROM form_encounter AS e1 " .
            "JOIN form_encounter AS e2 ON " .
            "e2.pid = e1.pid AND (e2.date < e1.date OR (e2.date = e1.date AND e2.encounter <= e1.encounter)) " .
            "JOIN shared_attributes AS sa ON " .
            "sa.pid = e2.pid AND sa.encounter = e2.encounter AND sa.field_id = ?" .
            "WHERE e1.pid = ? AND e1.encounter = ? " .
            "ORDER BY e2.date DESC, e2.encounter DESC LIMIT 1",
            array($field_id, $pid, $encounter));
          if (isset($tmp['field_value'])) $currvalue = $tmp['field_value'];

This collects the LBF form fields within OpenEMR. I would expect a function to match the CMS data against it to load/update but I do not see anything like that. Could it be missing? I think it is. If I am reading wrong let me know.

And if there is no code for this in your other files let me know. I think it is an important feature to have. I can try to build it myself. I am very slow however. You have done most of the work and I see what you have done in the other files. That helps a lot.

sunsetsystems wrote on Monday, February 15, 2016:

Hi Craig,

In interface/forms/LBF/new.php the fix that I gave you is inside a “while” loop that loops once for each form field. Thus cms_field_to_lbf() is called for each field.

Yes the data should be imported when you click OK and I’m pretty sure that was tested at the time. Probably what happened is that Ninja Forms was updated and the tables changed in some way that broke this. This has happened before and will doubtless happen again.

The client that I did this for is not using OpenEMR any more. So the fix will have to wait until someone wants it bad enough to either cover the cost or take care of it themselves. You will doubtless encounter the same issue from time to time in maintaining the CF7 version.

Rod
http://www.sunsetsystems.com/

cravaus wrote on Monday, February 15, 2016:

Ok I get it. I will see what I can do to work it out. I am getting the exact same result with CF7. Fortunately CF7 has had a very similar and simple structure for a very long time but I can see that Ninja does like to mix things up.

cravaus wrote on Monday, February 15, 2016:

One solutiion for the input problem.

Make this change in new.php:

Modify this–

    // Skip current-value fields for the display-only case.
    if (empty($is_lbf)) {
      if ($frow['edit_options'] == 'H')
        echo generate_display_field($frow, $currvalue);
      else
        generate_form_field($frow, $currvalue);
    }

To be this –

    // Skip current-value fields for the display-only case.
    if (empty($is_lbf)) {
      if ($frow['edit_options'] == 'H')
        echo generate_display_field($frow, $currvalue);
      else
		if ($portalid) {
		$currvalue = cms_field_to_lbf($data_type, $field_id, $portalres['fields']);	
		generate_form_field($frow, $currvalue);	
		}else{
        generate_form_field($frow, $currvalue);
		}
    }

So far it works. I have not experienced any conflicts with it yet. I have looked and looked and my amature eyes do not see anything that would do anything similar already existing in the code that would change the input fields based upon selecting the OK at the alert box. The other instance I see of cms_field_to_lbf is in Rod’s patch but is conditional to !$formid && $source == 'F' && $portalres which ends up doing nothing when $source == 'E' which most of my LBF’s are. There may be another way to do this with || in the condition. For now this is working.

cravaus wrote on Monday, February 15, 2016:

I have included all my changes (and Rod’s patch) for new.php here: