diff --git a/interface/patient_file/summary/demographics.php b/interface/patient_file/summary/demographics.php index c1142c03560..361295d0b40 100644 --- a/interface/patient_file/summary/demographics.php +++ b/interface/patient_file/summary/demographics.php @@ -35,12 +35,15 @@ use OpenEMR\Common\Session\SessionUtil; use OpenEMR\Common\Twig\TwigContainer; use OpenEMR\Core\Header; +use OpenEMR\Events\Patient\Summary\Card\RenderEvent as CardRenderEvent; +use OpenEMR\Events\Patient\Summary\Card\RenderModel; use OpenEMR\Events\PatientDemographics\ViewEvent; use OpenEMR\Events\PatientDemographics\RenderEvent; use OpenEMR\FHIR\SMART\SmartLaunchController; use OpenEMR\Menu\PatientMenuRole; use OpenEMR\OeUI\OemrUI; use OpenEMR\Reminder\BirthdayReminder; +use Symfony\Component\EventDispatcher\EventDispatcher; $twig = new TwigContainer(null, $GLOBALS['kernel']); @@ -60,6 +63,11 @@ $smartLaunchController = new SMARTLaunchController($GLOBALS["kernel"]->getEventDispatcher()); $smartLaunchController->registerContextEvents(); +/** + * @var EventDispatcher + */ +$ed = $GLOBALS['kernel']->getEventDispatcher(); + $active_reminders = false; $all_allergy_alerts = false; if ($GLOBALS['enable_cdr']) { @@ -926,6 +934,7 @@ function setMyPatient() { $insurancebalance = get_patient_balance($pid, true) - $patientbalance; $totalbalance = $patientbalance + $insurancebalance; $id = "billing_ps_expand"; + $dispatchResult = $ed->dispatch(CardRenderEvent::EVENT_HANDLE, new CardRenderEvent('billing')); $viewArgs = [ 'title' => xl('Billing'), 'id' => $id, @@ -935,6 +944,8 @@ function setMyPatient() { 'insuranceBalance' => $insurancebalance, 'totalBalance' => $totalbalance, 'forceAlwaysOpen' => $forceBillingExpandAlways, + 'prependedInjection' => $dispatchResult->getPrependedInjection(), + 'appendedInjection' => $dispatchResult->getAppendedInjection(), ]; if (!empty($result['billing_note'])) { @@ -955,6 +966,7 @@ function setMyPatient() { $GLOBALS["kernel"]->getEventDispatcher()->dispatch(RenderEvent::EVENT_SECTION_LIST_RENDER_BEFORE, new RenderEvent($pid), 10); if (AclMain::aclCheckCore('patients', 'demo')) : + $dispatchResult = $ed->dispatch(CardRenderEvent::EVENT_HANDLE, new CardRenderEvent('demographic')); // Render the Demographics box $viewArgs = [ 'title' => xl("Demographics"), @@ -968,6 +980,8 @@ function setMyPatient() { 'tabID' => "DEM", 'result' => $result, 'result2' => $result2, + 'prependedInjection' => $dispatchResult->getPrependedInjection(), + 'appendedInjection' => $dispatchResult->getAppendedInjection(), ]; echo $twig->getTwig()->render('patient/card/tab_base.html.twig', $viewArgs); @@ -1037,6 +1051,7 @@ function setMyPatient() { } $id = "insurance_ps_expand"; + $dispatchResult = $ed->dispatch(CardRenderEvent::EVENT_HANDLE, new CardRenderEvent('insurance')); $viewArgs = [ 'title' => xl("Insurance"), 'id' => $id, @@ -1048,6 +1063,8 @@ function setMyPatient() { 'eligibility' => $output, 'enable_oa' => $GLOBALS['enable_oa'], 'auth' => AclMain::aclCheckCore('patients', 'demo', '', 'write'), + 'prependedInjection' => $dispatchResult->getPrependedInjection(), + 'appendedInjection' => $dispatchResult->getAppendedInjection(), ]; if (count($insArr) > 0) { @@ -1056,6 +1073,7 @@ function setMyPatient() { endif; // end if demographics authorized if (AclMain::aclCheckCore('patients', 'notes')) : + $dispatchResult = $ed->dispatch(CardRenderEvent::EVENT_HANDLE, new CardRenderEvent('note')); // Notes expand collapse widget $id = "pnotes_ps_expand"; $viewArgs = [ @@ -1067,12 +1085,15 @@ function setMyPatient() { 'linkMethod' => "html", 'bodyClass' => "notab", 'auth' => AclMain::aclCheckCore('patients', 'notes', '', 'write'), + 'prependedInjection' => $dispatchResult->getPrependedInjection(), + 'appendedInjection' => $dispatchResult->getAppendedInjection(), ]; echo $twig->getTwig()->render('patient/card/loader.html.twig', $viewArgs); endif; // end if notes authorized if (AclMain::aclCheckCore('patients', 'reminder') && $GLOBALS['enable_cdr'] && $GLOBALS['enable_cdr_prw']) : // patient reminders collapse widget + $dispatchResult = $ed->dispatch(CardRenderEvent::EVENT_HANDLE, new CardRenderEvent('reminder')); $id = "patient_reminders_ps_expand"; $viewArgs = [ 'title' => xl('Patient Reminders'), @@ -1082,12 +1103,15 @@ function setMyPatient() { 'btnLink' => '../reminder/patient_reminders.php?mode=simple&patient_id=' . attr_url($pid), 'linkMethod' => 'html', 'bodyClass' => 'notab collapse show', - 'auth' => AclMain::aclCheckCore('patients', 'reminder', '', 'write') + 'auth' => AclMain::aclCheckCore('patients', 'reminder', '', 'write'), + 'prependedInjection' => $dispatchResult->getPrependedInjection(), + 'appendedInjection' => $dispatchResult->getAppendedInjection(), ]; echo $twig->getTwig()->render('patient/card/loader.html.twig', $viewArgs); endif; //end if prw is activated if (AclMain::aclCheckCore('patients', 'disclosure')) : + $dispatchResult = $ed->dispatch(CardRenderEvent::EVENT_HANDLE, new CardRenderEvent('disclosure')); // disclosures expand collapse widget $id = "disclosures_ps_expand"; $viewArgs = [ @@ -1098,12 +1122,15 @@ function setMyPatient() { 'btnLink' => 'disclosure_full.php', 'linkMethod' => 'html', 'bodyClass' => 'notab collapse show', - 'auth' => AclMain::aclCheckCore('patients', 'disclosure', '', 'write') + 'auth' => AclMain::aclCheckCore('patients', 'disclosure', '', 'write'), + 'prependedInjection' => $dispatchResult->getPrependedInjection(), + 'appendedInjection' => $dispatchResult->getAppendedInjection(), ]; echo $twig->getTwig()->render('patient/card/loader.html.twig', $viewArgs); endif; // end if disclosures authorized if ($GLOBALS['amendments'] && AclMain::aclCheckCore('patients', 'amendment')) : + $dispatchResult = $ed->dispatch(CardRenderEvent::EVENT_HANDLE, new CardRenderEvent('amendment')); // Amendments widget $sql = "SELECT * FROM amendments WHERE pid = ? ORDER BY amendment_date DESC"; $result = sqlStatement($sql, [$pid]); @@ -1124,11 +1151,14 @@ function setMyPatient() { 'bodyClass' => 'notab collapse show', 'auth' => AclMain::aclCheckCore('patients', 'amendment', '', 'write'), 'amendments' => $amendments, + 'prependedInjection' => $dispatchResult->getPrependedInjection(), + 'appendedInjection' => $dispatchResult->getAppendedInjection(), ]; echo $twig->getTwig()->render('patient/card/amendments.html.twig', $viewArgs); endif; // end amendments authorized if (AclMain::aclCheckCore('patients', 'lab')) : + $dispatchResult = $ed->dispatch(CardRenderEvent::EVENT_HANDLE, new CardRenderEvent('lab')); // labdata expand collapse widget // check to see if any labdata exist $spruch = "SELECT procedure_report.date_collected AS date @@ -1149,11 +1179,14 @@ function setMyPatient() { 'linkMethod' => 'html', 'bodyClass' => 'collapse show', 'auth' => $widgetAuth, + 'prependedInjection' => $dispatchResult->getPrependedInjection(), + 'appendedInjection' => $dispatchResult->getAppendedInjection(), ]; echo $twig->getTwig()->render('patient/card/loader.html.twig', $viewArgs); endif; // end labs authorized if ($vitals_is_registered && AclMain::aclCheckCore('patients', 'med')) : + $dispatchResult = $ed->dispatch(CardRenderEvent::EVENT_HANDLE, new CardRenderEvent('vital_sign')); // vitals expand collapse widget // check to see if any vitals exist $existVitals = sqlQuery("SELECT * FROM form_vitals WHERE pid=?", array($pid)); @@ -1169,6 +1202,8 @@ function setMyPatient() { 'linkMethod' => 'html', 'bodyClass' => 'collapse show', 'auth' => $widgetAuth, + 'prependedInjection' => $dispatchResult->getPrependedInjection(), + 'appendedInjection' => $dispatchResult->getAppendedInjection(), ]; echo $twig->getTwig()->render('patient/card/loader.html.twig', $viewArgs); endif; // end vitals @@ -1201,6 +1236,7 @@ function setMyPatient() { $widgetAuth = $existVitals; } + $dispatchResult = $ed->dispatch(CardRenderEvent::EVENT_HANDLE, new CardRenderEvent($gfrow['title'])); $viewArgs = [ 'title' => xl($gfrow['title']), 'id' => $vitals_form_id, @@ -1210,6 +1246,8 @@ function setMyPatient() { 'linkMethod' => 'html', 'bodyClass' => 'notab collapse show', 'auth' => $widgetAuth, + 'prependedInjection' => $dispatchResult->getPrependedInjection(), + 'appendedInjection' => $dispatchResult->getAppendedInjection(), ]; echo $twig->getTwig()->render('patient/card/loader.html.twig', $viewArgs); endwhile; // end while @@ -1220,16 +1258,24 @@ function setMyPatient() { getTwig()->render('patient/partials/erx.html.twig', []); + $dispatchResult = $ed->dispatch(CardRenderEvent::EVENT_HANDLE, new CardRenderEvent('demographics')); + echo $twig->getTwig()->render('patient/partials/erx.html.twig', [ + 'prependedInjection' => $dispatchResult->getPrependedInjection(), + 'appendedInjection' => $dispatchResult->getAppendedInjection(), + ]); endif; if ($GLOBALS['portal_onsite_two_enable']) : + $dispatchResult = $ed->dispatch(CardRenderEvent::EVENT_HANDLE, new CardRenderEvent('portal')); + echo $twig->getTwig()->render('patient/partials/portal.html.twig', [ 'portalAuthorized' => portalAuthorized($pid), 'portalLoginHref' => $portal_login_href, 'title' => xl('Patient Portal'), 'id' => 'patient_portal', 'initiallyCollapsed' => (getUserSetting($id) == 0) ? false : true, + 'prependedInjection' => $dispatchResult->getPrependedInjection(), + 'appendedInjection' => $dispatchResult->getAppendedInjection(), ]); endif; @@ -1237,6 +1283,7 @@ function setMyPatient() { $photos = pic_array($pid, $GLOBALS['patient_photo_category_name']); if ($photos or $idcard_doc_id) { $id = "photos_ps_expand"; + $dispatchResult = $ed->dispatch(CardRenderEvent::EVENT_HANDLE, new CardRenderEvent('patient_photo')); $viewArgs = [ 'title' => xl("ID Card / Photos"), 'id' => $id, @@ -1249,6 +1296,8 @@ function setMyPatient() { 'patientPhotoCategoryName' => $GLOBALS['patient_photo_category_name'], 'photos' => $photos, 'idCardDocID' => $idcard_doc_id, + 'prependedInjection' => $dispatchResult->getPrependedInjection(), + 'appendedInjection' => $dispatchResult->getAppendedInjection(), ]; echo $twig->getTwig()->render('patient/card/photo.html.twig', $viewArgs); } @@ -1296,6 +1345,8 @@ function setMyPatient() { } $id = "adv_directives_ps_expand"; + + $dispatchResult = $ed->dispatch(CardRenderEvent::EVENT_HANDLE, new CardRenderEvent('advance_directive')); $viewArgs = [ 'title' => xl("Advance Directives"), 'id' => $id, @@ -1307,6 +1358,8 @@ function setMyPatient() { 'auth' => true, 'advDirArr' => $advDirArr, 'counterFlag' => $counterFlag, + 'prependedInjection' => $dispatchResult->getPrependedInjection(), + 'appendedInjection' => $dispatchResult->getAppendedInjection(), ]; echo $twig->getTwig()->render('patient/card/adv_dir.html.twig', $viewArgs); } @@ -1319,6 +1372,7 @@ function setMyPatient() { if (!empty($clin_rem_check) && $cdr && $cdr_crw && AclMain::aclCheckCore('patients', 'alert')) { // clinical summary expand collapse widget $id = "clinical_reminders_ps_expand"; + $dispatchResult = $ed->dispatch(CardRenderEvent::EVENT_HANDLE, new CardRenderEvent('clinical_reminders')); $viewArgs = [ 'title' => xl("Clinical Reminders"), 'id' => $id, @@ -1327,6 +1381,8 @@ function setMyPatient() { 'btnLink' => "../reminder/clinical_reminders.php?patient_id=" . attr_url($pid), 'linkMethod' => "html", 'auth' => AclMain::aclCheckCore('patients', 'alert', '', 'write'), + 'prependedInjection' => $dispatchResult->getPrependedInjection(), + 'appendedInjection' => $dispatchResult->getAppendedInjection(), ]; echo $twig->getTwig()->render('patient/card/loader.html.twig', $viewArgs); } // end if crw @@ -1488,12 +1544,15 @@ function setMyPatient() { $count2++; } $id = "recall_ps_expand"; + $dispatchResult = $ed->dispatch(CardRenderEvent::EVENT_HANDLE, new CardRenderEvent('recall')); echo $twig->getTwig()->render('patient/card/recall.html.twig', [ 'title' => xl('Recall'), 'id' => $id, 'initiallyCollapsed' => (getUserSetting($id) == 0) ? false : true, 'recalls' => $recallArr, 'recallsAvailable' => ($count < 1 && empty($count2)) ? false : true, + 'prependedInjection' => $dispatchResult->getPrependedInjection(), + 'appendedInjection' => $dispatchResult->getAppendedInjection(), ]); } } // End of Appointments Widget. @@ -1578,6 +1637,7 @@ function setMyPatient() { // Display the Appt card $id = "appointments_ps_expand"; + $dispatchResult = $ed->dispatch(CardRenderEvent::EVENT_HANDLE, new CardRenderEvent('appointment')); echo $twig->getTwig()->render('patient/card/appointments.html.twig', [ 'title' => xl("Appointments"), 'id' => $id, @@ -1595,6 +1655,8 @@ function setMyPatient() { 'therapyGroupCategories' => $therapyGroupCategories, 'auth' => $resNotNull && (AclMain::aclCheckCore('patients', 'appt', '', 'write') || AclMain::aclCheckCore('patients', 'appt', '', 'addonly')), 'resNotNull' => $resNotNull, + 'prependedInjection' => $dispatchResult->getPrependedInjection(), + 'appendedInjection' => $dispatchResult->getAppendedInjection(), ]); echo "
"; @@ -1607,12 +1669,15 @@ function setMyPatient() { $spruch = "SELECT id FROM forms WHERE pid = ? AND formdir = ?"; $existTracks = sqlQuery($spruch, array($pid, "track_anything")); $id = "track_anything_ps_expand"; + $dispatchResult = $ed->dispatch(CardRenderEvent::EVENT_HANDLE, new CardRenderEvent('track_anything')); echo $twig->getTwig()->render('patient/card/loader.html.twig', [ 'title' => xl("Tracks"), 'id' => $id, 'initiallyCollapsed' => (getUserSetting($id) == 0) ? false : true, 'btnLink' => "../../forms/track_anything/create.php", - 'linkMethod' => "html" + 'linkMethod' => "html", + 'prependedInjection' => $dispatchResult->getPrependedInjection(), + 'appendedInjection' => $dispatchResult->getAppendedInjection(), ]); } // end track_anything diff --git a/src/Events/Patient/Summary/Card/RenderEvent.php b/src/Events/Patient/Summary/Card/RenderEvent.php new file mode 100644 index 00000000000..4e9bdb6a114 --- /dev/null +++ b/src/Events/Patient/Summary/Card/RenderEvent.php @@ -0,0 +1,141 @@ + + * @copyright Copyright (c) 2022 Robert Down + */ + +namespace OpenEMR\Events\Patient\Summary\Card; + +use OpenEMR\Events\Patient\Summary\Card\RenderInterface; +use Symfony\Component\EventDispatcher\Event; + +class RenderEvent extends Event +{ + /** + * The patiemtSummaryCard.render event occurs when card on the Patient + * Demographics screen is rendered. + */ + const EVENT_HANDLE = 'patientSummaryCard.render'; + + /** + * ID of the card being rendered + * + * @var string + */ + private $card; + + /** + * Array holding the prepended data + * + * @var array + */ + private $prependedData = []; + + /** + * Array holding the appended data + * + * @var array + */ + private $appendedData = []; + + /** + * UpdateEvent constructor. + * + * @param string $cardID The ID of the card being rendered + */ + public function __construct(string $cardID) + { + $this->setCard($cardID); + } + + /** + * Get the name of the card + * + * @return string Name of the card + */ + public function getCard(): string + { + return $this->card; + } + + /** + * Set the card ID + * + * @param string $card Name of the card + * @return void + */ + public function setCard(string $card): void + { + $this->card = $card; + } + + /** + * Add content to the end of a card + * + * @param RenderInterface $object + * @param int|null $position Specific position in array, optional. Defaults to end. + * @return void + */ + public function addAppendedData(RenderInterface $object, $position = null): void + { + $this->modifyArray('appendedData', $object, $position); + } + + /** + * Add content to the beginning of a card + * + * @param RenderInterface $object + * @param int|null $position Specific position in array, optional. Defaults to end. + * @return void + */ + public function addPrependedData(RenderInterface $object, $position = null): void + { + $this->modifyArray('prependedData', $object, $position); + } + + /** + * Modify the appended and prepended data array + * + * @param string $property Name of the property to modify + * @param RenderInterface $object The object to add + * @param int|null $position Specific position in array, optional. Defaults to end + * @return void + */ + private function modifyArray(string $property, RenderInterface $object, $position = null): void + { + if (property_exists($this, $property)) { + if (count($this->$property) === 0) { + $this->$property[] = $object; + } else { + $position = $position ?? -1; + array_splice($this->$property, $position, 0, $object); + } + } + } + + /** + * Get the data to be appended data + * + * @return array + */ + public function getAppendedInjection(): array + { + return $this->appendedData; + } + + /** + * Get the data to be prepended data + * + * @return array + */ + public function getPrependedInjection(): array + { + return $this->prependedData; + } +} diff --git a/src/Events/Patient/Summary/Card/RenderInterface.php b/src/Events/Patient/Summary/Card/RenderInterface.php new file mode 100644 index 00000000000..925d57b388f --- /dev/null +++ b/src/Events/Patient/Summary/Card/RenderInterface.php @@ -0,0 +1,31 @@ + + * @copyright Copyright (c) 2022 Robert Down + */ + +namespace OpenEMR\Events\Patient\Summary\Card; + +interface RenderInterface +{ + /** + * Return the name of the template to be rendered + * + * @return string + */ + public function getTemplateFile(): string; + + /** + * Return the array of variables to be rendered by the template + * + * @return array + */ + public function getVariables(): array; +} diff --git a/src/Events/Patient/Summary/Card/RenderModel.php b/src/Events/Patient/Summary/Card/RenderModel.php new file mode 100644 index 00000000000..f9bcf289eb1 --- /dev/null +++ b/src/Events/Patient/Summary/Card/RenderModel.php @@ -0,0 +1,42 @@ + + * @copyright Copyright (c) 2022 Robert Down + */ + +namespace OpenEMR\Events\Patient\Summary\Card; + +class RenderModel implements RenderInterface +{ + private $templateFileName; + + private $variables; + + public function __construct(string $templateFileName, array $variables) + { + $this->templateFileName = $templateFileName; + $this->variables = $variables; + } + + /** + * @inheritDoc + */ + public function getTemplateFile(): string + { + return $this->templateFileName; + } + + /** + * @inheritDoc + */ + public function getVariables(): array + { + return $this->variables; + } +} diff --git a/templates/patient/macros/card.macro.twig b/templates/patient/macros/card.macro.twig new file mode 100644 index 00000000000..2b360694f64 --- /dev/null +++ b/templates/patient/macros/card.macro.twig @@ -0,0 +1,20 @@ +{# +Macros for rendering a patient card + +@author Robert Down +@copyright Copyright (c) 2022 Robert Down +@package OpenEMR +#} + +{# +Render the prepended or appended content of a card. + +@var array Array of RenderInterface objects +#} +{% macro injectedContent(content) %} + {% if content is iterable %} + {% for row in content %} + {{ include(row.getTemplateFile, row.getVariables)}} + {% endfor %} + {% endif %} +{% endmacro %} diff --git a/templates/patient/partials/portal.html.twig b/templates/patient/partials/portal.html.twig index 11e7ae3878b..70a20f926f5 100644 --- a/templates/patient/partials/portal.html.twig +++ b/templates/patient/partials/portal.html.twig @@ -8,11 +8,19 @@ The Patient Portal card for the Medical Record Dashboard {% extends "patient/card/card_base.html.twig" %} +{% if (prependedInjection is defined) or (appendedInjection is defined) %} + {% import "patient/macros/card.macro.twig" as cardMacros %} + {% set prepend = cardMacros.injectedContent(prependedInjection) %} + {% set append = cardMacros.injectedContent(appendedInjection) %} +{% endif %} + + {% block content %}
{% if portalAuthorized == false %} {{ "Patient Portal Not Authorized"|xlt }} {% else %} + {{ prepend }} {% if portalAuthorized.created == true %} {% set class = "fa-key" %} {% set text = "Reset Credentials"|xlt %} @@ -22,6 +30,7 @@ The Patient Portal card for the Medical Record Dashboard {% endif %}  {{ text|text }} + {{ append }} {% endif %}
{% endblock %} diff --git a/templates/patient/partials/testing.html.twig b/templates/patient/partials/testing.html.twig new file mode 100644 index 00000000000..4b578a732bc --- /dev/null +++ b/templates/patient/partials/testing.html.twig @@ -0,0 +1,2 @@ +{# THIS IS FOR POC ONLY - REMOVE BEFORE SUBMITTING PR #} +{{ var1 }}