7 * @link http://www.open-emr.org
8 * @author Brady Miller <brady.g.miller@gmail.com>
9 * @copyright Copyright (c) 2017-2019 Brady Miller <brady.g.miller@gmail.com>
10 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
13 require_once("../globals.php");
15 use OpenEMR\Common\Acl\AclMain
;
16 use OpenEMR\Common\Crypto\CryptoGen
;
17 use OpenEMR\Common\Csrf\CsrfUtils
;
18 use OpenEMR\Common\Logging\EventAuditLogger
;
19 use OpenEMR\Common\Twig\TwigContainer
;
20 use OpenEMR\Core\Header
;
22 if (!AclMain
::aclCheckCore('admin', 'users')) {
23 echo (new TwigContainer(null, $GLOBALS['kernel']))->getTwig()->render('core/unauthorized.html.twig', ['pageTitle' => xl("Logs Viewer")]);
28 if (!CsrfUtils
::verifyCsrfToken($_GET["csrf_token_form"])) {
29 CsrfUtils
::csrfNotVerified();
36 <title
><?php
echo xlt('Logs Viewer'); ?
></title
>
38 <?php Header
::setupHeader(['datetime-picker']); ?
>
47 //function to disable the event type field if the event name is disclosure
48 function eventTypeChange(eventname
)
50 if (eventname
== "disclosure") {
51 document
.theform
.type_event
.disabled
= true;
54 document
.theform
.type_event
.disabled
= false;
58 // VicarePlus :: This invokes the find-patient popup.
59 function sel_patient() {
60 dlgopen('../main/calendar/find_patient_popup.php?pflag=0', '_blank', 500, 400);
63 // VicarePlus :: This is for callback by the find-patient popup.
64 function setpatient(pid
, lname
, fname
, dob
) {
65 var f
= document
.theform
;
66 f
.form_patient
.value
= lname +
', ' + fname
;
67 f
.form_pid
.value
= pid
;
71 <body
class="body_top">
72 <div id
="container_div" class="container">
74 <div
class="col-sm-12">
75 <div
class="clearfix">
76 <h2
><?php
echo xlt('Logs Viewer'); ?
></h2
>
79 </div
><!--end of header div
-->
80 <div
class="container-fluid mb-3">
81 <ul
class="nav nav-pills">
82 <li
class="nav-item" id
='li-main-log'>
83 <a href
='#' class="active nav-link font-weight-bold" id
='main-log-li'><?php
echo xlt('Main Log'); ?
></a
>
85 <li
class="nav-item" id
='li-others-log'>
86 <a href
='#' id
='others-log-li' class="nav-link font-weight-bold"><?php
echo xlt('Other Logs'); ?
></a
>
89 </div
><!--end of nav
-pills div
-->
90 <div
class="row" id
="main-log-div">
91 <div
class="col-sm-12">
92 <div
class="jumbotron jumbotron-fluid py-3">
93 <div
class="col-sm-12 col-md-12 col-lg-12">
97 $start_date = (!empty($_GET["start_date"])) ?
DateTimeToYYYYMMDDHHMMSS($_GET["start_date"]) : date("Y-m-d") . " 00:00";
98 $end_date = (!empty($_GET["end_date"])) ?
DateTimeToYYYYMMDDHHMMSS($_GET["end_date"]) : date("Y-m-d") . " 23:59";
100 * Start date should not be greater than end date - Date Validation
102 if ($start_date > $end_date) {
103 echo "<table><tr class='alert'><td colspan='7'>";
104 echo xlt('Start Date should not be greater than End Date');
105 echo "</td></tr></table>";
109 if (!empty($_GET["form_patient"])) {
110 $form_patient = isset($_GET["form_patient"]) ?
$_GET["form_patient"] : "";
115 $form_user = isset($_REQUEST['form_user']) ?
$_REQUEST['form_user'] : '';
116 $form_pid = isset($_REQUEST['form_pid']) ?
$_REQUEST['form_pid'] : '';
118 if (empty($form_patient)) {
122 $res = sqlStatement("select distinct LEFT(date,10) as date from log order by date desc limit 30");
123 for ($iter = 0; $row = sqlFetchArray($res); $iter++
) {
127 // Get the users list.
128 $sqlQuery = "SELECT username, fname, lname FROM users " .
129 "WHERE active = 1 AND ( info IS NULL OR info NOT LIKE '%Inactive%' ) ";
131 $ures = sqlStatement($sqlQuery);
133 $sortby = isset($_GET['sortby']) ?
$_GET['sortby'] : '';
134 $direction = isset($_GET['direction']) ?
$_GET['direction'] : '';
137 <div
class="jumbotron jumbotron-fluid px-3 py-3">
138 <h3
class="text-center"><?php
echo xlt('Main Log'); ?
></h3
>
139 <form method
="get" name
="theform" id
="theform">
140 <input type
="hidden" name
="csrf_token_form" value
="<?php echo attr(CsrfUtils::collectCsrfToken()); ?>" />
141 <input type
="hidden" name
="direction" id
="direction" value
="<?php echo !empty($direction) ? attr($direction) : 'asc'; ?>" />
142 <input type
="hidden" name
="sortby" id
="sortby" value
="<?php echo attr($sortby); ?>" />
143 <input type
=hidden name
="csum" value
="" />
144 <input type
=hidden name
="show" value
="show" />
145 <div
class="form-row">
146 <label
class="col-sm-1 col-form-label" for="start_date"><?php
echo xlt('Start Date'); ?
>:</label
>
147 <div
class="col-sm-3">
148 <input
class="datetimepicker form-control" type
="text" size
="18" name
="start_date" id
="start_date" value
="<?php echo attr(oeFormatDateTime($start_date, 0)); ?>" title
="<?php echo xla('Start Date'); ?>" />
150 <label
class="col-sm-1 col-form-label" for="end_date"><?php
echo xlt('End Date'); ?
>:</label
>
151 <div
class="col-sm-3">
152 <input
class="datetimepicker form-control" type
="text" size
="18" name
="end_date" id
="end_date" value
="<?php echo attr(oeFormatDateTime($end_date, 0)); ?>" title
="<?php echo xla('End Date'); ?>" />
154 <label
class="col-sm-1 col-form-label" for="end_date"><?php
echo xlt('Patient'); ?
>:</label
>
155 <div
class="col-sm-3">
156 <input type
='text' size
='20' class='form-control' name
='form_patient' id
='form_patient' style
='cursor:pointer;' value
='<?php echo (!empty($form_patient)) ? attr($form_patient) : xla('Click To Select
'); ?>' onclick
='sel_patient()' title
='<?php echo xla('Click to select patient
'); ?>' />
157 <input type
='hidden' name
='form_pid' value
='<?php echo attr($form_pid); ?>' />
160 <div
class="form-row">
161 <label
class="col-sm-1 col-form-label" for="form_user"><?php
echo xlt('User'); ?
>:</label
>
162 <div
class="col-sm-3">
163 <select name
='form_user' id
='form_user' class='form-control'>
165 echo " <option value=''>" . xlt('All') . "</option>\n";
166 while ($urow = sqlFetchArray($ures)) {
167 if (empty(trim($urow['username'] ??
''))) {
171 echo " <option value='" . attr($urow['username']) . "'";
172 if ($urow['username'] == $form_user) {
176 echo ">" . text($urow['lname']);
177 if ($urow['fname']) {
178 echo ", " . text($urow['fname']);
187 $eventname = isset($_GET['eventname']) ?
$_GET['eventname'] : '';
188 $res = sqlStatement("select distinct event from log order by event ASC");
189 $ename_list = array();
191 while ($erow = sqlFetchArray($res)) {
192 if (!trim($erow['event'])) {
196 $data = explode('-', $erow['event']);
197 $data_c = count($data);
199 for ($i = 1; $i < ($data_c - 1); $i++
) {
200 $ename .= "-" . $data[$i];
203 $ename_list[$j] = $ename;
206 $res1 = sqlStatement("select distinct event from extended_log order by event ASC");
207 // $j=0; // This can't be right! -- Rod 2013-08-23
208 while ($row = sqlFetchArray($res1)) {
209 if (!trim($row['event'])) {
213 $new_event = explode('-', $row['event']);
214 $no = count($new_event);
215 $events = $new_event[0];
216 for ($i = 1; $i < ($no - 1); $i++
) {
217 $events .= "-" . $new_event[$i];
220 if ($events == "disclosure") {
221 $ename_list[$j] = $events;
226 $ename_list = array_unique($ename_list);
227 $ename_list = array_merge($ename_list);
228 $ecount = count($ename_list);
230 <label
class="col-sm-1 col-form-label" for="form_user"><?php
echo xlt('Name of Events'); ?
>:</label
>
231 <div
class="col-sm-3">
232 <select name
='eventname' id
='eventname' class='form-control' onchange
='eventTypeChange(this.options[this.selectedIndex].value);'>
234 echo " <option value=''>" . xlt('All') . "</option>\n";
235 for ($k = 0; $k < $ecount; $k++
) {
236 echo " <option value='" . attr($ename_list[$k]) . "'";
237 if ($ename_list[$k] == $eventname && $ename_list[$k] != "") {
241 echo ">" . text($ename_list[$k]);
247 <label
class="col-sm-1 col-form-label" for="type_event"><?php
echo xlt('Type of Events'); ?
>:</label
>
248 <div
class="col-sm-3">
250 $type_event = isset($_GET['type_event']) ?
$_GET['type_event'] : '';
251 $event_types = array("select", "update", "insert", "delete", "replace");
252 $lcount = count($event_types);
253 if ($eventname == "disclosure") {
254 echo "<select name='type_event' id='type_event' class='form-control' disabled='disabled'>\n";
255 echo " <option value=''>" . xlt('All') . "</option>\n";
258 echo "<select name='type_event' id='type_event' class='form-control'>\n";
261 echo " <option value=''>" . xlt('All') . "</option>\n";
262 for ($k = 0; $k < $lcount; $k++
) {
263 echo " <option value='" . attr($event_types[$k]) . "'";
264 if ($event_types[$k] == $type_event && $event_types[$k] != "") {
268 echo ">" . text(preg_replace('/^select$/', 'Query', $event_types[$k])); // Convert select to Query for MU2 requirement
275 <input type
="hidden" name
="event" value
="<?php echo attr($event ?? ''); ?>" />
276 <div
class="btn-group" role
="group">
277 <a href
="javascript:document.theform.submit();" class="btn btn-secondary btn-save"><?php
echo xlt('Submit'); ?
></a
>
281 <?php
if (!(!empty($_GET['show']) && ($_GET['show'] = 'show') && $start_date && $end_date && ($err_message != 1))) { ?
>
282 <?php
if (empty($_GET['show']) ||
($_GET['show'] != 'show')) { ?
>
283 <div
class="alert alert-info">
284 <?php
echo xlt("Click the Submit button to display the main log"); ?
>
288 <div
class="table-responsive">
289 <table
class="table table-striped">
291 <th id
="sortby_date" class="sortby" title
="<?php echo xla('Sort by date/time'); ?>"><?php
echo xlt('Date'); ?
></th
>
292 <th id
="sortby_event" class="sortby" title
="<?php echo xla('Sort by Event'); ?>"><?php
echo xlt('Event'); ?
></th
>
293 <th id
="sortby_category" class="sortby" title
="<?php echo xla('Sort by Category'); ?>"><?php
echo xlt('Category'); ?
></th
>
294 <th id
="sortby_user" class="sortby" title
="<?php echo xla('Sort by User'); ?>"><?php
echo xlt('User'); ?
></th
>
295 <th id
="sortby_cuser" class="sortby" title
="<?php echo xla('Sort by Crt User'); ?>"><?php
echo xlt('Certificate User'); ?
></th
>
296 <th id
="sortby_group" class="sortby" title
="<?php echo xla('Sort by Group'); ?>"><?php
echo xlt('Group'); ?
></th
>
297 <th id
="sortby_pid" class="sortby" title
="<?php echo xla('Sort by PatientID'); ?>"><?php
echo xlt('Patient ID'); ?
></th
>
298 <th id
="sortby_success" class="sortby" title
="<?php echo xla('Sort by Success'); ?>"><?php
echo xlt('Success'); ?
></th
>
299 <th title
="<?php echo xla('API logging'); ?>"><?php
echo xlt('API logging'); ?
></th
>
300 <th id
="sortby_comments" class="sortby" title
="<?php echo xla('Sort by Comments'); ?>"><?php
echo xlt('Comments'); ?
></th
>
304 <input type
="hidden" name
="event" value
="<?php echo attr($eventname) . "-" . attr($type_event) ?>" />
309 if ($eventname != "" && $type_event != "") {
310 $getevent = $eventname . "-" . $type_event;
313 if (($eventname == "") && ($type_event != "")) {
314 $tevent = $type_event;
315 } elseif ($type_event == "" && $eventname != "") {
317 } elseif ($eventname == "") {
323 if ($ret = EventAuditLogger
::instance()->getEvents(array('sdate' => $start_date,'edate' => $end_date, 'user' => $form_user, 'patient' => $form_pid, 'sortby' => $_GET['sortby'], 'levent' => $gev, 'tevent' => $tevent,'direction' => $_GET['direction']))) {
324 // Set up crypto object (object will increase performance since caches used keys)
325 $cryptoGen = new CryptoGen();
327 while ($iter = sqlFetchArray($ret)) {
328 if (empty($iter['id'])) {
329 //skip empty log items (this means they were deleted and will show up as deleted in the audit log tamper script)
334 $patterns = array ('/^success/','/^failure/','/ encounter/');
335 $replace = array ( xl('success'), xl('failure'), xl('encounter', '', ' '));
337 if (!empty($iter['encrypt'])) {
338 $commentEncrStatus = $iter['encrypt'];
340 $commentEncrStatus = "No";
342 if (!empty($iter['version'])) {
343 $encryptVersion = $iter['version'];
348 // Decrypt comment data if encrypted
349 if ($commentEncrStatus == "Yes") {
350 if ($encryptVersion >= 3) {
351 // Use new openssl method
352 if (extension_loaded('openssl')) {
353 $trans_comments = $cryptoGen->decryptStandard($iter["comments"]);
354 if ($trans_comments !== false) {
355 $trans_comments = preg_replace($patterns, $replace, $trans_comments);
357 $trans_comments = xl("Unable to decrypt these comments since decryption failed.");
360 $trans_comments = xl("Unable to decrypt these comments since the PHP openssl module is not installed.");
362 } elseif ($encryptVersion == 2) {
363 // Use new openssl method
364 if (extension_loaded('openssl')) {
365 $trans_comments = $cryptoGen->aes256DecryptTwo($iter["comments"]);
366 if ($trans_comments !== false) {
367 $trans_comments = preg_replace($patterns, $replace, $trans_comments);
369 $trans_comments = xl("Unable to decrypt these comments since decryption failed.");
372 $trans_comments = xl("Unable to decrypt these comments since the PHP openssl module is not installed.");
374 } elseif ($encryptVersion == 1) {
375 // Use new openssl method
376 if (extension_loaded('openssl')) {
377 $trans_comments = preg_replace($patterns, $replace, $cryptoGen->aes256DecryptOne($iter["comments"]));
379 $trans_comments = xl("Unable to decrypt these comments since the PHP openssl module is not installed.");
381 } else { //$encryptVersion == 0
382 // Use old mcrypt method
383 if (extension_loaded('mcrypt')) {
384 $trans_comments = preg_replace($patterns, $replace, $cryptoGen->aes256Decrypt_mycrypt($iter["comments"]));
386 $trans_comments = xl("Unable to decrypt these comments since the PHP mycrypt module is not installed.");
390 // base64 decode if applicable (note the $encryptVersion is a misnomer here, we have added in base64 encoding
391 // of comments in OpenEMR 6.0.0 and greater when the comments are not encrypted since they hold binary (uuid) elements)
392 if ($encryptVersion >= 4) {
393 $iter["comments"] = base64_decode($iter["comments"]);
395 $trans_comments = preg_replace($patterns, $replace, $iter["comments"]);
399 <td
><?php
echo text(oeFormatDateTime($iter["date"])); ?
></td
>
400 <td
><?php
echo text(preg_replace('/select$/', 'Query', $iter["event"])); //Convert select term to Query for MU2 requirements ?></td>
401 <td
><?php
echo text($iter["category"]); ?
></td
>
402 <td
><?php
echo text($iter["user"]); ?
></td
>
403 <td
><?php
echo text($iter["crt_user"]); ?
></td
>
404 <td
><?php
echo text($iter["groupname"]); ?
></td
>
405 <td
><?php
echo text($iter["patient_id"]); ?
></td
>
406 <td
><?php
echo text($iter["success"]); ?
></td
>
407 <?php
if (!empty($iter["ip_address"])) { ?
>
408 <td
><?php
echo text($iter["ip_address"]) . ", " . text($iter["method"]) . ", " . text($iter["request"]); ?
></td
>
413 // Convert select term to Query for MU2 requirements
414 // Also using mb_convert_encoding to change binary stuff (uuid) to just be '?' characters
415 echo nl2br(text(preg_replace('/^select/i', 'Query', mb_convert_encoding($trans_comments, 'UTF-8', 'UTF-8'))));
424 if (($eventname == "disclosure") ||
($gev == "")) {
425 $eventname = "disclosure";
426 if ($ret = EventAuditLogger
::instance()->getEvents(array('sdate' => $start_date,'edate' => $end_date, 'user' => $form_user, 'patient' => $form_pid, 'sortby' => $_GET['sortby'], 'event' => $eventname))) {
427 while ($iter = sqlFetchArray($ret)) {
428 $comments = xl('Recipient Name') . ":" . $iter["recipient"] . ";" . xl('Disclosure Info') . ":" . $iter["description"];
431 <td
><?php
echo text(oeFormatDateTime($iter["date"])); ?
></td
>
432 <td
><?php
echo xlt($iter["event"]); ?
></td
>
433 <td
><?php
echo xlt($iter["category"] ??
''); ?
></td
>
434 <td
><?php
echo text($iter["user"]); ?
></td
>
435 <td
><?php
echo text($iter["crt_user"] ??
''); ?
></td
>
436 <td
><?php
echo text($iter["groupname"] ??
''); ?
></td
>
437 <td
><?php
echo text($iter["patient_id"]); ?
></td
>
438 <td
><?php
echo text($iter["success"] ??
''); ?
></td
>
440 <td
><?php
echo text($comments); ?
></td
>
454 </div
><!--end of main log div
-->
456 <div
class="row oe-display" id
="other-logs-div">
457 <div
class="col-sm-12">
458 <div
class="jumbotron jumbotron-fluid py-3">
459 <div
class="col-sm-12 col-md-12 col-lg-12">
461 <h3
class="text-center"><?php
echo xlt('Other Logs'); ?
></h3
>
462 <div
class="btn-group">
463 <a href
='#' id
='view-billing-log-link' class='btn btn-secondary' title
='<?php echo xla('See messages from the last set of generated claims
'); ?>'><?php
echo xlt('Billing Log'); ?
></a
>
464 <a href
='#' id
='view-couchdb-log-link' class='btn btn-secondary' title
='<?php echo xla('See couchdb error log
'); ?>'><?php
echo xlt('CouchDB Error Log'); ?
></a
>
470 </div
><!--end of others log div
-->
474 // jQuery stuff to make the page a little easier to use
476 $
("#other-logs-div").hide();
477 $
("#main-log-li").click(function(){
478 $
("#main-log-div").show(250);
479 $
("#other-logs-div").hide(250);
480 $
("#main-log-li").addClass("active");
481 $
("#others-log-li").removeClass("active");
484 $
("#others-log-li").click(function(){
485 $
("#other-logs-div").show(250);
486 $
("#main-log-div").hide(250);
487 $
("#others-log-li").addClass("active");
488 $
("#main-log-li").removeClass("active");
492 $
("#view-billing-log-link").click( function() {
493 top
.restoreSession();
494 dlgopen('../billing/customize_log.php', '_blank', 500, 400);
497 $
("#view-couchdb-log-link").click( function() {
498 top
.restoreSession();
499 dlgopen('../couchdb/couchdb_log.php', '_blank', 500, 400);
502 // click-able column headers to sort the list
504 $
("#sortby_date").click(function() { set_sort_direction(); $
("#sortby").val("date"); $
("#theform").submit(); });
505 $
("#sortby_event").click(function() { set_sort_direction(); $
("#sortby").val("event"); $
("#theform").submit(); });
506 $
("#sortby_category").click(function() { set_sort_direction(); $
("#sortby").val("category"); $
("#theform").submit(); });
507 $
("#sortby_user").click(function() { set_sort_direction(); $
("#sortby").val("user"); $
("#theform").submit(); });
508 $
("#sortby_cuser").click(function() { set_sort_direction(); $
("#sortby").val("user"); $
("#theform").submit(); });
509 $
("#sortby_group").click(function() { set_sort_direction(); $
("#sortby").val("groupname"); $
("#theform").submit(); });
510 $
("#sortby_pid").click(function() { set_sort_direction(); $
("#sortby").val("patient_id"); $
("#theform").submit(); });
511 $
("#sortby_success").click(function() { set_sort_direction(); $
("#sortby").val("success"); $
("#theform").submit(); });
512 $
("#sortby_comments").click(function() { set_sort_direction(); $
("#sortby").val("comments"); $
("#theform").submit(); });
514 $
('.datetimepicker').datetimepicker({
515 <?php
$datetimepicker_timepicker = true; ?
>
516 <?php
$datetimepicker_showseconds = false; ?
>
517 <?php
$datetimepicker_formatInput = true; ?
>
518 <?php
require($GLOBALS['srcdir'] . '/js/xl/jquery-datetimepicker-2-5-4.js.php'); ?
>
519 <?php
// can add any additional javascript settings to datetimepicker here; need to prepend first setting with a comma ?>
523 function set_sort_direction(){
524 if($
('#direction').val() == 'asc')
525 $
('#direction').val('desc');
527 $
('#direction').val('asc');