Repository: openemr/openemr (language: php)

Issue:
New feature FaxSMS module - Auto notifications
### Describe the feature
It's always an issue for regular users to create batch/cron jobs to automate notifications, for now, appointments
### Describe the solution you'd like
From module SMS and/or Email setup schedule background task for appropriate notifications.


<!-- Love openemr? Please consider supporting our project:
👉  https://github.com/sponsors/openemr -->



Discussion / hints:

Fix patch:
diff --git a/interface/modules/custom_modules/oe-module-faxsms/library/rc_sms_notification.php b/interface/modules/custom_modules/oe-module-faxsms/library/rc_sms_notification.php
index 76ffac6446f..277f116e463 100644
--- a/interface/modules/custom_modules/oe-module-faxsms/library/rc_sms_notification.php
+++ b/interface/modules/custom_modules/oe-module-faxsms/library/rc_sms_notification.php
@@ -51,7 +51,7 @@
 // so service can set some settings if needed on init.
 $sessionAllowWrite = true;
 require_once(__DIR__ . "/../../../../globals.php");
-require_once("$srcdir/appointments.inc.php");
+require_once($GLOBALS['srcdir'] . "/appointments.inc.php");
 
 // Check for help argument
 if ($argc > 1 && (in_array('--help', $argv) || in_array('-h', $argv))) {
diff --git a/interface/modules/custom_modules/oe-module-faxsms/library/run_notifications.php b/interface/modules/custom_modules/oe-module-faxsms/library/run_notifications.php
new file mode 100644
index 00000000000..ae841dfe3f4
--- /dev/null
+++ b/interface/modules/custom_modules/oe-module-faxsms/library/run_notifications.php
@@ -0,0 +1,19 @@
+<?php
+
+function doEmailNotificationTask(): void
+{
+    $scheduled_task_flag = 1;
+    $_GET['type'] = 'email';
+    $_GET['site'] = $_SESSION['site_id'];
+
+    require_once("rc_sms_notification.php");
+}
+
+function doSmsNotificationTask(): void
+{
+    $scheduled_task_flag = 1;
+    $_GET['type'] = 'sms';
+    $_GET['site'] = $_SESSION['site_id'];
+
+    require_once("rc_sms_notification.php");
+}
diff --git a/interface/modules/custom_modules/oe-module-faxsms/library/setup_services.php b/interface/modules/custom_modules/oe-module-faxsms/library/setup_services.php
index 43a57610cd3..7c5126a0567 100644
--- a/interface/modules/custom_modules/oe-module-faxsms/library/setup_services.php
+++ b/interface/modules/custom_modules/oe-module-faxsms/library/setup_services.php
@@ -16,10 +16,55 @@
 use OpenEMR\Common\Csrf\CsrfUtils;
 use OpenEMR\Core\Header;
 use OpenEMR\Modules\FaxSMS\BootstrapService;
+use OpenEMR\Modules\FaxSMS\Controller\NotificationTaskManager;
 
 $module_config = 1;
 
 $boot = new BootstrapService();
+$taskManager = new NotificationTaskManager();
+$services = ['sms', 'email'];
+$actions = ['create', 'enable', 'disable', 'delete'];
+
+if (($_POST['action'] ?? null) || ($_POST['selected_service'] ?? null)) {
+    if (!CsrfUtils::verifyCsrfToken($_POST["csrf_token_form"])) {
+        CsrfUtils::csrfNotVerified();
+    }
+
+    $selectedService = $_POST['selected_service'] ?? null;
+    $selectedAction = $_POST['action'] ?? null;
+    $status = $taskManager->getServiceStatus($selectedService);
+
+    $period = $_POST['period'] ?? null;
+    if (empty($period)) {
+        $period = $taskManager->getTaskHours($selectedService);
+    }
+
+    if ($selectedService && $selectedAction) {
+        if ($selectedAction === 'create') {
+            if ($period) {
+                $taskManager->manageService($selectedService, $period);
+            }
+        }
+        switch ($selectedAction) {
+            case 'create':
+                break;
+            case 'enable':
+                $taskManager->enableService($selectedService, $period);
+                break;
+            case 'disable':
+                $taskManager->disableService($selectedService);
+                break;
+            case 'delete':
+                $taskManager->deleteService($selectedService);
+                break;
+            default:
+                throw new Exception("Invalid action selected.");
+        }
+    }
+}
+
+$currentStatus = $selectedService ? $taskManager->getServiceStatus($selectedService) : null;
+
 if ($_POST['form_save'] ?? null) {
     if (!CsrfUtils::verifyCsrfToken($_POST["csrf_token_form"])) {
         CsrfUtils::csrfNotVerified();
@@ -39,11 +84,15 @@
         $boot->createVendorGlobals();
         $vendors = $boot->getVendorGlobals();
     }
+    $isSmsEnabled = $vendors['oefax_enable_sms'] > 0 ? 'sms' : '';
+    $isEmailEnable = $vendors['oe_enable_email'] > 0 ? 'email' : '';
+    $services = [$isSmsEnabled, $isEmailEnable];
+
     $isRCSMS = $vendors['oefax_enable_sms'] == 1 ? '1' : '0';
-    $isRCFax = $vendors['oefax_enable_fax'] == 1 ? '1' : '0';
     $isEMAIL = $vendors['oe_enable_email'] == 4 ? '1' : '0';
+    $isRCFax = $vendors['oefax_enable_fax'] == 1 ? '1' : '0';
     $setupUrl = './../setup.php';
-    if ($isRCFax) {
+    if ($isRCFax || $isRCSMS) {
         $setupUrl = './../setup_rc.php';
     }
     Header::setupHeader();
@@ -53,6 +102,11 @@
         let ServiceSMS = <?php echo js_escape($isRCSMS); ?>;
         let ServiceEmail = <?php echo js_escape($isEMAIL); ?>;
 
+        function toggleHelpCard() {
+            const helpCard = document.getElementById('helpCard');
+            helpCard.style.display = helpCard.style.display === 'none' ? 'block' : 'none';
+        }
+
         function toggleSetup(id, type = 'single') {
             let url = './../setup.php';
             if (ServiceFax === '1') {
@@ -110,6 +164,11 @@ function toggleSetup(id, type = 'single') {
                     $("#form_save").click();
                 })
             });
+            document.querySelectorAll('input[type="radio"]').forEach(function (radio) {
+                radio.addEventListener('change', function () {
+                    $("#form_action").submit();
+                });
+            });
         });
     </script>
 </head>
@@ -122,7 +181,8 @@ function toggleSetup(id, type = 'single') {
             <?php }
             if (!empty($vendors['oefax_enable_fax'])) { ?>
                 <button class="btn btn-outline-light" onclick="toggleSetup('set-fax')"><?php echo xlt("Setup Fax Account"); ?><span class="caret"></span></button>
-            <?php } if (!empty($vendors['oe_enable_email'])) { ?>
+            <?php }
+            if (!empty($vendors['oe_enable_email'])) { ?>
                 <button class="btn btn-outline-light" onclick="toggleSetup('set-email')"><?php echo xlt("Setup Email Account"); ?><span class="caret"></span></button>
             <?php } ?>
             <span class="checkbox text-light br-dark" title="Use Dialog or Panels">
@@ -186,6 +246,61 @@ function toggleSetup(id, type = 'single') {
                     </div>
                 </div>
             </form>
+            <form class="form w-100" id="form_action" method="POST" action="setup_services.php">
+                <input type="hidden" name="csrf_token_form" id="csrf_token_form" value="<?php echo attr(CsrfUtils::collectCsrfToken()); ?>" />
+                <fieldset>
+                    <legend><?php echo xlt('Select Background Service to Manage');
+                        $showFlag = false; ?></legend>
+                    <div id="helpCard" class="help-card card" style="display: none;">
+                        <div class="card-header text-center"><h5><?php echo xlt('Managing Background Services Help'); ?></h5></div>
+                        <div class="card-body"><?php echo nl2br(text('Select a background service to manage.  
+                        You may specify an required interval in hours when creating the task. The default is 24 hours
+                        Use the action buttons to create the task, enable or disable its execution, or delete it. 
+                        When Create is used the task is created but disabled. 
+                        If Enable is selected, the task is created if not already installed and enabled as a step saver. 
+                        Whichever one is used, Create or Enable, and the task already exists, it will be updated with the execute interval input value and enabled if Enable or the task last state if Create.
+                        Whenever a new service task is created and enabled, the task will run initial notifications within 2 minutes so, be prepared.')); ?></div>
+                    </div>
+                    <div class="pl-2 form-group clearfix">
+                        <?php foreach ($services as $service) {
+                            if (empty($service)) {
+                                continue;
+                            }
+                            $showFlag = true;
+                            if ($service != 'disabled') {
+                            }
+                            ?>
+                            <label>
+                                <input type="radio" name="selected_service" value="<?php echo attr($service); ?>" <?php echo ($selectedService === $service) ? 'checked' : ''; ?> required />
+                                <?php echo text(ucfirst($service)); ?>
+                            </label>
+                        <?php }
+                        if ($showFlag) { ?>
+                            <div class="form-group">
+                                <button type="submit" class="btn btn-primary" name="action" value="create"><?php echo xlt('Create and Run'); ?></button>
+                                <?php echo xlt('Every'); ?> <input type="text" name="period" id="period_input" class="" style="display:inline-block; max-width: 125px;" maxlength="4"
+                                    placeholder="<?php echo xla('Execute Interval'); ?>"
+                                    value="<?php echo attr($period ?? ''); ?>" /> <?php echo xlt('Hours'); ?>
+                                <span class="button-group">
+                                <button type="submit" class="btn btn-success" name="action" value="enable"><?php echo xlt('Enable'); ?></button>
+                                <button type="submit" class="btn btn-warning" name="action" value="disable"><?php echo xlt('Disable'); ?></button>
+                                <button type="submit" class="btn btn-danger" name="action" value="delete"><?php echo xlt('Delete'); ?></button>
+                                <button type="button" class="btn btn-info" onclick="toggleHelpCard()"><?php echo xlt('Help'); ?></button>
+                            </span>
+                            </div>
+                            <?php if ($currentStatus !== null && isset($currentStatus[$selectedService])) { ?>
+                                <span><strong><?php echo xlt('Status of'); ?> <?php echo text(ucfirst($selectedService)); ?> <?php echo xlt('Service'); ?>:</strong></span>
+                                <ul>
+                                    <li><strong><?php echo xlt('Service Status'); ?>: </strong><?php echo text($currentStatus[$selectedService]['active']) ? xlt('Enabled to Run.') : xlt('Disabled or not Created.'); ?></li>
+                                    <li><strong><?php echo xlt('Execution Run Interval'); ?>: </strong><?php echo text($currentStatus[$selectedService]['execute_interval']) . ' ' . xlt('Minutes'); ?></li>
+                                    <li><strong><?php echo xlt('Next Run Time'); ?>: </strong><?php echo text($currentStatus[$selectedService]['next_run']); ?></li>
+                                </ul>
+                            <?php }
+                        } else { ?>
+                            <h5><?php echo xlt('To enable Background Services setup an SMS vendor, enable the Mail Client or Both.'); ?></h5>
+                        <?php } ?>
+                    </div>
+                </fieldset>
         </div>
         <!-- iframes to hold setup account scripts. Dialogs replace these if requested in UI -->
         <?php if (!empty($vendors['oefax_enable_fax'])) { ?>
diff --git a/interface/modules/custom_modules/oe-module-faxsms/messageUI.php b/interface/modules/custom_modules/oe-module-faxsms/messageUI.php
index 572d2ac1c0a..7d7726f8c10 100644
--- a/interface/modules/custom_modules/oe-module-faxsms/messageUI.php
+++ b/interface/modules/custom_modules/oe-module-faxsms/messageUI.php
@@ -52,7 +52,7 @@
                 require($GLOBALS['srcdir'] . '/js/xl/jquery-datetimepicker-2-5-4.js.php');
                 ?>
             });
-            let dateRange = new Date(new Date().setDate(new Date().getDate() - 7));
+            let dateRange = new Date(new Date().setDate(new Date().getDate() - 1));
             $("#fromdate").val(dateRange.toJSON().slice(0, 10));
             $("#todate").val(new Date().toJSON().slice(0, 10));
 
@@ -98,6 +98,7 @@
             SendNotificationEvent::JAVASCRIPT_READY_NOTIFICATION_POST
         );
         ?>
+
         const sendFax = function (filePath, from = '') {
             let btnClose = <?php echo xlj("Cancel"); ?>;
             let title = <?php echo xlj("Send To Contact"); ?>;
@@ -473,13 +474,12 @@ function retrieveMsgs(e = '', req = '') {
             const dateto = $('#todate').val();
 
             // Cache DOM elements
-            const brandElement = $(".brand");
             const rcvDetailsBody = $("#rcvdetails tbody");
             const sentDetailsBody = $("#sent-details tbody");
             const msgDetailsBody = $("#msgdetails tbody");
 
             // Start loading animation
-            brandElement.addClass('fa fa-spinner fa-spin');
+            $(".brand").addClass('fa fa-spinner fa-spin');
             rcvDetailsBody.empty();
             sentDetailsBody.empty();
             msgDetailsBody.empty();
@@ -490,9 +490,8 @@ function retrieveMsgs(e = '', req = '') {
                 'datefrom': datefrom,
                 'dateto': dateto
             }, null, 'json').done(function (data) {
-                brandElement.removeClass('fa fa-spinner fa-spin');
-
                 if (data.error) {
+                    $(".brand").removeClass('fa fa-spinner fa-spin');
                     alertMsg(data.error);
                     return false;
                 }
@@ -501,16 +500,14 @@ function retrieveMsgs(e = '', req = '') {
                 sentDetailsBody.append(data[1]);
                 msgDetailsBody.append(data[2]);
 
-                // Get call logs if the service type is SMS
-                if (serviceType === 'sms') {
+                if (serviceType) {
                     getLogs();
                 }
             }).fail(function (xhr, status, error) {
-                const message = `Error: ${error || 'Request to fetch pending new faxes failed with Unknown error!'}<br />Perhaps invalid or missing credentials. Verify, fix and try again if so.`;
+                const message = `Error: ${error || 'Request to fetch pendings failed with Unknown error!'}`;
                 alertMsg(message, 10000);
-                console.error('Request failed: ', status, error);
             }).always(function () {
-                brandElement.removeClass('fa fa-spinner fa-spin');
+                $(".brand").removeClass('fa fa-spinner fa-spin');
             });
         }
 
@@ -585,8 +582,32 @@ function getSelResource() {
             return $('#resource option:selected').val();
         }
 
-        function messageShow(id) {
-            $("." + id).toggleClass("d-none");
+        function messageShow(id, uri) {
+            try {
+                top.restoreSession();
+            } catch (error) {
+                console.log('Session restore failed!');
+            }
+
+            $(".brand").addClass('fa fa-spinner fa-spin');
+            let actionUrl = 'fetchTextMessage?type=sms';
+            $.post(actionUrl, {
+                'id': id,
+                'uri': uri
+            }, null, 'json').done(function (data) {
+                $(".brand").removeClass('fa fa-spinner fa-spin');
+                if (data.error) {
+                    alertMsg(data.error);
+                    return false;
+                }
+                $("." + id).empty().append(data);
+            }).fail(function (xhr, status, error) {
+                const message = `Error: ${error || 'Request to fetch message failed with Unknown error!'}`;
+                alertMsg(message, 10000);
+                console.error('Request failed: ', status, error);
+            }).always(function () {
+                $(".brand").removeClass('fa fa-spinner fa-spin');
+            });
         }
 
         function messageReply(phone) {
@@ -869,7 +890,7 @@ function createPatient(e, faxId, recordId, data) {
                                             </tbody>
                                         </table>
                                     </div>
-                                <?php } else { ?>
+                                <?php } elseif ($service != '1') { // not RC ?>
                                     <div class="table-responsive">
                                         <table class="table table-sm table-striped" id="sent-details">
                                             <thead>
@@ -971,42 +992,65 @@ function createPatient(e, faxId, recordId, data) {
                     <?php }
                     if ($service == '1') { ?>
                         <div class="tab-content">
+                            <?php  if ($serviceType == 'fax') { ?>
+                                <div class="tab-pane fade in active" id="received">
+                                    <div class="table-responsive">
+                                        <table class="table table-sm table-striped" id="rcvdetails">
+                                            <thead>
+                                            <tr>
+                                                <th><?php echo xlt("Start Time") ?></th>
+                                                <th><?php echo xlt("End Time") ?></th>
+                                                <th><?php echo xlt("Pages") ?></th>
+                                                <th><?php echo xlt("From") ?></th>
+                                                <th><?php echo xlt("To") ?></th>
+                                                <th><?php echo xlt("Status") ?></th>
+                                                <th><?php echo xlt("Actions") ?></th>
+                                            </tr>
+                                            </thead>
+                                            <tbody>
+                                            <tr>
+                                                <td><?php echo xlt("No Items Try Refresh") ?></td>
+                                            </tr>
+                                            </tbody>
+                                        </table>
+                                    </div>
+                                </div>
+                                <div class="tab-pane fade" id="sent">
+                                    <div class="table-responsive">
+                                        <table class="table table-sm table-striped" id="sent-details">
+                                            <thead>
+                                            <tr>
+                                                <th><?php echo xlt("Start Time") ?></th>
+                                                <th><?php echo xlt("End Time") ?></th>
+                                                <th><?php echo xlt("Pages") ?></th>
+                                                <th><?php echo xlt("From") ?></th>
+                                                <th><?php echo xlt("To") ?></th>
+                                                <th><?php echo xlt("Status") ?></th>
+                                                <th><?php echo xlt("Actions") ?></th>
+                                                <th><i role="button" id="delete-selected-sent" title="<?php echo xla("Delete selected fax documents") ?>" class="delete-selected-items text-danger fa fa-trash"></i></th>
+                                            </tr>
+                                            </thead>
+                                            <tbody>
+                                            <tr>
+                                                <td><?php echo xlt("No Items Try Refresh") ?></td>
+                                            </tr>
+                                            </tbody>
+                                        </table>
+                                    </div>
+                                </div>
+                            <?php } else { ?>
                             <div role="tabpanel" class="container-fluid tab-pane fade" id="received">
                                 <div class="table-responsive">
                                     <table class="table table-sm table-striped" id="rcvdetails">
                                         <thead>
                                         <tr>
-                                            <th><?php echo xlt("Start Time") ?></th>
-                                            <th><?php echo xlt("End Time") ?></th>
-                                            <th><?php echo xlt("Pages") ?></th>
-                                            <th><?php echo xlt("From") ?></th>
-                                            <th><?php echo xlt("To") ?></th>
+                                            <th><?php echo xlt("Date") ?></th>
                                             <th><?php echo xlt("Status") ?></th>
-                                            <th><?php echo xlt("Actions") ?></th>
-                                            <th><i role="button" id="delete-selected-received" title="<?php echo xla("Delete selected fax documents") ?>" class="delete-selected-items text-danger fa fa-trash"></i></th>
-                                        </tr>
-                                        </thead>
-                                        <tbody>
-                                        <tr>
-                                            <td><?php echo xlt("No Items Try Refresh") ?></td>
-                                        </tr>
-                                        </tbody>
-                                    </table>
-                                </div>
-                            </div>
-                            <div role="tabpanel" class="container-fluid tab-pane fade" id="sent">
-                                <div class="table-responsive">
-                                    <table class="table table-sm table-striped" id="sent-details">
-                                        <thead>
-                                        <tr>
-                                            <th><?php echo xlt("Start Time") ?></th>
-                                            <th><?php echo xlt("End Time") ?></th>
-                                            <th><?php echo xlt("Pages") ?></th>
                                             <th><?php echo xlt("From") ?></th>
                                             <th><?php echo xlt("To") ?></th>
-                                            <th><?php echo xlt("Status") ?></th>
+                                            <th><?php echo xlt("Result") ?></th>
+                                            <th><?php echo xlt("Message") ?></th>
                                             <th><?php echo xlt("Actions") ?></th>
-                                            <th><i role="button" id="delete-selected-received" title="<?php echo xla("Delete selected fax documents") ?>" class="delete-selected-items text-danger fa fa-trash"></i></th>
                                         </tr>
                                         </thead>
                                         <tbody>
@@ -1017,29 +1061,51 @@ function createPatient(e, faxId, recordId, data) {
                                     </table>
                                 </div>
                             </div>
-                            <div role="tabpanel" class="container-fluid tab-pane fade d-none" id="messages">
-                                <div class="table-responsive">
-                                    <table class="table table-sm table-striped" id="msgdetails">
-                                        <thead>
-                                        <tr>
-                                            <th><?php echo xlt("Date") ?></th>
-                                            <th><?php echo xlt("Type") ?></th>
-                                            <th><?php echo xlt("From") ?></th>
-                                            <th><?php echo xlt("To") ?></th>
-                                            <th><?php echo xlt("Result") ?></th>
-                                            <th class="twilio"><?php echo xlt("Download") ?></th>
-                                            <th class="ringcentral"><?php echo xlt("Message") ?></th>
-                                            <th><?php echo xlt("View") ?></th>
-                                        </tr>
-                                        </thead>
-                                        <tbody>
-                                        <tr>
-                                            <td><?php echo xlt("No Items Try Refresh") ?></td>
-                                        </tr>
-                                        </tbody>
-                                    </table>
+                             <div role="tabpanel" class="container-fluid tab-pane fade" id="sent">
+                                    <div class="table-responsive">
+                                        <table class="table table-sm table-striped" id="sent-details">
+                                            <thead>
+                                            <tr>
+                                                <th><?php echo xlt("Date") ?></th>
+                                                <th><?php echo xlt("Status") ?></th>
+                                                <th><?php echo xlt("From") ?></th>
+                                                <th><?php echo xlt("To") ?></th>
+                                                <th><?php echo xlt("Result") ?></th>
+                                                <th><?php echo xlt("Message") ?></th>
+                                                <th><?php echo xlt("Actions") ?></th>
+                                            </tr>
+                                            </thead>
+                                            <tbody>
+                                            <tr>
+                                                <td><?php echo xlt("No Items Try Refresh") ?></td>
+                                            </tr>
+                                            </tbody>
+                                        </table>
+                                    </div>
                                 </div>
-                            </div>
+                             <div role="tabpanel" class="container-fluid tab-pane fade" id="messages">
+                                    <div class="table-responsive">
+                                        <table class="table table-sm table-striped" id="msgdetails">
+                                            <thead>
+                                            <tr>
+                                                <th><?php echo xlt("Date") ?></th>
+                                                <th><?php echo xlt("Status") ?></th>
+                                                <th><?php echo xlt("From") ?></th>
+                                                <th><?php echo xlt("To") ?></th>
+                                                <th><?php echo xlt("Result") ?></th>
+                                                <th><?php echo xlt("Message") ?></th>
+                                                <th><?php echo xlt("Actions") ?></th>
+                                            </tr>
+                                            </thead>
+                                            <tbody>
+                                            <tr>
+                                                <td><?php echo xlt("No Items Try Refresh") ?></td>
+                                            </tr>
+                                            </tbody>
+                                        </table>
+                                    </div>
+                                </div>
+                            <?php } ?>
                             <div role="tabpanel" class="container-fluid tab-pane fade" id="logs">
                                 <div class="table-responsive">
                                     <table class="table table-sm table-striped" id="logdetails">
diff --git a/interface/modules/custom_modules/oe-module-faxsms/setup_rc.php b/interface/modules/custom_modules/oe-module-faxsms/setup_rc.php
index 18e0031e284..cd1b636abf0 100644
--- a/interface/modules/custom_modules/oe-module-faxsms/setup_rc.php
+++ b/interface/modules/custom_modules/oe-module-faxsms/setup_rc.php
@@ -35,7 +35,7 @@
     <title>><?php echo xlt("Setup") ?></title>
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <?php Header::setupHeader();
-    echo "<script>let Service=" . js_escape($service) . "</script>";
+    echo "<script>let Service=" . js_escape($serviceType) . "</script>";
     ?>
     <script>
         $(function () {
@@ -79,10 +79,10 @@
                     return false;
                 }
             });
-            if (Service === '2') {
-                $(".ringcentral").hide();
+            if (Service === 'fax') {
+                $(".smsHide").hide();
             } else {
-                $(".twilio").hide();
+                $(".faxHide").hide();
             }
         });
 
@@ -131,16 +131,17 @@ function togglePasswordVisibility(id) {
                             <input id="form_production" type="checkbox" name="production" <?php echo attr($c['production']) ? ' checked' : '' ?>>
                             <?php echo xlt("Production Check") ?>
                         </label>
+                        <p><?php echo xlt("Currently only JWT and Phone numbers are required as credentials."); ?></p>
                     </div>
                         <div class="form-group">
                             <label for="form_extension"><?php echo xlt("Extension") ?></label>
                             <input id="form_extension" type="text" name="extension" class="form-control" required="required" value='<?php echo attr($c['extension']) ?>'>
                         </div>
-                        <div class="form-group">
+                        <div class="form-group faxHide">
                             <label for="form_phone"><?php echo xlt("FAX Phone Number") ?></label>
                             <input type="tel" class="form-control" id="form_phone" name="phone" value='<?php echo attr($c['phone']) ?>'>
                         </div>
-                        <div class="form-group">
+                        <div class="form-group smsHide">
                             <label for="form_smsnumber"><?php echo xlt("SMS Phone Number") ?></label>
                             <input id="form_smsnumber" type="tel" name="smsnumber" class="form-control"
                                 value='<?php echo attr($c['smsNumber']) ?>'>
diff --git a/interface/modules/custom_modules/oe-module-faxsms/src/Controller/AppDispatch.php b/interface/modules/custom_modules/oe-module-faxsms/src/Controller/AppDispatch.php
index 341743b586d..8d521a39770 100644
--- a/interface/modules/custom_modules/oe-module-faxsms/src/Controller/AppDispatch.php
+++ b/interface/modules/custom_modules/oe-module-faxsms/src/Controller/AppDispatch.php
@@ -563,18 +563,6 @@ public static function getLoggedInUser(): array
         return $result;
     }
 
-    /**
-     * @return string|false
-     */
-    public function getPatientDetails(): bool|string
-    {
-        $id = $this->getRequest('pid');
-        $query = "SELECT fname, lname, phone_cell, email FROM patient_data WHERE pid = ?";
-        $result = sqlQuery($query, array($id));
-
-        return json_encode($result);
-    }
-
     /**
      *
      * @param $email
diff --git a/interface/modules/custom_modules/oe-module-faxsms/src/Controller/NotificationTaskManager.php b/interface/modules/custom_modules/oe-module-faxsms/src/Controller/NotificationTaskManager.php
new file mode 100644
index 00000000000..1796f66e1c0
--- /dev/null
+++ b/interface/modules/custom_modules/oe-module-faxsms/src/Controller/NotificationTaskManager.php
@@ -0,0 +1,138 @@
+<?php
+
+/**
+ *
+ * @package        OpenEMR
+ * @link           https://www.open-emr.org
+ * @author         Jerry Padgett <sjpadgett@gmail.com>
+ * @copyright      Copyright (c) 2025 <sjpadgett@gmail.com>
+ * @license        https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
+ */
+
+namespace OpenEMR\Modules\FaxSMS\Controller;
+
+/**
+ * Manages background task operations for Notifications.
+ */
+class NotificationTaskManager
+{
+    public function getTaskHours($type): int
+    {
+        if ($type == 'email') {
+            $name = 'Notification_Email_Task';
+        } elseif ($type == 'sms') {
+            $name = 'Notification_SMS_Task';
+        } else {
+            return 24;
+        }
+
+        // Get the interval in minutes
+        $total_minutes = sqlQueryNoLog("SELECT `execute_interval` as hours FROM `background_services` WHERE `name` = ?", [$name])['hours'] ?? 0;
+        return $total_minutes > 0 ? $total_minutes / 60 : 24;
+    }
+
+    /**
+     * Creates or updates the background Notification task
+     */
+    public function manageService($type, $hours = 0): bool
+    {
+        if ($type == 'sms') {
+            $name = 'Notification_SMS_Task';
+            $fn = 'doSmsNotificationTask';
+        } elseif ($type == 'email') {
+            $name = 'Notification_Email_Task';
+            $fn = 'doEmailNotificationTask';
+        } else {
+            return false;
+        }
+
+        if (empty($hours)) {
+            $total_minutes = $this->getTaskHours($type);
+        } else {
+            $total_minutes = $hours * 60;
+        }
+
+        $sql = "SELECT COUNT(*) as count FROM `background_services` WHERE `name` = ?";
+        $result = sqlQueryNoLog($sql, [$name]);
+        if ($result['count'] > 0) {
+            $sql = "UPDATE `background_services` SET `execute_interval` = ? WHERE `name` = ?";
+            sqlStatementNoLog($sql, [$total_minutes, $name]);
+            return false;
+        }
+
+        $sql = "INSERT INTO `background_services` 
+                (`name`, `title`, `active`, `running`, `next_run`, `execute_interval`, `function`, `require_once`, `sort_order`) 
+                VALUES (?, 'Scheduled Automated Notifications', '0', '0', current_timestamp(), ?, ?, '/interface/modules/custom_modules/oe-module-faxsms/library/run_notifications.php', '100')";
+        sqlStatementNoLog($sql, [$name, $total_minutes, $fn]);
+
+        return true;
+    }
+
+    /**
+     * Deletes the background Notification task.
+     */
+    public function deleteService($type)
+    {
+        if ($type == 'sms') {
+            $name = 'Notification_SMS_Task';
+        } elseif ($type == 'email') {
+            $name = 'Notification_Email_Task';
+        } else {
+            return false;
+        }
+
+        $sql = "DELETE FROM `background_services` WHERE `name` = ?";
+        sqlStatementNoLog($sql, [$name]);
+    }
+
+    /**
+     * Enables the background Notification task.
+     */
+    public function enableService($type, $period = 24)
+    {
+        if ($type == 'sms') {
+            $name = 'Notification_SMS_Task';
+        } elseif ($type == 'email') {
+            $name = 'Notification_Email_Task';
+        } else {
+            return false;
+        }
+        $this->manageService($type, $period);
+
+        $sql = "UPDATE `background_services` SET `active` = '1' WHERE `name` = ?";
+        sqlStatementNoLog($sql, [$name]);
+    }
+
+    /**
+     * Disables the background Notification task.
+     */
+    public function disableService($type)
+    {
+        if ($type == 'sms') {
+            $name = 'Notification_SMS_Task';
+        } elseif ($type == 'email') {
+            $name = 'Notification_Email_Task';
+        } else {
+            return false;
+        }
+
+        $sql = "UPDATE `background_services` SET `active` = '0' WHERE `name` = ?";
+        sqlStatementNoLog($sql, [$name]);
+    }
+
+    public function getServiceStatus($type): false|array|string
+    {
+        if ($type == 'sms') {
+            $name = 'Notification_SMS_Task';
+        } elseif ($type == 'email') {
+            $name = 'Notification_Email_Task';
+        } else {
+            return false;
+        }
+
+        $sql = "SELECT * FROM `background_services` WHERE `name` = ?";
+        $result[$type] = sqlQueryNoLog($sql, [$name]);
+
+        return $result;
+    }
+}
diff --git a/interface/modules/custom_modules/oe-module-faxsms/src/Controller/RCFaxClient.php b/interface/modules/custom_modules/oe-module-faxsms/src/Controller/RCFaxClient.php
index 43b7c4d0aa9..14ff37b2a96 100644
--- a/interface/modules/custom_modules/oe-module-faxsms/src/Controller/RCFaxClient.php
+++ b/interface/modules/custom_modules/oe-module-faxsms/src/Controller/RCFaxClient.php
@@ -115,11 +115,11 @@ private function loginWithJWT(): int|string
                 return 1;
             }
         } catch (ApiException $e) {
-            return "API Error: " . text($e->getMessage()) . " - " . text($e->getCode());
+            return js_escape(['error' => "API Error: " . text($e->getMessage()) . " - " . text($e->getCode())]);
         } catch (Exception $e) {
-            return "Error: " . text($e->getMessage());
+            return js_escape(['error' => "Error: " . text($e->getMessage())]);
         }
-        return "Login with JWT failed.";
+        return js_escape(['error' => "Login with JWT failed."]);
     }
 
     /**
@@ -220,8 +220,7 @@ public function sendSMS($toPhone = '', $subject = '', $message = '', $from = '')
                     'to' => [['phoneNumber' => $toPhone]],
                     'text' => $message,
                 ]);
-                sleep(1.25);
-                // RC may only allow 1/second.
+                sleep(1); // Sleep to avoid rate limit 10 per minute
                 return true;
             } catch (ApiException $e) {
                 return text("API Error: " . $e->getMessage() . " - " . $e->getCode());
@@ -230,6 +229,31 @@ public function sendSMS($toPhone = '', $subject = '', $message = '', $from = '')
 
         return true;
     }
+    /**
+     * @return string
+     */
+    public function fetchTextMessage(): string
+    {
+        $id = $_REQUEST['id'] ?? null;
+        $uri = $_REQUEST['uri'] ?? null;
+
+        if (empty($id) || empty($uri)) {
+            return "Missing id or uri parameters.";
+        }
+
+        $authResult = $this->authenticateRingCentral();
+        if ($authResult !== 1) {
+            return $authResult;
+        }
+
+        try {
+            $response = $this->platform->get($uri);
+            return js_escape((string)$response->text());
+        } catch (Exception $e) {
+            $responseMsg = "<tr><td>" . text($e->getMessage()) . "</td></tr>";
+            return json_encode(['error' => $responseMsg]);
+        }
+    }
 
     /**
      * @return array
@@ -244,6 +268,7 @@ public function getCredentials(): array
 
     /**
      * API Endpoint for sending
+     *
      * @return string
      */
     public function forwardFax(): string
@@ -797,6 +822,39 @@ public function downloadFax(string $messageId): string
             return text(json_encode(['error' => "Error: " . $e->getMessage()]));
         }
     }
+    /**
+     * @param string $phone
+     * @return string|bool
+     */
+    public function findPatientByPhone(string $phone): bool|string
+    {
+        if (empty($phone)) {
+            return '';
+        }
+        $normalizedPhone = preg_replace('/[^0-9]/', '', $phone);
+        if (strlen($normalizedPhone) === 11 && str_starts_with($normalizedPhone, '1')) {
+            $normalizedPhone = substr($normalizedPhone, 1);
+        }
+
+        $likePhone = "%" . $normalizedPhone;
+
+        $sql = "
+        SELECT CONCAT(fname, ' ', lname) AS fullname
+        FROM patient_data
+        WHERE REPLACE(REPLACE(REPLACE(REPLACE(phone_cell, '-', ''), '(', ''), ')', ''), ' ', '') LIKE ?
+        LIMIT 1
+    ";
+
+        $result = sqlQuery($sql, [$likePhone]);
+        $rtn = $result['fullname'] ?? '';
+        if (!empty($rtn)) {
+            $rtn .= ' ';
+        } else {
+            $rtn = '';
+        }
+
+        return $rtn;
+    }
 
     /**
      * @return string
@@ -813,7 +871,7 @@ public function getUser(): string
     /**
      * @return string
      */
-   /* public function getNotificationLog(): string
+    public function getNotificationLog(): string
     {
         $type = $this->getRequest('type');
         $fromDate = $this->getRequest('datefrom');
@@ -833,7 +891,7 @@ public function getUser(): string
         }
 
         return $responseMsg;
-    }*/
+    }
 
     /**
      * @return string
@@ -894,42 +952,53 @@ public function getCallLogs(): string
      */
     public function getPending(): false|string
     {
+        // Authenticate and refresh token if needed
         $authErrorMsg = $this->authenticate();
         if ($authErrorMsg !== 1) {
             return json_encode(['error' => js_escape($authErrorMsg)]);
         }
 
+        // Get the date range and service type from the request
         $dateFrom = $this->getRequest('datefrom');
         $dateTo = $this->getRequest('dateto');
         $serviceType = $this->getRequest('type', '');
 
         try {
-            $messageStoreDir = $this->baseDir;
-
-            if (!file_exists($messageStoreDir) && !mkdir($messageStoreDir, 0777, true) && !is_dir($messageStoreDir)) {
-                throw new \RuntimeException(sprintf('Directory "%s" was not created', $messageStoreDir));
-            }
-
             $dateFrom .= 'T00:00:01.000Z';
             $dateTo .= 'T23:59:59.000Z';
-
-            $messageStoreList = $this->platform->get('/account/~/extension/~/message-store', [
-                'dateFrom' => $dateFrom,
-                'dateTo' => $dateTo,
-            ])->json()->records;
+            $serviceType = strtolower($serviceType);
+            // Fetch the message store list based on the service type
+            if ($serviceType == 'sms') {
+                $messageStoreList = $this->platform->get('/account/~/extension/~/message-store', [
+                    'dateFrom' => $dateFrom,
+                    'dateTo' => $dateTo,
+                    'messageType' => 'SMS',
+                ])->json()->records;
+            } else {
+                $messageStoreList = $this->platform->get('/account/~/extension/~/message-store', [
+                    'dateFrom' => $dateFrom,
+                    'dateTo' => $dateTo,
+                    'messageType' => 'Fax',
+                ])->json()->records;
+            }
 
             $responseMsg = $this->processMessageStoreList($messageStoreList, $serviceType);
         } catch (ApiException $e) {
-            $responseMsg = "<tr><td>" . text($e->getMessage()) . " : " . xlt('Ensure account credentials are correct.') . "</td></tr>";
+            $responseMsg = "<tr><td>" . text($e->getMessage()) . " : " . xlt('Report to Administration.') . "</td></tr>";
             return json_encode(['error' => $responseMsg]);
         }
 
         return json_encode($responseMsg ?: [xlt("Nothing to report"), xlt("Nothing to report"), xlt("Nothing to report")]);
     }
 
-    private function processMessageStoreList($messageStoreList, $serviceType): array
+    private function processMessageStoreList($messageStoreList, $serviceType): false|array|string
     {
         $responseMsg = [];
+        $count = count($messageStoreList ?? []);
+        $timePerMessageStore = 1; // seconds
+        $start = microtime();
+        $useLink = false;
+        $cnt = 0;
         foreach ($messageStoreList as $messageStore) {
             if (property_exists($messageStore, 'attachments')) {
                 foreach ($messageStore->attachments as $attachment) {
@@ -945,14 +1014,51 @@ private function processMessageStoreList($messageStoreList, $serviceType): array
                     $checkbox = "<input type='checkbox' class='delete-fax-checkbox' value='" . attr($id) . "'>";
                     $type = strtolower($messageStore->type);
                     $direction = strtolower($messageStore->direction);
-                    $readStatus = $messageStore->readStatus;
-                    if ($type === "sms") {
-                        $messageText = $this->getMessageContent($uri);
-                        $responseMsg[2] .= "<tr><td>" . text($faxFormattedDate) . "</td><td>" . text($messageStore->type) . "</td><td>" . text($from) . "</td><td>" . text($to) . "</td><td>" . text($status) . "</td><td><span class='$id'>" . text(substr($messageText, 0, 30)) . "</span><div class='d-none $id'>" . text($messageText) . "</div></td><td class='btn-group'>" . attr($links['sms']) . "</td><td class='text-center'>" . $checkbox . "</td></tr>";
-                    } elseif ($direction === "inbound" && $type === $serviceType) {
+                    $messageText = '';
+                    if ($type === "sms" && $type === $serviceType) {
+                        if ($direction === "inbound") {
+                            $links = $this->generateSmsActionLinks($id, $uri, $messageStore->from->phoneNumber ?? '');
+                            $pname = $this->findPatientByPhone($messageStore->from->phoneNumber ?? '');
+                            try {
+                                if (!$useLink) {
+                                    $response = $this->platform->get($uri);
+                                    $messageText = (string)$response->text();
+                                    $messageText = str_replace("\n", "<br>", $messageText);
+                                    sleep(0.5); // Sleep to avoid rate limit 10 per minute
+                                    $cnt++;
+                                    if ($cnt >= 9) {
+                                        $authErrorMsg = $this->authenticate();
+                                        if ($authErrorMsg !== 1) {
+                                            return json_encode(['error' => js_escape($authErrorMsg)]);
+                                        }
+                                        $cnt = 0;
+                                    }
+                                } else {
+                                    $messageText = xlt("Text retrieval error. Click show message");
+                                }
+                            } catch (ApiException $e) {
+                                $messageText = "Error: " . text($e->getMessage());
+                                if ($e->getCode() == 401) {
+                                    $useLink = true;
+                                }
+                            }
+                            $responseMsg[0] .= "<tr><td>" . text($faxFormattedDate) . "</td><td>" . text($messageStore->readStatus) . "</td><td>" . text($pname . $from) . "</td><td>" . text($to) . "</td><td>" . text($status) . "</td><td><div class='$id'>" . text($messageText) . "</div></td><td class='btn-group'>" . $links['sms'] . "</td></tr>";
+                        } elseif ($direction === "outbound") {
+                            $links = $this->generateSmsActionLinks($id, $uri, $messageStore->to[0]->phoneNumber ?? '');
+                            $pname = $this->findPatientByPhone($messageStore->to->phoneNumber ?? '');
+                            $responseMsg[1] .= "<tr><td>" . text($faxFormattedDate) . "</td><td>" . text($messageStore->readStatus) . "</td><td>" . text($from) . "</td><td>" . text($pname . $to) . "</td><td>" . text($status) . "</td><td><div class='$id'>" . text($messageText) . "</div></td><td class='btn-group'>" . $links['sms'] . "</td></tr>";
+                        }
+                        $toName = $to;
+                        $fromName = $pname . $from;
+                        if ($direction === "outbound") {
+                            $toName = $pname . $to;
+                            $fromName = $from;
+                        }
+                        $responseMsg[2] .= "<tr><td>" . text($faxFormattedDate) . "</td><td>" . text($messageStore->readStatus) . "</td><td>" . text($fromName) . "</td><td>" . text($toName) . "</td><td>" . text($status) . "</td><td><div class='$id'>" . text($messageText) . "</div></td><td class='btn-group'>" . $links['sms'] . "</td></tr>";
+                    } elseif ($direction === "inbound" && $type === $serviceType && $serviceType === "fax") {
                         $status = $messageStore->to[0]->faxErrorCode ?: $messageStore->messageStatus;
                         $responseMsg[0] .= "<tr><td>" . text($faxFormattedDate) . "</td><td>" . text($updateDate) . "</td><td>" . text($messageStore->faxPageCount) . "</td><td>" . text($from) . "</td><td>" . text($messageStore->subject) . "</td><td>" . text($status) . "</td><td class='text-left'>" . $links['inbound'] . "</td><td class='text-center'>" . $checkbox . "</td></tr>";
-                    } elseif ($direction === "outbound" && $type === $serviceType) {
+                    } elseif ($direction === "outbound" && $type === $serviceType && $serviceType === "fax") {
                         $status = $messageStore->to[0]->faxErrorCode ?: $messageStore->messageStatus;
                         $responseMsg[1] .= "<tr><td>" . text($faxFormattedDate) . "</td><td>" . text($updateDate) . "</td><td>" . text($messageStore->faxPageCount) .
                             "</td><td>" . text($from) . "</td><td>" . text($to) . "</td><td>" . text($status) . "</td><td>" . $links['outbound'] . "</td><td class='text-center'>" . $checkbox . "</td></tr>";
@@ -972,73 +1078,21 @@ private function generateActionLinks($id, $uri): array
         $viewLink = "<a role='button' href='javascript:void(0)' onclick=\"getDocument(event, null, " . attr_js($id) . ", 'false')\"> <i class='fa fa-file-pdf mr-2' title='" . xla("View fax document") . "'></i></a>";
         $deleteLink = "<a role='button' href='javascript:void(0)' onclick=\"getDocument(event, null, " . attr_js($id) . ", 'false', 'true')\"> <i class='text-danger fa fa-trash mr-2' title='" . xla("Delete this fax document") . "'></i></a>";
         $forwardLink = "<a role='button' href='javascript:void(0)' onclick=\"forwardFax(event, " . attr_js($id) . ")\"> <i class='fa fa-forward mr-2' title='" . xla("Forward fax to new fax recipient or email attachment.") . "'></i></a>";
-
-        $vtoggle = "<a href='javascript:' onclick=messageShow(" . attr_js($id) . ")><span class='mx-1 fa fa-eye-slash fa-1x'></span></a>";
-        $vreply = "<a href='javascript:' onclick=messageReply(" . attr_js($id) . ")><span class='mx-1 fa fa-reply'></span></a>";
-
         return [
-            'sms' => $vtoggle . $vreply,
             'inbound' => $patientLink . $messageLink . $forwardLink . $viewLink . $downloadLink . $deleteLink,
             'outbound' => $viewLink . $downloadLink . $deleteLink
         ];
     }
 
-    /**
-     * @param array $responseMsg
-     * @param       $messageStore
-     * @param       $attachment
-     * @return void
-     */
-    private function formatMessageStore(array &$responseMsg, $messageStore, $attachment): void
+    private function generateSmsActionLinks($id, $uri, $phone): array
     {
-        $id = $attachment->id;
-        $uri = $attachment->uri;
-        $to = $messageStore->to[0]->name . " " . $messageStore->to[0]->phoneNumber;
-        $from = $messageStore->from->name . " " . $messageStore->from->phoneNumber;
-        $errors = $messageStore->to[0]->faxErrorCode ? "why: " . $messageStore->to[0]->faxErrorCode : $messageStore->from->faxErrorCode;
-        $status = $messageStore->messageStatus . " " . $errors;
-        $patientLink = "<a role='button' href='javascript:void(0)' onclick=\"createPatient(event, " . attr_js($id) . ", " . attr_js($id) . ", " . attr_js(json_encode($parse ?? [])) . ")\"> <i class='fa fa-chart-simple mr-2' title='" . xla("Chart fax or Create patient and chart fax to documents.") . "'></i></a>";
-        $messageLink = "<a role='button' href='javascript:void(0)' onclick=\"notifyUser(event, " . attr_js($id) . ", " . attr_js($id) . ", " . attr_js(($pid_assumed ?? 0)) . ")\"> <i class='fa fa-paper-plane mr-2' title='" . xla("Notify a user and attach this fax to message.") . "'></i></a>";
-        $downloadLink = "<a role='button' href='javascript:void(0)' onclick=\"getDocument(event, null, " . attr_js($id) . ", 'true')\"> <i class='fa fa-file-download mr-2' title='" . xla("Download and delete fax") . "'></i></a>";
-        $viewLink = "<a role='button' href='javascript:void(0)' onclick=\"getDocument(event, null, " . attr_js($id) . ", 'false')\"> <i class='fa fa-file-pdf mr-2' title='" . xla("View fax document") . "'></i></a>";
-        $deleteLink = "<a role='button' href='javascript:void(0)' onclick=\"getDocument(event, null, " . attr_js($id) . ", 'false', 'true')\"> <i class='text-danger fa fa-trash mr-2' title='" . xla("Delete this fax document") . "'></i></a>";
-        $forwardLink = "<a role='button' href='javascript:void(0)' onclick=\"forwardFax(event, " . attr_js($id) . ")\"> <i class='fa fa-forward mr-2' title='" . xla("Forward fax to new fax recipient or email attachment.") . "'></i></a>";
-
-        $faxFormattedDate = date('M j, Y g:i:sa T', strtotime($messageStore->lastModifiedTime));
-        $docLen = text(round(1024 / 1024, 2)) . "KB"; // todo add length
-        $responseMsg[0] .= "<tr><td>" . text($faxFormattedDate) . "</td><td>" . text($from) . "</td><td>" . text('todo: add caller id') . "</td><td>" . text($to) . "</td><td>" . text($messageStore->PagesReceived) . "</td><td>" . text($docLen) . "</td><td class='text-left'>" . /*$detailLink .*/
-            "</td><td class='text-center'>" . text($pid_assumed ?? '') . "</td><td class='text-left'>" . $patientLink . $messageLink . $forwardLink . $viewLink . $downloadLink . $deleteLink . "</td></tr>";
-        //$responseMsg[0] .= $form;
-        $aUrl = "<a href='#' onclick=getDocument(event," . attr_js($uri) . "," . attr_js($id) . ",'true')>" . text($id) . " <span class='fa fa-download'></span></a></br>";
-        $vUrl = "<a href='#' onclick=getDocument(event," . attr_js($uri) . "," . attr_js($id) . ",'false')> <span class='fa fa-file-pdf-o'></span></a></br>";
-        if ($status != 'failed' && $this->formatPhone($this->credentials['smsNumber']) != $messageStore->from) {
-            $vreply = "<a href='javaScript:' onclick=messageReply(" . attr_js($messageStore->from) . ")><span class='mx-1 fa fa-reply'></span></a>";
-        } else {
-            $vreply = "<a href='#' title='SMS failure'> <span class='fa fa-file-pdf text-danger'></span></a></br>";
-        }
-        $row = "<tr>
-                <td>" . text(str_replace(["T", "Z"], " ", $messageStore->lastModifiedTime)) . "</td>
-                <td>" . text($messageStore->type) . "</td>
-                <td>" . text($from) . "</td>
-                <td>" . text($to) . "</td>
-                <td>" . text($status) . "</td>
-                <td>" . ($aUrl) . "</td>
-                <td>" . ($vUrl) . "</td>";
-        if (strtolower($messageStore->type) === "sms") {
-            $row .= "<td>" . ($vreply) . "</td>";
-        }
+        $vtoggle = "<a href='javascript:' onclick=messageShow(" . attr_js($id) . "," . attr_js($uri) . ")><span class='mx-1 fas fa-comment fa-1x'></span></a>";
+        $vreply = "<a href='javascript:' onclick=messageReply(" . attr_js($phone) . ")><span class='mx-1 fa fa-reply'></span></a>";
 
-        $row .= "</tr>";
-        if (strtolower($messageStore->type) === "sms") {
-            $responseMsg[2] .= $row;
-            // sms
-        } elseif (strtolower($messageStore->direction) === "inbound") {
-            $responseMsg[0] .= $row;
-// in fax
-        } else {
-            $responseMsg[1] .= $row;
-// out fax
-        }
+        return [
+            'sms' => $vtoggle . $vreply,
+            'smsoutbound' => $vreply
+        ];
     }
 
     /**
@@ -1057,63 +1111,6 @@ public function formatPhone($number): string
         return $n;
     }
 
-    /**
-     * @return string|void
-     */
-    public function getMessage()
-    {
-        $authErrorMsg = $this->authenticate();
-        if ($authErrorMsg !== 1) {
-            return text(js_escape($authErrorMsg));
-            // goes to alert
-        }
-
-        try {
-            $messageStoreDir = $this->baseDir;
-            if (!file_exists($messageStoreDir)) {
-                mkdir($messageStoreDir, 0777, true);
-            }
-
-            $messageStoreList = $this->platform->get('/account/~/extension/~/message-store', [
-                'messageType' => "",
-                'dateFrom' => '2018-05-01'
-            ])->json()->records;
-            $timePerMessageStore = 6;
-            $responseMsgs = "";
-            foreach ($messageStoreList as $messageStore) {
-                if (property_exists($messageStore, 'attachments')) {
-                    foreach ($messageStore->attachments as $attachment) {
-                        $id = $attachment->id;
-                        $uri = $attachment->uri;
-                        try {
-                            $apiResponse = $this->platform->get($uri);
-                        } catch (ApiException $e) {
-                            $responseMsgs .= "<tr><td>Errors: " . text($e->getMessage()) . $e->apiResponse()->request()->getUri()->__toString() . "</td></tr>";
-                            continue;
-                        }
-
-                        $ext = $this->getExtensionFromContentType($apiResponse->response()->getHeader('Content-Type')[0]);
-                        $type = $this->getTypeFromContentType($apiResponse->response()->getHeader('Content-Type')[0]);
-                        $start = microtime(true);
-                        file_put_contents("{$messageStoreDir}/{$type}_{$id}.{$ext}", $apiResponse->raw());
-                        $responseMsgs .= "<tr><td>" . $messageStore->creationTime . "</td><td>" . $messageStore->type . "</td><td>" . $messageStore->from->name . "</td><td>" . $messageStore->to->name . "</td><td>" . $messageStore->availability . "</td><td>" . $messageStore->messageStatus . "</td><td>" . $messageStore->message->id . "</td></tr>";
-                        $end = microtime(true);
-                        $time = ($end - $start);
-                        if ($time < $timePerMessageStore) {
-                            sleep($timePerMessageStore - $time);
-                        }
-                    }
-                } else {
-                    echo xlt("Does not have messages") . PHP_EOL;
-                }
-            }
-        } catch (ApiException $e) {
-            echo "<tr><td>Error: " . text($e->getMessage() . $e->apiResponse()->request()->getUri()->__toString()) . "</td></tr>";
-        }
-
-        exit;
-    }
-
     /**
      * @return string|null
      */
@@ -1156,7 +1153,7 @@ public function fetchReminderCount(): string|bool
                 'availability' => 'Alive'
             ]);
             $json = $response->json();
-            return (string) text(count($json->records));
+            return (string)text(count($json->records));
         } catch (Exception $e) {
             error_log('Error fetching incoming faxes: ' . text($e->getMessage()));
             return false;
diff --git a/interface/modules/custom_modules/oe-module-faxsms/src/Controller/TwilioSMSClient.php b/interface/modules/custom_modules/oe-module-faxsms/src/Controller/TwilioSMSClient.php
index 0e1c314fa34..96b0bc65629 100644
--- a/interface/modules/custom_modules/oe-module-faxsms/src/Controller/TwilioSMSClient.php
+++ b/interface/modules/custom_modules/oe-module-faxsms/src/Controller/TwilioSMSClient.php
@@ -171,7 +171,7 @@ public function _getPending()
                 ], 100);
             } catch (Exception $e) {
                 $message = $e->getMessage();
-                $emsg = xlt('Ensure account credentials are correct.');
+                $emsg = xlt('Report to Administration');
                 return json_encode(array('error' => $message . " : " . $emsg));
             }
 
@@ -210,7 +210,7 @@ public function _getPending()
             }
         } catch (Exception $e) {
             $message = $e->getMessage();
-            $responseMsgs = "<tr><td>" . text($message) . " : " . xlt('Ensure account credentials are correct.') . "</td></tr>";
+            $responseMsgs = "<tr><td>" . text($message) . " : " . xlt('Report to Administration') . "</td></tr>";
             echo json_encode(array('error' => $responseMsgs));
             exit();
         }
diff --git a/interface/modules/custom_modules/oe-module-faxsms/version.php b/interface/modules/custom_modules/oe-module-faxsms/version.php
index b6b0cc44cfb..2760817a5be 100644
--- a/interface/modules/custom_modules/oe-module-faxsms/version.php
+++ b/interface/modules/custom_modules/oe-module-faxsms/version.php
@@ -6,6 +6,6 @@
  * but not fully implemented.
  * */
 
-$v_major = '4';
-$v_minor = '1';
+$v_major = '5';
+$v_minor = '0';
 $v_patch = '0';


Test patch: