Skip to content

Commit

Permalink
Merge pull request #7409 from sjpadgett/cherrypick_sjp_702
Browse files Browse the repository at this point in the history
Cherry pick sjp 702
  • Loading branch information
sjpadgett committed Apr 25, 2024
2 parents 85dd25b + 4c5543f commit 24b7ed2
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 48 deletions.
80 changes: 43 additions & 37 deletions controllers/C_Document.class.php
Expand Up @@ -25,7 +25,6 @@
use OpenEMR\Services\PatientService;
use OpenEMR\Events\PatientDocuments\PatientDocumentTreeViewFilterEvent;
use OpenEMR\Events\PatientDocuments\PatientRetrieveOffsiteDocument;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

class C_Document extends Controller
{
Expand All @@ -41,10 +40,6 @@ class C_Document extends Controller
private $cryptoGen;
private bool $skip_acl_check = false;
private DocumentTemplateService $templateService;
/**
* @var EventDispatcherInterface $eventDispatcher
*/
private $eventDispatcher;

public function __construct($template_mod = "general")
{
Expand Down Expand Up @@ -774,42 +769,53 @@ public function retrieve_action(string $patient_id = null, $document_id, $as_fil
//strip url of protocol handler
$url = preg_replace("|^(.*)://|", "", $url);

//change full path to current webroot. this is for documents that may have
//been moved from a different filesystem and the full path in the database
//is not current. this is also for documents that may of been moved to
//different patients. Note that the path_depth is used to see how far down
//the path to go. For example, originally the path_depth was always 1, which
//only allowed things like documents/1/<file>, but now can have more structured
//directories. For example a path_depth of 2 can give documents/encounters/1/<file>
// etc.
// change full path to current webroot. this is for documents that may have
// been moved from a different filesystem and the full path in the database
// is not current. this is also for documents that may of been moved to
// different patients. Note that the path_depth is used to see how far down
// the path to go. For example, originally the path_depth was always 1, which
// only allowed things like documents/1/<file>, but now can have more structured
// directories. For example a path_depth of 2 can give documents/encounters/1/<file>
// etc.
// NOTE that $from_filename and basename($url) are the same thing
$from_all = explode("/", $url);
$from_filename = array_pop($from_all);
$from_pathname_array = array();
for ($i = 0; $i < $d->get_path_depth(); $i++) {
$from_pathname_array[] = array_pop($from_all);
}
$from_pathname_array = array_reverse($from_pathname_array);
$from_pathname = implode("/", $from_pathname_array);
if ($couch_docid && $couch_revid) {
//for couchDB no URL is available in the table, hence using the foreign_id which is patientID
$temp_url = $GLOBALS['OE_SITE_DIR'] . '/documents/temp/' . $d->get_foreign_id() . '_' . $from_filename;
} else {
$temp_url = $GLOBALS['OE_SITE_DIR'] . '/documents/' . $from_pathname . '/' . $from_filename;
}
// no point in doing any of these checks if $from_filename is empty which can lead to false positives on file_exists
if (!empty($from_filename)) {
$from_pathname_array = array();
for ($i = 0; $i < $d->get_path_depth(); $i++) {
$from_pathname_array[] = array_pop($from_all);
}
$from_pathname_array = array_reverse($from_pathname_array);
$from_pathname = implode("/", $from_pathname_array);
if ($couch_docid && $couch_revid) {
//for couchDB no URL is available in the table, hence using the foreign_id which is patientID
$temp_url = $GLOBALS['OE_SITE_DIR'] . '/documents/temp/' . $d->get_foreign_id() . '_' . $from_filename;
} else {
$temp_url = $GLOBALS['OE_SITE_DIR'] . '/documents/' . $from_pathname . '/' . $from_filename;
}

if (file_exists($temp_url)) {
$url = $temp_url;
}
//fire a remote call to see if the file is stored somewhere else
$s3Key = explode("//", $temp_url); //split the url to get the s3 key
$retrieveOffsiteDocument = new PatientRetrieveOffsiteDocument("/" . $s3Key[1]);
$this->eventDispatcher->dispatch($retrieveOffsiteDocument, PatientRetrieveOffsiteDocument::REMOTE_DOCUMENT_LOCATION);
//this is for the s3 bucket module. If the file is not found locally, it will be found remotely
if ($retrieveOffsiteDocument->getOffsiteUrl() != null) {
header('Content-Description: File Transfer');
header("Location: " . $retrieveOffsiteDocument->getOffsiteUrl());
exit;
if (file_exists($temp_url)) {
$url = $temp_url;
}

$retrieveOffsiteDocument = new PatientRetrieveOffsiteDocument($d->get_url(), $d);
$updatedOffsiteDocumentEvent = $GLOBALS['kernel']->getEventDispatcher()->dispatch(
$retrieveOffsiteDocument,
PatientRetrieveOffsiteDocument::REMOTE_DOCUMENT_LOCATION
);
// if a module writer has an independent offsite storage mechanism used this accomdoates that.
// If the file is not found locally, it will be found remotely. Systems like s3, blob stores, etc, can
// be tied and and use those urls. Note NO security is handled here so any kind of security mechanism must
// be handled on the receiving end's URL (s3/azure for example use signed urls with signature verification)
if (
$updatedOffsiteDocumentEvent instanceof PatientRetrieveOffsiteDocument
&& $updatedOffsiteDocumentEvent->getOffsiteUrl() != null
) {
header('Content-Description: File Transfer');
header("Location: " . $updatedOffsiteDocumentEvent->getOffsiteUrl());
exit;
}
}

if (!file_exists($url)) {
Expand Down
Expand Up @@ -85,7 +85,7 @@ function downloadWenoPrescriptionLog(): void
}

$logSync = new LogProperties();
if (!$logSync->logSync()) {
if (!$logSync->logSync('background')) {
error_log("Background services failed for prescription log.");
}
}
Expand Down
Expand Up @@ -139,7 +139,7 @@ public function logReview()
/**
* @throws Exception
*/
public function logSync()
public function logSync($tasked = 'background')
{
$wenoLog = new WenoLogService();
$provider_info['email'] = $this->weno_admin_email;
Expand Down Expand Up @@ -167,9 +167,20 @@ public function logSync()
if ($isError['is_error']) {
$error = $isError['messageText'];
error_log('Prescription download failed: ' . errorLogEscape($error));
$wenoLog->insertWenoLog("prescription", "Invalid Prescriber Credentials Hint:see previous errors.");
EventAuditLogger::instance()->newEvent("prescriptions_log", $_SESSION['authUser'], $_SESSION['authProvider'], 0, ($error));
die(js_escape($error));
// if background task then return false
if ($tasked == 'background') {
$wenoLog->insertWenoLog("prescription", "Invalid Prescriber Credentials Background Service.");
return false;
}
if ($tasked == 'downloadLog') {
$wenoLog->insertWenoLog("prescription", "Invalid Prescriber Credentials User Download.");
echo(js_escape($error));
exit;
}
$wenoLog->insertWenoLog("prescription", "Invalid Prescriber Credentials Hint:see previous errors.");
echo(js_escape($error));
return false;
}
$wenoLog->insertWenoLog("prescription", "Success");
} else {
Expand Down
Expand Up @@ -26,7 +26,8 @@

$logProperties = new LogProperties();
try {
$result = $logProperties->logSync();
$task = $_REQUEST['key'] ?? $_POST['key'] ?? '';
$result = $logProperties->logSync($task);
} catch (Exception $e) {
$result = false;
error_log('Error syncing log: ' . errorLogEscape($e->getMessage()));
Expand Down
2 changes: 1 addition & 1 deletion library/htmlspecialchars.inc.php
Expand Up @@ -41,7 +41,7 @@ function attr_url($text)
*/
function js_url($text)
{
return js_escape(urlencode($text));
return js_escape(urlencode($text ?? ''));
}

/**
Expand Down
27 changes: 22 additions & 5 deletions src/Events/PatientDocuments/PatientRetrieveOffsiteDocument.php
Expand Up @@ -13,24 +13,41 @@
namespace OpenEMR\Events\PatientDocuments;

use Symfony\Contracts\EventDispatcher\Event;
use Document;

class PatientRetrieveOffsiteDocument extends Event
{
const REMOTE_DOCUMENT_LOCATION = 'remote.document.retrieve.location';
private string $url;
private $offsiteurl;
public function __construct($url)
private $offsiteUrl;
private Document $document;
public function __construct(string $url, ?Document $document = null)
{
$this->url = $url;
$this->document = $document;
}

public function setOffsiteUrl(string $offsitedUrl): void
/**
* Returns the OpenEMR document class that represents this document in the file system
* @return Document|null
*/
public function getOpenEMRDocument(): ?Document
{
$this->offsiteurl = $offsiteUrl;
return $this->document;
}

public function getOpenEMRDocumentUrl(): string
{
return $this->url;
}

public function setOffsiteUrl(string $offsiteUrl): void
{
$this->offsiteUrl = $offsiteUrl;
}

public function getOffsiteUrl()
{
return $this->offsiteurl;
return $this->offsiteUrl;
}
}

0 comments on commit 24b7ed2

Please sign in to comment.