7 * @link https://www.open-emr.org
8 * @author Jerry Padgett <sjpadgett@gmail.com>
9 * @author Brady Miller <brady.g.miller@gmail.com>
10 * @author Tyler Wrenn <tyler@tylerwrenn.com>
11 * @copyright Copyright (c) 2016-2021 Jerry Padgett <sjpadgett@gmail.com>
12 * @copyright Copyright (c) 2019 Brady Miller <brady.g.miller@gmail.com>
13 * @copyright Copyright (c) 2020 Tyler Wrenn <tyler@tylerwrenn.com>
14 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
17 // Will start the (patient) portal OpenEMR session/cookie.
18 require_once(__DIR__
. "/../../src/Common/Session/SessionUtil.php");
19 OpenEMR\Common\Session\SessionUtil
::portalSessionStart();
21 if (isset($_SESSION['pid']) && isset($_SESSION['patient_portal_onsite_two'])) {
22 $pid = $_SESSION['pid'];
23 $ignoreAuth_onsite_portal = true;
24 require_once(__DIR__
. "/../../interface/globals.php");
25 define('IS_DASHBOARD', false);
26 define('IS_PORTAL', $_SESSION['portal_username']);
28 OpenEMR\Common\Session\SessionUtil
::portalSessionCookieDestroy();
30 require_once(__DIR__
. "/../../interface/globals.php");
31 if (!isset($_SESSION['authUserID'])) {
32 $landingpage = "index.php";
33 header('Location: ' . $landingpage);
37 define('IS_DASHBOARD', $_SESSION['authUser']);
38 define('IS_PORTAL', false);
41 require_once("$srcdir/patient.inc.php");
42 require_once("$srcdir/options.inc.php");
43 require_once("$srcdir/classes/Document.class.php");
44 require_once("./../lib/portal_mail.inc.php");
46 use OpenEMR\Common\Csrf\CsrfUtils
;
47 use OpenEMR\Core\Header
;
48 use OpenEMR\Events\Messaging\SendSmsEvent
;
50 if (!(isset($GLOBALS['portal_onsite_two_enable'])) ||
!($GLOBALS['portal_onsite_two_enable'])) {
51 echo xlt('Patient Portal is turned off');
55 $docid = empty($_REQUEST['docid']) ?
0 : (int)$_REQUEST['docid'];
56 $orderid = empty($_REQUEST['orderid']) ?
0 : (int)$_REQUEST['orderid'];
58 $result = getMails(IS_DASHBOARD ?
: IS_PORTAL
, 'inbox', '', '');
60 foreach ($result as $iter) {
66 $dashuser = getUserIDInfo($_SESSION['authUserID']);
69 function getAuthPortalUsers()
71 $resultpd = $resultusers = $resultpatients = array();
73 if (IS_DASHBOARD
) { // admin can mail anyone
74 $authusers = sqlStatement("SELECT users.username as userid,
75 CONCAT(users.fname,' ',users.lname) as username, 'user' as type FROM users WHERE active = 1 AND portal_user = 1");
76 while ($row = sqlFetchArray($authusers)) {
77 $resultusers[] = $row;
79 if (count($resultusers ??
[]) === 0) {
80 $resultusers[] = sqlQuery("SELECT users.username as userid,
81 CONCAT(users.fname,' ',users.lname) as username, 'user' as type FROM users WHERE id = 1");
84 $authpatients = sqlStatement("SELECT (CONCAT(patient_data.fname, patient_data.id)) as userid,
85 CONCAT(patient_data.fname,' ',patient_data.lname) as username,'p' as type,patient_data.pid as pid FROM patient_data WHERE allow_patient_portal = 'YES'");
86 while ($row = sqlFetchArray($authpatients)) {
87 $resultpatients[] = $row;
90 $resultpd = array_merge($resultusers, $resultpatients);
91 } else { // patient gets only portal users
93 $authusers = sqlStatement("SELECT users.username as userid, CONCAT(users.fname,' ',users.lname) as username FROM users WHERE active = 1 AND portal_user = 1");
94 while ($row = sqlFetchArray($authusers)) {
97 if (count($resultpd ??
[]) === 0) {
98 $resultpd[] = sqlQuery("SELECT users.username as userid, CONCAT(users.fname,' ',users.lname) as username FROM users WHERE id = 1");
109 <meta charset
="utf-8" />
112 Header
::setupHeader(['no_main-theme', 'portal-theme', 'summernote', 'angular', 'angular-summernote', 'angular-sanitize', 'checklist-model', 'dompurify']);
114 Header
::setupHeader(['summernote', 'angular', 'angular-summernote', 'angular-sanitize', 'checklist-model', 'dompurify']);
117 <title
><?php
echo xlt("Secure Messaging"); ?
></title
>
118 <meta name
="description" content
="Mail Application" />
120 <body
class="body_top">
123 var app
= angular
.module("emrMessageApp", ['ngSanitize', 'summernote', "checklist-model"]);
124 app
.controller('inboxCtrl', ['$scope', '$filter', '$http', '$window', '$q', function ($scope, $filter, $http, $window, $q) {
125 $scope.date
= new Date
;
126 $scope.sortingOrder
= 'id';
127 $scope.pageSizes
= [5, 10, 20, 50, 100];
128 $scope.reverse
= false;
129 $scope.filteredItems
= [];
130 $scope.groupedItems
= [];
131 $scope.itemsPerPage
= 20;
132 $scope.pagedItems
= [];
134 $scope.selrecip
= [];
135 $scope.currentPage
= 0;
136 $scope.sentItems
= [];
137 $scope.allItems
= [];
138 $scope.deletedItems
= [];
139 $scope.inboxItems
= [];
140 $scope.inboxItems
= <?php
echo json_encode($theresult);?
>;
141 $scope.userproper
= <?php
echo !empty($_SESSION['ptName']) ?
js_escape($_SESSION['ptName']) : js_escape($dashuser['fname'] . ' ' . $dashuser['lname']);?
>;
142 $scope.isPortal
= "<?php echo IS_PORTAL;?>";
143 $scope.isDashboard
= "<?php echo IS_DASHBOARD ?: 0;?>";
144 $scope.cUserId
= $scope.isPortal ?
$scope.isPortal
: $scope.isDashboard
;
145 $scope.authrecips
= <?php
echo json_encode(getAuthPortalUsers());?
>;
146 $scope.compose
.task
= 'add';
148 $scope.xLate
.confirm
= [];
149 $scope.xLate
.fwd
= <?php
echo xlj('Forwarded Portal Message Re: '); ?
>;
150 $scope.xLate
.confirm
.one
= <?php
echo xlj('Confirm to Archive Current Thread?'); ?
>;
151 $scope.xLate
.confirm
.all
= <?php
echo xlj('Confirm to Archive Selected Messages?'); ?
>;
152 $scope.xLate
.confirm
.err
= <?php
echo xlj('You are sending to yourself!'); ?
>; // I think I got rid of this ability - look into..
153 $scope.csrf
= <?php
echo js_escape(CsrfUtils
::collectCsrfToken('messages-portal')); ?
>;
154 $scope.isInit
= false;
156 $scope.init
= function () {
157 $http.defaults
.headers
.post
["Content-Type"] = "application/x-www-form-urlencoded";
159 promises
.push($scope.getSentMessages());
160 promises
.push($scope.getAllMessages());
161 promises
.push($scope.getDeletedMessages());
162 $scope.isInboxSelected();
164 $scope.errorLoadingMessages
= false;
167 $scope.isInit
= true;
170 $scope.errorLoadingMessages
= true;
171 $scope.isInit
= true;
175 const searchMatch
= function (haystack
, needle
) {
179 return haystack
.toLowerCase().indexOf(needle
.toLowerCase()) !== -1;
183 $scope.search
= function () {
184 $scope.filteredItems
= $filter('filter')($scope.items
, function (item
) {
185 for (var attr in item
) {
186 if (searchMatch(item
[attr
], $scope.query
))
191 $scope.currentPage
= 0;
192 // now group by pages
193 $scope.groupToPages();
196 // calculate page in place
197 $scope.groupToPages
= function () {
198 $scope.selected
= null;
199 $scope.pagedItems
= [];
200 for (let i
= 0; i
< $scope.filteredItems
.length
; i++
) {
201 if (i %
$scope.itemsPerPage
=== 0) {
202 $scope.pagedItems
[Math
.floor(i
/ $scope.itemsPerPage
)] = [$scope.filteredItems
[i
]];
204 $scope.pagedItems
[Math
.floor(i
/ $scope.itemsPerPage
)].push($scope.filteredItems
[i
]);
209 $scope.range
= function (start
, end
) {
215 for (var i
= start
; i
< end
; i++
) {
221 $scope.prevPage
= function () {
222 if ($scope.currentPage
> 0) {
223 $scope.currentPage
--;
228 $scope.nextPage
= function () {
229 if ($scope.currentPage
< $scope.pagedItems
.length
- 1) {
230 $scope.currentPage++
;
235 $scope.setPage
= function () {
236 $scope.currentPage
= this
.n
;
239 $scope.deleteItem
= function (idx
) {
240 if (!confirm($scope.xLate
.confirm
.one
)) return false;
241 const itemToDelete
= $scope.allItems
[idx
];
242 const idxInItems
= $scope.items
.indexOf(itemToDelete
);
243 $scope.deleteMessage(itemToDelete
.mail_chain
); // Just this user's message
244 $scope.items
.splice(idxInItems
, 1);
250 $scope.batchDelete
= function (i
) {
251 if (!confirm($scope.xLate
.confirm
.all
)) return false;
252 var itemToDelete
= [];
253 angular
.forEach(i
, function (o
, key
) {
254 if (o
.hasOwnProperty('deleted')) {
255 itemToDelete
.push($scope.items
[i
.indexOf(o
)].id
);
258 $http.post('handle_note.php', $
.param({'task': 'massdelete', 'notejson': JSON
.stringify(itemToDelete
), 'csrf_token_form': $scope.csrf
})).then(function successCallback(response
) {
259 $window.location
.reload();
260 }, function errorCallback(response
) {
261 alert(response
.data
);
266 $scope.deleteMessage
= function (id
) {
267 $http.post('handle_note.php', $
.param({'task': 'delete', 'noteid': id
, 'csrf_token_form': $scope.csrf
})).then(function successCallback(response
) {
269 }, function errorCallback(response
) {
270 alert(response
.data
);
275 $scope.isMessageSelected
= function () {
276 return typeof
$scope.selected
!== "undefined" && $scope.selected
!== null;
279 $scope.isSentSelected
= function () {
280 $scope.isSent
= true;
281 $scope.isTrash
= $scope.isAll
= $scope.isInbox
= false;
283 $scope.items
= $scope.sentItems
;
288 $scope.isTrashSelected
= function () {
289 $scope.isTrash
= true;
290 $scope.isSent
= $scope.isAll
= $scope.isInbox
= false;
292 $scope.items
= $scope.deletedItems
;
297 $scope.isInboxSelected
= function () {
298 $scope.isInbox
= true;
299 $scope.isTrash
= $scope.isAll
= $scope.isSent
= false;
300 $scope.items
= $scope.inboxItems
;
305 $scope.isAllSelected
= function () {
307 $scope.isTrash
= $scope.isSent
= $scope.isInbox
= false;
308 $scope.items
= $scope.allItems
;
313 $scope.readMessage
= function (idx
) {
314 if ($scope.items
[idx
].message_status
== 'New') { // mark mail read else ignore
315 $http.post('handle_note.php', $
.param({'task': 'setread', 'noteid': $scope.items
[idx
].id
, 'csrf_token_form': $scope.csrf
})).then(function successCallback(response
) {
316 $scope.items
[idx
].message_status
= 'Read';
317 $scope.selected
.message_status
= 'Read';
318 }, function errorCallback(response
) {
319 alert(response
.data
);
322 idx
= $filter('getById')($scope.allItems
, this
.item
.id
);
324 $scope.isTrash
= $scope.isSent
= $scope.isInbox
= false;
325 $scope.items
= $scope.allItems
;
326 $scope.selected
= $scope.items
[idx
];
329 $scope.selMessage
= function (idx
) {
330 $scope.selected
= $scope.allItems
[idx
];
334 $scope.readAll
= function () {
335 for (var i in
$scope.items
) {
336 $scope.items
[i
].message_status
= 'Read';
340 $scope.closeMessage
= function () {
341 $scope.selected
= null;
344 $scope.renderMessageBody
= function (html
) {
345 html
= DOMPurify
.sanitize(html
, { USE_PROFILES
: { html
: true } });
349 $scope.htmlToText
= function (html
) {
350 const hold
= document
.createElement('DIV');
351 hold
.innerHTML
= DOMPurify
.sanitize(html
, { USE_PROFILES
: { html
: true } });
352 return jsText(hold
.textContent || hold
.innerText ||
'');
355 // note backend supports a task of getinbox but we prefetch this on server so we don't have that here.
356 $scope.getAllMessages
= function () {
357 return $http.post('handle_note.php', $
.param({'task': 'getall', 'csrf_token_form': $scope.csrf
})).then(function successCallback(response
) {
359 $scope.allItems
= angular
.copy(response
.data
);
360 } else alert(response
.data
);
361 }, function errorCallback(response
) {
362 alert(response
.data
);
366 $scope.getDeletedMessages
= function () {
367 return $http.post('handle_note.php', $
.param({'task': 'getdeleted', 'csrf_token_form': $scope.csrf
})).then(function successCallback(response
) {
369 $scope.deletedItems
= [];
370 $scope.deletedItems
= angular
.copy(response
.data
);
371 } else alert(response
.data
);
372 }, function errorCallback(response
) {
373 alert(response
.data
);
377 $scope.getSentMessages
= function () {
378 return $http.post('handle_note.php', $
.param({'task': 'getsent', 'csrf_token_form': $scope.csrf
})).then(function successCallback(response
) {
379 $scope.sentItems
= [];
380 $scope.sentItems
= angular
.copy(response
.data
);
381 }, function errorCallback(response
) {
382 alert(response
.data
);
386 $scope.submitForm
= function (compose
) {
387 $http.defaults
.headers
.post
["Content-Type"] = "application/x-www-form-urlencoded";
388 // re-enable title for submit
389 $
("#title").prop("disabled", false);
390 $
("#selSendto").prop("disabled", false);
392 compose
.csrf_token_form
= $scope.csrf
;
393 compose
.sender_id
= $scope.cUserId
;
394 compose
.sender_name
= $scope.userproper
;
395 if ($scope.selrecip
== $scope.cUserId
) {
396 if (!confirm($scope.xLate
.confirm
.err
))
399 if (compose
.task
== 'add') {
400 compose
.recipient_name
= $
("#selSendto option:selected").text();
402 if (compose
.task
== 'forward') { // Just overwrite default reply but send to pnotes.
403 compose
.sender_id
= $
("#selForwardto option:selected").val();
404 compose
.sender_name
= $
("#selForwardto option:selected").text();
405 compose
.selrecip
= compose
.recipient_id
;
407 compose
.inputBody
= $
("#inputBody").summernote('code');
409 return true; // okay to submit
412 $
('#modalCompose').on('hidden.bs.modal', function (e
) {
413 window
.location
.reload();
416 $
('#modalCompose').on('show.bs.modal', function (e
) {
417 // Sets up the compose modal before we show it
419 $
('#inputBody').summernote('destroy');
420 var mode
= $
(e
.relatedTarget
).attr('data-mode');
421 $scope.compose
.task
= mode
;
422 if (mode
== 'forward') {
423 $
('#modalCompose .modal-header .modal-title').html("Forward Message");
424 $scope.compose
.task
= mode
;
425 var recipId
= $
(e
.relatedTarget
).attr('data-whoto');
426 var title
= $
(e
.relatedTarget
).attr('data-mtitle');
427 var uname
= $
(e
.relatedTarget
).attr('data-username');
428 $
(e
.currentTarget
).find('select[id="selSendto"]').prop("disabled", false);
429 $
(e
.currentTarget
).find('input[name="title"]').prop("disabled", false);
430 $scope.compose
.title
= title
;
431 $scope.compose
.selrecip
= recipId
;
432 $scope.compose
.selrecip
.username
= uname
;
433 $scope.compose
.recipient_name
= uname
;
434 $scope.compose
.recipient_id
= recipId
;
435 angular
.forEach($scope.authrecips
, function (o
, key
) {// Need the pid of patient for pnotes.
436 if (o
.userid
== recipId
) {
437 $scope.compose
.pid
= o
.pid
;
440 const fmsg
= '\n\n\n> ' +
$scope.xLate
.fwd + title +
' by ' + uname +
'\n> ' + $
("#referMsg").text();
441 $
("textarea#finputBody").text(fmsg
)
442 $scope.compose
.noteid
= $
(e
.relatedTarget
).attr('data-noteid');
443 } else if (mode
== 'reply') {
444 $
('#inputBody').summernote({
449 disableDragAndDrop
: true,
453 $
('#modalCompose .modal-header .modal-title').html(<?php
xlt("Compose Reply Message"); ?
>)
454 $scope.compose
.task
= mode
;
455 //get data attributes of the clicked element (selected recipient) for replies only
456 var chain
= $
(e
.relatedTarget
).attr('data-mailchain');
458 chain
= $
(e
.relatedTarget
).attr('data-noteid');
460 let recipId
= $
(e
.relatedTarget
).attr('data-whoto');
461 let title
= $
(e
.relatedTarget
).attr('data-mtitle');
462 let uname
= $
(e
.relatedTarget
).attr('data-username');
463 $
(e
.currentTarget
).find('select[id="selSendto"]').val(recipId
)
464 $
(e
.currentTarget
).find('input[name="title"]').val(title
);
465 // Set the modal var's
466 $scope.compose
.title
= title
;
467 $scope.compose
.selrecip
= recipId
;
468 $scope.compose
.selrecip
.username
= uname
;
469 $scope.compose
.recipient_name
= uname
;
470 $scope.compose
.recipient_id
= recipId
;
471 $scope.compose
.noteid
= chain
;
473 $
('#inputBody').summernote({
478 disableDragAndDrop
: true,
487 $
('#modalCompose .modal-header .modal-title').html(<?php
xlt("Compose New Message"); ?
>)
488 $scope.compose
.task
= 'add';
489 $
(e
.currentTarget
).find('select[id="selSendto"]').prop("disabled", false);
490 $
(e
.currentTarget
).find('input[name="title"]').prop("disabled", false);
492 if ($scope.compose
.task
!= 'reply') {
496 // initialize application
497 if (!$scope.isInit
) {
500 }]) /* end inbox functions */
501 .filter('Chained', function () {
502 return function (input
, id
) {
507 angular
.forEach(input
, function (item
) {
508 if (item
.mail_chain
== id
) {
515 }).filter('getById', function () {
516 return function (input
, id
) {
517 var i
= 0, len
= input
.length
;
518 for (; i
< len
; i++
) {
519 if (+input
[i
].id
== +id
) {
525 }).controller('messageCtrl', ['$scope', function ($scope) {
526 $scope.message
= function (idx
) {
529 }]); // end messageCtrl
530 })(); // application end
534 $GLOBALS['kernel']->getEventDispatcher()->dispatch(new SendSmsEvent($pid), SendSmsEvent
::JAVASCRIPT_READY_SMS_POST
);
538 <ng ng
-app
="emrMessageApp">
539 <div
class="container-fluid" id
='main' ng
-controller
="inboxCtrl">
541 <h2
><i
class='fa fa-envelope w-auto h-auto mr-2'></i
><?php
echo xlt('Secure Messaging'); ?
></h2
>
543 <div
class="row" ng
-class="{'d-none': isInit}">
545 <div
class="alert alert-info"><h3
><?php
echo xlt("Loading..."); ?
> <i
class="wait fa fa-cog fa-spin ml-2"></i
></h3
></div
>
548 <div
class="row d-none" ng
-class="{'d-none': !isInit}">
549 <div
class="col-md-2 p-0 m-0 text-left border-right bg-light text-dark">
550 <div
class="sticky-top">
551 <ul
class="nav nav-pills nav-stacked flex-column">
552 <li
class="nav-item">
553 <a
class="nav-link active" data
-toggle
="pill" href
="javascript:;" ng
-click
="isInboxSelected()"><span
class="badge float-right">{{inboxItems
.length
}}</span
><?php
echo xlt('Inbox'); ?
></a
>
555 <li
class="nav-item">
556 <a
class="nav-link" data
-toggle
="pill" href
="javascript:;" ng
-click
="isSentSelected()"><span
class="badge float-right">{{sentItems
.length
}}</span
><?php
echo xlt('Sent{{Mails}}'); ?
></a
>
558 <li
class="nav-item">
559 <a
class="nav-link" data
-toggle
="pill" href
="javascript:;" ng
-click
="isAllSelected()"><span
class="badge float-right">{{allItems
.length
}}</span
><?php
echo xlt('All{{Mails}}'); ?
></a
>
561 <li
class="nav-item">
562 <a
class="nav-link" data
-toggle
="pill" href
="javascript:;" ng
-click
="isTrashSelected()"><span
class="badge float-right">{{deletedItems
.length
}}</span
><?php
echo xlt('Archive'); ?
></a
>
564 <li
class="nav-item">
565 <a
class="nav-link" href
="<?php echo $GLOBALS['web_root'] ?>/portal/patient/provider" ng
-show
="!isPortal"><?php
echo xlt('Exit Mail'); ?
></a
>
567 <!--<li
class="nav-item">
568 <a
class="nav-link" href
="javascript:;" onclick
='window.location.replace("<?php /*echo $GLOBALS['web_root
'] */ ?>/portal/home.php")' ng
-show
="isPortal"><?php
/*echo xlt('Exit'); */ ?
></a
>
573 <div
class="col-md-10">
575 <div
class="row" ng
-show
="!isMessageSelected()">
576 <div
class="col-12 mb-2">
577 <button
class="btn btn-primary" title
="<?php echo xla("Compose Message
"); ?>" data
-mode
="add" data
-toggle
="modal" data
-target
="#modalCompose">
578 <span
class="fa fa-edit fa-lg"></span
> <?php
echo xlt("Compose Message"); ?
>
582 $GLOBALS['kernel']->getEventDispatcher()->dispatch(new SendSmsEvent($_SESSION['pid'] ??
0), SendSmsEvent
::ACTIONS_RENDER_SMS_POST
);
585 <a
class="btn btn-secondary" data
-toggle
="tooltip" title
="<?php echo xla("Refresh to see
new messages
"); ?>" id
="refreshInbox" href
="javascript:;" onclick
='window.location.replace("./messages.php")'> <span
class="fa fa-sync fa-lg"></span
>
587 <div
class="btn-group btn-group float-right">
588 <button type
="button" class="btn btn-primary dropdown-toggle" data
-toggle
="dropdown"><?php
echo xlt('Actions'); ?
></button
>
589 <ul
class="dropdown-menu dropdown-menu-right">
591 <a
class="dropdown-item" href
="javascript:;" ng
-click
="readAll()"><?php
echo xlt('Mark all as read'); ?
></a
>
593 <li
class="dropdown-divider"></li
>
595 <a
class="dropdown-item" href
="" data
-mode
="add" data
-toggle
="modal" data
-target
="#modalCompose"><i
class="fa fa-edit"></i
> <?php
echo xlt('Compose Message'); ?
></a
>
597 <li ng
-show
='!isTrash'>
598 <a
class="dropdown-item" href
="javascript:;" ng
-click
="batchDelete(items)"><i
class="fa fa-trash"></i
> <?php
echo xlt('Send Selected to Archive'); ?
></a
></li
>
600 <a href
="javascript:;" onclick
='window.location.replace("./messages.php")' ng
-show
="isPortal" class="dropdown-item"><i
class="fa fa-sync"></i
> <?php
echo xlt('Refresh'); ?
></a
>
603 <a href
="<?php echo $GLOBALS['web_root'] ?>/portal/patient/provider" ng
-show
="!isPortal" class="dropdown-item"><i
class="fa fa-home"></i
> <?php
echo xlt('Return Home'); ?
></a
>
609 <div
class="col-12"></div
>
612 <!--/inbox toolbar
-->
613 <div
class="inbox" id
="inboxPanel">
615 <div
class="table-responsive" ng
-show
="!isMessageSelected()">
616 <table
class="table table-striped table-bordered table-hover refresh-container pull-down">
617 <thead
class="bg-info d-none"></thead
>
619 <tr ng
-repeat
="item in pagedItems[currentPage]" role
='button'>
620 <!-- | orderBy
:sortingOrder
:reverse
-->
621 <td role
= "button" class="message-row">
622 <span
class="col-sm-1" style
="max-width: 5px;"><input type
="checkbox" checklist
-model
="item.deleted" value
={{item
.deleted
}}></span
>
624 <span
class="col-sm-1 px-1" ng
-click
="readMessage($index)" ><span ng
-class="{strong: !item.read}">{{item
.message_status
}}</span
></span
>
625 <span
class="col-sm-2 px-1" ng
-click
="readMessage($index)" ><span ng
-class="{strong: !item.read}">{{item
.date | date
:'yyyy-MM-dd hh:mm'}}</span
></span
>
626 <span
class="col-sm-3 px-1" ng
-click
="readMessage($index)" >
627 <a ng
-click
="readMessage($index)" class="btn-link">
628 <span ng
-class="{strong: !item.read}">{{item
.sender_name
}} to
{{item
.recipient_name
}}</span
>
631 <span
class="col-sm-1" ng
-click
="readMessage($index)">
632 <a ng
-click
="readMessage($index)" class="btn-link">
633 <span ng
-class="{strong: !item.read}">{{item
.title
}}</span
>
636 <span
class="col-sm-4 px-1" ng
-click
="readMessage($index)"><span ng
-class="{strong: !item.read}" ng
-bind
='(htmlToText(item.body) | limitTo:35)'></span
></span
>
637 <!-- below
for attachments
, eventually
-->
638 <!-- <span
class="col-sm-1 " ng
-click
="readMessage($index)"><span ng
-show
="item.attachment"
639 class="glyphicon glyphicon-paperclip float-right"></span
> <span ng
-show
="item.priority==1"
640 class="float-right glyphicon glyphicon-warning-sign text-danger"></span
></span
> -->
646 <!--message detail
-->
647 <div
class="container-fluid" ng
-show
="isMessageSelected()">
648 <div
class="row" ng
-controller
="messageCtrl">
649 <div
class="w-100 pl-1 mb-1 bg-light text-dark">
651 <a href
="javascript:;" ng
-click
="groupToPages()"><?php
echo xlt('Conversation from'); ?
></a
>
652 <strong
>{{selected
.sender_name
}}</strong
> <?php
echo xlt('regarding'); ?
> <strong
>{{selected
.title
}}</strong
> <?php
echo xlt('on'); ?
> <
;{{selected
.date | date
:'yyyy-MM-dd hh:mm'}}>
;
654 <!-- Leave below
for future menu items
-->
655 <!--<span
class="btn-group float-right">
656 <button ng
-show
="selected.sender_id != cUserId" class="btn btn-primary" title
="<?php /*echo xla('Reply to this message'); */?>" data
-toggle
="modal" data
-mode
="reply" data
-noteid
='{{selected.id}}' data
-whoto
='{{selected.sender_id}}' data
-mtitle
='{{selected.title}}' data
-username
='{{selected.sender_name}}' data
-mailchain
='{{selected.mail_chain}}' data
-target
="#modalCompose">
657 <i
class="fa fa-reply"></i
> <?php
/*echo xlt('Reply'); */?
></button
>
658 <button
class="btn btn-primary dropdown-toggle" data
-toggle
="dropdown" title
="<?php /*echo xla("More options
"); */?>"></button
>
659 <ul
class="dropdown-menu float-right">
660 <li ng
-show
='!isTrash'><a href
="javascript:;" ng
-click
="batchDelete(items)"><i
class="fa fa-trash"></i
> <?php
/*echo xlt('Send to Archive'); */?
></a
></li
>
662 <button ng
-show
='!isTrash' class="btn btn-md btn-primary float-right" ng
-click
="deleteItem(items.indexOf(selected))" title
="<?php /*echo xla('Delete this message'); */?>" data
-toggle
="tooltip">
663 <i
class="fa fa-trash fa-1x"></i
>
667 <div
class="table-responsive row ml-1">
668 <table
class="table table-hover table-striped table-bordered refresh-container pull-down">
669 <thead
><?php
echo xlt('Associated Messages in thread.');?
></thead
>
671 <tr
class="animate-repeat" ng
-repeat
="item in allItems | Chained:selected.mail_chain">
672 <td role
= "button" ng
-click
="readMessage($index)">
673 <span
class="col-sm px-1"><span
>{{item
.date | date
:'yyyy-MM-dd hh:mm'}}</span
></span
>
674 <span
class="col-sm"><span
>{{item
.message_status
}}</span
></span
>
675 <span
class="col-sm px-1"><span
>{{item
.sender_name
}}
676 to
{{item
.recipient_name
}}</span
></span
> <span
class="col-sm-1"><span
>{{item
.title
}}</span
></span
>
677 <span
class="col-sm px-1" ng
-hide
="selected.id == item.id"><span ng
-bind
-html
='(htmlToText(item.body) | limitTo:35)'></span
></span
>
678 <span
class='btn-group float-right m-0'>
679 <button ng
-show
="selected.sender_id != cUserId && selected.id == item.id" class="btn btn-primary btn-small" title
="<?php echo xla('Reply to this message'); ?>" data
-toggle
="modal" data
-mode
="reply" data
-noteid
='{{selected.id}}' data
-whoto
='{{selected.sender_id}}' data
-mtitle
='{{selected.title}}' data
-username
='{{selected.sender_name}}' data
-mailchain
='{{selected.mail_chain}}' data
-target
="#modalCompose"><i
class="fa fa-reply"></i
></button
>
680 <button ng
-show
="selected.id == item.id && selected.sender_id != cUserId && !isPortal" class="btn btn-primary btn-small" title
="<?php echo xla('Forward message to practice.'); ?>" data
-toggle
="modal" data
-mode
="forward" data
-noteid
='{{selected.id}}' data
-whoto
='{{selected.sender_id}}' data
-mtitle
='{{selected.title}}' data
-username
='{{selected.sender_name}}' data
-mailchain
='{{selected.mail_chain}}' data
-target
="#modalCompose"><i
class="fa fa-share"></i
></button
>
681 <button ng
-show
='!isTrash && selected.id == item.id' class="btn btn-small btn-primary" ng
-click
="deleteItem(items.indexOf(selected))" title
="<?php echo xla('Archive this message'); ?>" data
-toggle
="tooltip"><i
class="fa fa-trash fa-1x"></i
>
684 <div
class='col jumbotron jumbotron-fluid my-3 p-1 bg-light text-dark rounded border border-info' ng
-show
="selected.id == item.id">
685 <span ng
-bind
-html
=renderMessageBody(selected
.body
)></span
>
699 <div
class="float-right my-2" ng
-hide
="selected">
700 <span
class="text-muted"><strong
>{{(itemsPerPage
* currentPage
) +
1}}</strong
>~
<strong
>{{(itemsPerPage
701 * currentPage
) + pagedItems
[currentPage
].length
}}</strong
> of
<strong
>{{items
.length
}}</strong
></span
>
702 <div
class="btn-group" ng
-show
="items.length > itemsPerPage">
703 <button type
="button" class="btn btn-secondary btn-lg" ng
-class="{disabled: currentPage == 0}" ng
-click
="prevPage()"><i
class="fa fa-chevron-left"></i
></button
>
704 <button type
="button" class="btn btn-secondary btn-lg" ng
-class="{disabled: currentPage == pagedItems.length - 1}" ng
-click
="nextPage()"><i
class="fa fa-chevron-right"></i
></button
>
708 <!-- /.modal compose message
-->
709 <div
class="modal fade" id
="modalCompose">
710 <div
class="modal-dialog modal-xl">
711 <div
class="modal-content">
712 <div
class="modal-header">
713 <h4
class="modal-title"><?php
echo xlt('Compose Message'); ?
></h4
>
714 <button type
="button" class="close" data
-dismiss
="modal" aria
-label
="Close"><span aria
-hidden
="true">×
;</span
></button
>
716 <div
class="modal-body ">
718 <label ng
-show
='selected.mail_chain'><?php
echo xlt('Refer to Message') . ' # '; ?
>{{selected
.id
}}</label
>
719 <div
class="jumbotron col-lg-12 m-1 p-1 bg-light text-dark" id
="referMsg" ng
-show
='selected.mail_chain' ng
-bind
-html
='renderMessageBody(selected.body)'></div
>
721 <form role
="form" class="form-horizontal" ng
-submit
="submitForm(compose)" name
="fcompose" id
="fcompose" method
="post" action
="./handle_note.php">
722 <fieldset
class="row">
723 <div
class="col-lg-6 input-group my-2">
724 <label
for="selSendto"><?php
echo xlt('To{{Destination}}'); ?
></label
>
725 <select
class="form-control ml-2 to-select-forward" id
="selForwardto" ng
-hide
="compose.task != 'forward'" ng
-model
="compose.selrecip" ng
-options
="recip.userid as recip.username for recip in authrecips | filter:'user' track by recip.userid"></select
>
726 <select
class="form-control ml-2 to-select-send" id
="selSendto" ng
-hide
="compose.task == 'forward'" ng
-model
="compose.selrecip" ng
-options
="recip.userid as recip.username for recip in authrecips track by recip.userid"></select
>
728 <div
class="input-group col-lg-6 my-2">
729 <label
for="title"><?php
echo xlt('Subject'); ?
></label
>
730 <input type
='text' list='listid' name
='title' id
='title' class="form-control ml-2" ng
-model
='compose.title' value
="<?php echo xla('General'); ?>">
731 <datalist id
='listid'>
732 <option label
='<?php echo xlt('General
'); ?>'
733 value
='<?php echo xla('General
'); ?>'></option
>
734 <option label
='<?php echo xlt('Insurance
'); ?>'
735 value
='<?php echo xla('Insurance
'); ?>'></option
>
736 <option label
='<?php echo xlt('Prior Auth
'); ?>'
737 value
='<?php echo xla('Prior Auth
'); ?>'></option
>
738 <option label
='<?php echo xlt('Bill
/Collect
'); ?>'
739 value
='<?php echo xla('Bill
/Collect
'); ?>'></option
>
740 <option label
='<?php echo xlt('Referral
'); ?>'
741 value
='<?php echo xla('Referral
'); ?>'></option
>
742 <option label
='<?php echo xlt('Pharmacy
'); ?>'
743 value
='<?php echo xla('Pharmacy
'); ?>'></option
>
746 <div
class="col-12" id
="inputBody" ng
-hide
="compose.task == 'forward'" ng
-model
="compose.inputBody"></div
>
747 <textarea
class="col-12" id
="finputBody" rows
="8" ng
-hide
="compose.task != 'forward'" ng
-model
="compose.inputBody"></textarea
>
749 <input type
="hidden" name
="csrf_token_form" id
="csrf_token_form" ng
-value
="compose.csrf_token_form" />
750 <input type
='hidden' name
='noteid' id
='noteid' ng
-value
="compose.noteid" />
751 <input type
='hidden' name
='replyid' id
='replyid' ng
-value
='selected.reply_mail_chain' />
752 <input type
='hidden' name
='recipient_id' ng
-value
='compose.selrecip' />
753 <input type
='hidden' name
='recipient_name' ng
-value
='compose.recipient_name' />
754 <input type
='hidden' name
='sender_id' ng
-value
='compose.sender_id' />
755 <input type
='hidden' name
='sender_name' ng
-value
='compose.sender_name' />
756 <input type
='hidden' name
='task' ng
-value
='compose.task' />
757 <input type
='hidden' name
='inputBody' ng
-value
='compose.inputBody' />
758 <input type
='hidden' name
='pid' ng
-value
='compose.pid' />
759 <div
class='modal-footer'>
760 <button type
="button" class="btn btn-secondary"
761 data
-dismiss
="modal"><?php
echo xlt('Cancel'); ?
></button
>
762 <button type
="submit" id
="submit" name
="submit"
763 class="btn btn-primary float-right" value
="messages.php"><?php
echo xlt('Send'); ?
> <i
764 class="fa fa-arrow-circle-right fa-lg"></i
>
771 <!-- /.modal
-content
-->
773 <!-- /.modal
-dialog
-->
775 <!-- /.modal compose message
-->
777 <!--/row ng
-controller
-->
779 <!--/container
--> </ng
>