Bug Fixes, track anything implementation. (#6985)
[openemr.git] / interface / logview / logview.php
blob9870290ba079423f9835f624eb95979720134daa
1 <?php
3 /**
4 * Log Viewer.
6 * @package OpenEMR
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")]);
24 exit;
27 if (!empty($_GET)) {
28 if (!CsrfUtils::verifyCsrfToken($_GET["csrf_token_form"])) {
29 CsrfUtils::csrfNotVerified();
34 <html>
35 <head>
36 <title><?php echo xlt('Logs Viewer'); ?></title>
38 <?php Header::setupHeader(['datetime-picker']); ?>
40 <style>
41 .sortby {
42 cursor: pointer;
44 </style>
46 <script>
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;
53 else {
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;
69 </script>
70 </head>
71 <body class="body_top">
72 <div id="container_div" class="container">
73 <div class="row">
74 <div class="col-sm-12">
75 <div class="clearfix">
76 <h2><?php echo xlt('Logs Viewer'); ?></h2>
77 </div>
78 </div>
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>
84 </li>
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>
87 </li>
88 </ul>
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">
94 <?php
95 $err_message = 0;
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>";
106 $err_message = 1;
109 if (!empty($_GET["form_patient"])) {
110 $form_patient = isset($_GET["form_patient"]) ? $_GET["form_patient"] : "";
114 <?php
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)) {
119 $form_pid = '';
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++) {
124 $ret[$iter] = $row;
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'); ?>" />
149 </div>
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'); ?>" />
153 </div>
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); ?>' />
158 </div>
159 </div>
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'>
164 <?php
165 echo " <option value=''>" . xlt('All') . "</option>\n";
166 while ($urow = sqlFetchArray($ures)) {
167 if (empty(trim($urow['username'] ?? ''))) {
168 continue;
171 echo " <option value='" . attr($urow['username']) . "'";
172 if ($urow['username'] == $form_user) {
173 echo " selected";
176 echo ">" . text($urow['lname']);
177 if ($urow['fname']) {
178 echo ", " . text($urow['fname']);
181 echo "</option>\n";
184 </select>
185 </div>
186 <?php
187 $eventname = isset($_GET['eventname']) ? $_GET['eventname'] : '';
188 $res = sqlStatement("select distinct event from log order by event ASC");
189 $ename_list = array();
190 $j = 0;
191 while ($erow = sqlFetchArray($res)) {
192 if (!trim($erow['event'])) {
193 continue;
196 $data = explode('-', $erow['event']);
197 $data_c = count($data);
198 $ename = $data[0];
199 for ($i = 1; $i < ($data_c - 1); $i++) {
200 $ename .= "-" . $data[$i];
203 $ename_list[$j] = $ename;
204 $j = $j + 1;
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'])) {
210 continue;
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;
224 $j = $j + 1;
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);'>
233 <?php
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] != "") {
238 echo " selected";
241 echo ">" . text($ename_list[$k]);
242 echo "</option>\n";
245 </select>
246 </div>
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">
249 <?php
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";
256 echo "</option>\n";
257 } else {
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] != "") {
265 echo " selected";
268 echo ">" . text(preg_replace('/^select$/', 'Query', $event_types[$k])); // Convert select to Query for MU2 requirement
269 echo "</option>\n";
272 </select>
273 </div>
274 </div>
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>
278 </div>
279 </form>
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"); ?>
285 </div>
286 <?php } ?>
287 <?php } else { ?>
288 <div class="table-responsive">
289 <table class="table table-striped">
290 <tr>
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>
301 </tr>
302 <?php
304 <input type="hidden" name="event" value="<?php echo attr($eventname) . "-" . attr($type_event) ?>" />
305 <?php
307 $tevent = "";
308 $gev = "";
309 if ($eventname != "" && $type_event != "") {
310 $getevent = $eventname . "-" . $type_event;
313 if (($eventname == "") && ($type_event != "")) {
314 $tevent = $type_event;
315 } elseif ($type_event == "" && $eventname != "") {
316 $gev = $eventname;
317 } elseif ($eventname == "") {
318 $gev = "";
319 } else {
320 $gev = $getevent;
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)
330 continue;
333 //translate comments
334 $patterns = array ('/^success/','/^failure/','/ encounter/');
335 $replace = array ( xl('success'), xl('failure'), xl('encounter', '', ' '));
337 if (!empty($iter['encrypt'])) {
338 $commentEncrStatus = $iter['encrypt'];
339 } else {
340 $commentEncrStatus = "No";
342 if (!empty($iter['version'])) {
343 $encryptVersion = $iter['version'];
344 } else {
345 $encryptVersion = 0;
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);
356 } else {
357 $trans_comments = xl("Unable to decrypt these comments since decryption failed.");
359 } else {
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);
368 } else {
369 $trans_comments = xl("Unable to decrypt these comments since decryption failed.");
371 } else {
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"]));
378 } else {
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"]));
385 } else {
386 $trans_comments = xl("Unable to decrypt these comments since the PHP mycrypt module is not installed.");
389 } else {
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"]);
398 <tr>
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>
409 <?php } else { ?>
410 <td> </td>
411 <?php } ?>
412 <td><?php
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'))));
417 </td>
418 </tr>
420 <?php
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"];
430 <tr>
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>
439 <td> </td>
440 <td><?php echo text($comments); ?></td>
441 </tr>
442 <?php
447 </table>
448 </div>
450 <?php } ?>
451 </div>
452 </div>
453 </div>
454 </div><!--end of main log div-->
455 </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">
460 <div class="col-lg">
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>
465 </div>
466 </div>
467 </div>
468 </div>
469 </div>
470 </div><!--end of others log div-->
472 <script>
474 // jQuery stuff to make the page a little easier to use
475 $(function () {
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");
491 // billing log modal
492 $("#view-billing-log-link").click( function() {
493 top.restoreSession();
494 dlgopen('../billing/customize_log.php', '_blank', 500, 400);
496 // couchdb log modal
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
503 $('.sortby')
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');
526 else
527 $('#direction').val('asc');
529 </script>
531 </body>
532 </html>