6 * @link https://www.open-emr.org
7 * @author Jerry Padgett <sjpadgett@gmail.com>
8 * @author Brady Miller <brady.g.miller@gmail.com>
9 * @copyright Copyright (c) 2016-2017 Jerry Padgett <sjpadgett@gmail.com>
10 * @copyright Copyright (c) 2019 Brady Miller <brady.g.miller@gmail.com>
11 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
17 if (isset($_SESSION['pid']) && isset($_SESSION['patient_portal_onsite_two'])) {
18 $pid = $_SESSION['pid'];
20 require_once(dirname(__FILE__
) . "/../../interface/globals.php");
21 define('IS_DASHBOARD', false);
22 define('IS_PORTAL', $_SESSION['pid']);
26 require_once(dirname(__FILE__
) . "/../../interface/globals.php");
27 if (! isset($_SESSION['authUserID'])) {
28 $landingpage = "index.php";
29 header('Location: '.$landingpage);
32 $admin = sqlQueryNoLog(
33 "SELECT CONCAT(users.fname,' ',users.lname) as user_name FROM users WHERE id = ?",
34 array($_SESSION['authUserID'])
36 define('ADMIN_USERNAME', $admin['user_name']);
37 define('IS_DASHBOARD', $_SESSION['authUser']);
38 define('IS_PORTAL', false);
39 $_SERVER[REMOTE_ADDR
] = 'admin::' . $_SERVER[REMOTE_ADDR
];
43 define('C_USER', IS_PORTAL ? IS_PORTAL
: IS_DASHBOARD
);
45 if (isset($_REQUEST['fullscreen'])) {
46 $_SESSION['whereto'] = 'messagespanel';
47 define('IS_FULLSCREEN', true);
49 define('IS_FULLSCREEN', false);
52 define('CHAT_HISTORY', '150');
53 define('CHAT_ONLINE_RANGE', '1');
54 define('ADMIN_USERNAME_PREFIX', 'adm_');
60 public function __construct()
62 //$this->db = new \mysqli(DB_HOST, DB_USERNAME, DB_PASSWORD, DB_NAME);
66 abstract class Controller
68 private $_request, $_response, $_query, $_post, $_server, $_cookies, $_session;
69 protected $_currentAction, $_defaultModel;
71 const ACTION_POSTFIX
= 'Action';
72 const ACTION_DEFAULT
= 'indexAction';
74 public function __construct()
76 $this->_request
= &$_REQUEST;
77 $this->_query
= &$_GET;
78 $this->_post
= &$_POST;
79 $this->_server
= &$_SERVER;
80 $this->_cookies
= &$_COOKIE;
81 $this->_session
= &$_SESSION;
85 public function init()
87 $this->dispatchActions();
91 public function dispatchActions()
93 $action = $this->getQuery('action');
94 if ($action && $action .= self
::ACTION_POSTFIX
) {
95 if (method_exists($this, $action)) {
97 call_user_func(array($this, $action), array())
100 $this->setHeader("HTTP/1.0 404 Not Found");
104 call_user_func(array($this, self
::ACTION_DEFAULT
), array())
108 return $this->_response
;
111 public function render()
113 if ($this->_response
) {
114 if (is_scalar($this->_response
)) {
115 echo $this->_response
;
117 throw new \
Exception('Response content must be scalar');
124 public function indexAction()
129 public function setResponse($content)
131 $this->_response
= $content;
134 public function setHeader($params)
136 if (! headers_sent()) {
137 if (is_scalar($params)) {
140 foreach ($params as $key => $value) {
141 header(sprintf('%s: %s', $key, $value));
149 public function setModel($namespace)
151 $this->_defaultModel
= $namespace;
155 public function setSession($key, $value)
157 $_SESSION[$key] = $value;
161 public function setCookie($key, $value, $seconds = 3600)
163 $this->_cookies
[$key] = $value;
164 if (! headers_sent()) {
165 setcookie($key, $value, time() +
$seconds);
170 public function getRequest($param = null, $default = null)
173 return isset($this->_request
[$param]) ?
174 $this->_request
[$param] : $default;
177 return $this->_request
;
180 public function getQuery($param = null, $default = null)
183 return isset($this->_query
[$param]) ?
184 $this->_query
[$param] : $default;
187 return $this->_query
;
190 public function getPost($param = null, $default = null)
193 return isset($this->_post
[$param]) ?
194 $this->_post
[$param] : $default;
200 public function getServer($param = null, $default = null)
203 return isset($this->_server
[$param]) ?
204 $this->_server
[$param] : $default;
207 return $this->_server
;
210 public function getSession($param = null, $default = null)
213 return isset($this->_session
[$param]) ?
214 $this->_session
[$param] : $default;
217 return $this->_session
;
220 public function getCookie($param = null, $default = null)
223 return isset($this->_cookies
[$param]) ?
224 $this->_cookies
[$param] : $default;
227 return $this->_cookies
;
230 public function getUser()
232 return $this->_session
['ptName'] ?
$this->_session
['ptName'] : $this->_session
['authUser'];
234 public function getIsPortal()
238 public function getIsFullScreen()
240 return IS_FULLSCREEN
;
242 public function getModel()
244 if ($this->_defaultModel
&& class_exists($this->_defaultModel
)) {
245 return new $this->_defaultModel
;
249 public function sanitize($string, $quotes = ENT_QUOTES
, $charset = 'utf-8')
251 return htmlentities($string, $quotes, $charset);
255 abstract class Helper
262 // @codingStandardsIgnoreStart
264 // @codingStandardsIgnoreEnd
265 class Model
extends SMA_Common\Model
267 public function getAuthUsers()
272 $query = "SELECT patient_data.pid as recip_id, Concat_Ws(' ', patient_data.fname, patient_data.lname) as username FROM patient_data " .
273 "LEFT JOIN patient_access_onsite pao ON pao.pid = patient_data.pid " .
274 "WHERE patient_data.pid = pao.pid AND pao.portal_pwd_status = 1";
275 $response = sqlStatementNoLog($query);
276 while ($row = sqlFetchArray($response)) {
281 $query = "SELECT users.username as recip_id, users.authorized as dash, CONCAT(users.fname,' ',users.lname) as username " .
282 "FROM users WHERE active = 1 AND username > ''";
283 $response = sqlStatementNoLog($query);
285 while ($row = sqlFetchArray($response)) {
289 $all = array_merge($result, $resultpd);
291 return json_encode($all);
293 public function getMessages($limit = CHAT_HISTORY
, $reverse = true)
295 $response = sqlStatementNoLog("(SELECT * FROM onsite_messages
296 ORDER BY `date` DESC LIMIT " . escape_limit($limit) . ") ORDER BY `date` ASC");
299 while ($row = sqlFetchArray($response)) {
300 if (IS_PORTAL || IS_DASHBOARD
) {
301 $u = json_decode($row['recip_id'], true);
306 if ((in_array(C_USER
, $u)) ||
$row['sender_id'] == C_USER
) {
307 $result[] = $row; // only current patient messages
310 $result[] = $row; // admin gets all
317 public function addMessage($username, $message, $ip, $senderid = 0, $recipid = '')
319 return sqlQueryNoLog("INSERT INTO onsite_messages VALUES (NULL, ?, ?, ?, NOW(), ?, ?)", array($username,$message,$ip,$senderid,$recipid));
322 public function removeMessages()
324 return sqlQueryNoLog("TRUNCATE TABLE onsite_messages");
327 public function removeOldMessages($limit = CHAT_HISTORY
)
329 /* @todo Patched out to replace with soft delete. Besides this query won't work with current ado(or any) */
330 /* return sqlStatementNoLog("DELETE FROM onsite_messages
331 WHERE id NOT IN (SELECT id FROM onsite_messages
332 ORDER BY date DESC LIMIT {$limit})"); */
335 public function getOnline($count = true, $timeRange = CHAT_ONLINE_RANGE
)
338 $response = sqlStatementNoLog("SELECT count(*) as total FROM onsite_online");
339 return sqlFetchArray($response);
342 $response = sqlStatementNoLog("SELECT * FROM onsite_online");
344 while ($row = sqlFetchArray($response)) {
351 public function updateOnline($hash, $ip, $username = '', $userid = 0)
353 return sqlStatementNoLog("REPLACE INTO onsite_online
354 VALUES ( ?, ?, NOW(), ?, ? )", array($hash, $ip, $username, $userid)) or die(mysql_error());
357 public function clearOffline($timeRange = CHAT_ONLINE_RANGE
)
359 return sqlStatementNoLog("DELETE FROM onsite_online
360 WHERE last_update <= (NOW() - INTERVAL " . escape_limit($timeRange) . " MINUTE)");
363 public function __destruct()
368 class Controller
extends SMA_Common\Controller
372 public function __construct()
374 $this->setModel('SMA_Msg\Model');
375 parent
::__construct();
378 public function indexAction()
381 public function authusersAction()
383 return $this->getModel()->getAuthUsers(true);
385 public function listAction()
387 $this->setHeader(array('Content-Type' => 'application/json'));
388 $messages = $this->getModel()->getMessages();
389 foreach ($messages as &$message) {
390 $message['me'] = C_USER
=== $message['sender_id']; // $this->getServer('REMOTE_ADDR') === $message['ip'];
393 return json_encode($messages);
396 public function saveAction()
398 $username = $this->getPost('username');
399 $message = $this->getPost('message');
400 $ip = $this->getServer('REMOTE_ADDR');
401 $this->setCookie('username', $username, 9999 * 9999);
402 $recipid = $this->getPost('recip_id');
405 $senderid = IS_PORTAL
;
407 $senderid = IS_DASHBOARD
;
410 $result = array('success' => false);
411 if ($username && $message) {
412 $cleanUsername = preg_replace('/^'.ADMIN_USERNAME_PREFIX
.'/', '', $username);
414 'success' => $this->getModel()->addMessage($cleanUsername, $message, $ip, $senderid, $recipid)
418 if ($this->_isAdmin($username)) {
419 $this->_parseAdminCommand($message);
422 $this->setHeader(array('Content-Type' => 'application/json'));
423 return json_encode($result);
426 private function _isAdmin($username)
428 return IS_DASHBOARD?
true:false;
429 //return preg_match('/^'.ADMIN_USERNAME_PREFIX.'/', $username);
432 private function _parseAdminCommand($message)
434 if (strpos($message, '/clear') !== false) {
435 $this->getModel()->removeMessages();
439 if (strpos($message, '/online') !== false) {
440 $online = $this->getModel()->getOnline(false);
442 foreach ($online as $item) {
443 $ipArr[] = $item->ip
;
446 $message = 'Online: ' . implode(", ", $ipArr);
447 $this->getModel()->addMessage('Admin Command', $message, '0.0.0.0');
452 private function _getMyUniqueHash()
454 $unique = $this->getServer('REMOTE_ADDR');
455 $unique .= $this->getServer('HTTP_USER_AGENT');
456 $unique .= $this->getServer('HTTP_ACCEPT_LANGUAGE');
461 public function pingAction()
463 $ip = $this->getServer('REMOTE_ADDR');
464 $hash = $this->_getMyUniqueHash();
465 $user = $this->getRequest('username', 'No Username');
466 if ($user == 'currentol') {
467 $onlines = $this->getModel()->getOnline(false);
468 $this->setHeader(array('Content-Type' => 'application/json'));
469 return json_encode($onlines);
475 $userid = IS_DASHBOARD
;
478 $this->getModel()->updateOnline($hash, $ip, $user, $userid);
479 $this->getModel()->clearOffline();
480 // $this->getModel()->removeOldMessages(); // @todo For soft delete when I decide. DO NOT REMOVE
482 $onlines = $this->getModel()->getOnline();
484 $this->setHeader(array('Content-Type' => 'application/json'));
485 return json_encode($onlines);
489 $msgApp = new Controller();
492 <html ng
-app
="MsgApp">
494 <meta name
="viewport" content
="initial-scale=1.0, user-scalable=no">
495 <meta charset
="utf-8">
497 <title
><?php
echo xlt('Secure Patient Chat'); ?
></title
>
498 <meta name
="author" content
="Jerry Padgett sjpadgett{{at}} gmail {{dot}} com">
500 <script type
='text/javascript' src
="<?php echo $GLOBALS['assets_static_relative']; ?>/jquery/dist/jquery.min.js"></script
>
502 <link href
="<?php echo $GLOBALS['assets_static_relative']; ?>/bootstrap/dist/css/bootstrap.min.css" rel
="stylesheet" type
="text/css" />
503 <?php
if ($_SESSION['language_direction'] == 'rtl') { ?
>
504 <link href
="<?php echo $GLOBALS['assets_static_relative']; ?>/bootstrap-rtl/dist/css/bootstrap-rtl.min.css" rel
="stylesheet" type
="text/css" />
507 <script type
='text/javascript' src
="<?php echo $GLOBALS['assets_static_relative']; ?>/bootstrap/dist/js/bootstrap.min.js"></script
>
509 <link rel
="stylesheet" href
="<?php echo $GLOBALS['assets_static_relative']; ?>/summernote/dist/summernote.css" />
510 <script type
='text/javascript' src
="<?php echo $GLOBALS['assets_static_relative']; ?>/summernote/dist/summernote.js"></script
>
512 <script type
='text/javascript' src
="<?php echo $GLOBALS['assets_static_relative']; ?>/angular/angular.min.js"></script
>
513 <script type
='text/javascript' src
="<?php echo $GLOBALS['assets_static_relative']; ?>/angular-summernote/dist/angular-summernote.js"></script
>
514 <script type
='text/javascript' src
="<?php echo $GLOBALS['assets_static_relative']; ?>/angular-sanitize/angular-sanitize.min.js"></script
>
515 <script src
='<?php echo $GLOBALS['assets_static_relative
']; ?>/checklist-model/checklist-model.js'></script
>
518 <script type
="text/javascript">
520 var MsgApp
= angular
.module('MsgApp',['ngSanitize','summernote',"checklist-model"]);
521 MsgApp
.config(function( $compileProvider ) {
522 $compileProvider.aHrefSanitizationWhitelist(/^\s
*(https?|file|ftp|blob
):|data
:image\
//);
525 MsgApp
.directive('ngEnter', function () {
526 return function (scope
, element
, attrs
) {
527 element
.bind("keydown keypress", function (event
) {
528 if (event
.which
=== 13) {
529 scope
.$apply(function (){
530 scope
.$eval(attrs
.ngEnter
);
532 event
.preventDefault();
537 MsgApp
.directive('tooltip', function(e
){
540 link
: function(scope
, element
, attrs
){
541 $
(element
).hover(function(){
542 $
(element
).tooltip('show');
544 $
(element
).tooltip('hide');
549 MsgApp
.filter('unique', function() {
550 return function(collection
, keyname
) {
553 angular
.forEach(collection
, function(item
) {
554 var key
= item
[keyname
];
555 if(keys
.indexOf(key
) === -1) {
564 MsgApp
.controller('MsgAppCtrl', ['$scope', '$http', '$filter', function($scope, $http, $filter) {
565 $scope.urlListMessages
= '?action=list'; // actions for restful
566 $scope.urlSaveMessage
= '?action=save';
567 $scope.urlListOnlines
= '?action=ping';
568 $scope.urlGetAuthUsers
= '?action=authusers';
570 $scope.pidMessages
= null;
571 $scope.pidPingServer
= null;
573 $scope.beep
= new Audio('beep.ogg'); // you've got mail!!!! really just a beep
574 $scope.messages
= [];
575 $scope.online
= null;
576 $scope.lastMessageId
= null;
577 $scope.historyFromId
= null;
578 $scope.onlines
= []; // all online users id and ip's
579 $scope.user
= <?php
echo $_SESSION['ptName'] ?
js_escape($_SESSION['ptName']) : js_escape(ADMIN_USERNAME
); ?
>;// current user - dashboard user is from session authUserID
580 $scope.userid
= <?php
echo IS_PORTAL ?
js_escape($_SESSION['pid']) : js_escape($_SESSION['authUser']); ?
>;
581 $scope.isPortal
= "<?php echo IS_PORTAL;?>";
582 $scope.isFullScreen
= "<?php echo IS_FULLSCREEN; ?>";
583 $scope.pusers
= []; // selected recipients for chat
584 $scope.chatusers
= []; // authorize chat recipients for dashboard user
585 $scope.noRecipError
= <?php
echo xlj("Please Select a Recipient for Message.") ?
>;
587 username
: $scope.user
,
589 sender_id
: $scope.userid
,
595 placeholder
: 'Start typing your message...',
598 ['style', ['bold', 'italic', 'underline', 'clear']],
599 ['fontsize', ['fontsize']],
600 ['color', ['color']],
601 ['para', ['ul', 'ol', 'paragraph']],
602 ['insert', ['link','picture', 'video', 'hr']],
603 ['view', ['fullscreen', 'codeview']]
606 $scope.checkAll
= function() {
608 $scope.pusers
= $scope.chatusers
.map(function(item
) { return item
.recip_id
; });
609 $scope.getAuthUsers();
611 $scope.uncheckAll
= function() {
613 $scope.getAuthUsers();
615 $scope.makeCurrent
= function(sel
) {
617 $scope.pusers
.splice(0, $scope.pusers
.length
);
618 $scope.pusers
.push(sel
.sender_id
);
621 $scope.pageTitleNotificator
= {
623 originalTitle
: window
.document
.title
,
627 on
: function(title
, intervalSpeed
) {
629 if (! self
.vars
.status
) {
630 self
.vars
.interval
= window
.setInterval(function() {
631 window
.document
.title
= (self
.vars
.originalTitle
== window
.document
.title
) ?
632 title
: self
.vars
.originalTitle
;
633 }, intervalSpeed ||
500);
634 self
.vars
.status
= 1;
638 window
.clearInterval(this
.vars
.interval
);
639 window
.document
.title
= this
.vars
.originalTitle
;
640 this
.vars
.status
= 0;
644 $scope.editmsg
= function() {
645 $
('.summernote').summernote();
648 $scope.saveedit
= function() {
649 var makrup
= $
('.summernote').summernote('code');
650 $scope.me
.message
= makrup
;
651 $scope.saveMessage();
652 $
('.summernote').summernote('code', ''); //add this options to reset editor or not-default is persistent content
653 //$('.summernote').summernote('destroy');
656 $scope.saveMessage
= function(form
, callback
) {
657 $scope.me
.recip_id
= JSON
.stringify(angular
.copy($scope.pusers
));
658 var data
= $
.param($scope.me
);
659 if (! ($scope.me
.username
&& $scope.me
.username
.trim())) {
660 return $scope.openModal();
662 if (! ($scope.me
.message
&& $scope.me
.message
.trim() &&
663 $scope.me
.username
&& $scope.me
.username
.trim())) {
666 if($scope.me
.recip_id
== "[]") {
667 alert($scope.noRecipError
);
670 $scope.me
.message
= '';
673 url
: $scope.urlSaveMessage
,
675 headers
: {'Content-Type': 'application/x-www-form-urlencoded'}
676 }).success(function(data
) {
677 $scope.listMessages(true);
681 $scope.replaceShortcodes
= function(message
) {
683 msg
= message
.toString().replace(/(\
[img
])(.*)(\
[\
/img
])/, "<img class='img-responsive' src='$2' />");
684 msg
= msg
.toString().replace(/(\
[url
])(.*)(\
[\
/url
])/, "<a href='$2'>$2</a>");
685 msg
= message
.toString().replace("<img ", "<img class='img-responsive' ");
689 $scope.notifyLastMessage
= function() {
690 if (typeof window
.Notification
=== 'undefined') {
693 window
.Notification
.requestPermission(function (permission
) {
694 var lastMessage
= $scope.getLastMessage();
695 if (permission
== 'granted' && lastMessage
&& lastMessage
.username
) {
696 var notify
= new window
.Notification('Message notification from ' + lastMessage
.username +
' : ', {
697 body
: 'New message' //lastMessage.message
699 notify
.onclick
= function() {
702 notify
.onclose
= function() {
703 $scope.pageTitleNotificator
.off();
705 var timmer
= setInterval(function() {
706 notify
&& notify
.close();
707 typeof timmer
!== 'undefined' && window
.clearInterval(timmer
);
713 $scope.getLastMessage
= function() {
714 return $scope.messages
[$scope.messages
.length
- 1];
717 $scope.listMessages
= function(wasListingForMySubmission
) {
718 return $http.post($scope.urlListMessages
, {}).success(function(data
) {
719 $scope.messages
= [];
720 angular
.forEach(data
, function(message
) {
721 message
.message
= $scope.replaceShortcodes(message
.message
);
722 $scope.messages
.push(message
);
725 var lastMessage
= $scope.getLastMessage();
726 var lastMessageId
= lastMessage
&& lastMessage
.id
;
728 if ($scope.lastMessageId
!== lastMessageId
) {
729 $scope.onNewMessage(wasListingForMySubmission
);
731 $scope.lastMessageId
= lastMessageId
;
732 if($scope.pusers
=== ''){ // refresh current in chat list.
733 angular
.forEach($filter('unique')($scope.messages
,'sender_id'), function(m
,k
){
735 angular
.forEach($scope.pusers
, function(id
) {
736 if(id
=== m
.sender_id
){ flg
= true; }
738 if(!flg
) $scope.pusers
.push(m
.sender_id
);
745 $scope.onNewMessage
= function(wasListingForMySubmission
) {
746 if ($scope.lastMessageId
&& !wasListingForMySubmission
) {
748 $scope.pageTitleNotificator
.on('New message');
749 $scope.notifyLastMessage();
752 window
.addEventListener('focus', function() {
753 $scope.pageTitleNotificator
.off();
757 $scope.getAuthUsers
= function() {
758 $scope.chatusers
= [];
759 return $http.post($scope.urlGetAuthUsers
, {}).success(function(data
) {
760 $scope.chatusers
= data
;
764 $scope.pingServer
= function(msgItem
) {
765 return $http.post($scope.urlListOnlines+
'&username='+
$scope.user
, {}).success(function(data
) {
766 $scope.online
= data
;
771 $scope.getOnlines
= function() {
772 return $http.post($scope.urlListOnlines+
'&username=currentol', {}).success(function(data
) {
773 $scope.onlines
= data
;
777 $scope.init
= function() {
778 $scope.listMessages();
779 $scope.pidMessages
= window
.setInterval($scope.listMessages
, 6000);
780 $scope.pidPingServer
= window
.setInterval($scope.pingServer
, 10000);
781 $scope.getAuthUsers();
782 $
("#popeditor").on("show.bs.modal", function() {
783 var height
= $
(window
).height() - 200;
784 $
(this
).find(".modal-body").css("max-height", height
);
788 $scope.scrollDown
= function() {
790 pidScroll
= window
.setInterval(function() {
791 $
('.direct-chat-messages').scrollTop(window
.Number
.MAX_SAFE_INTEGER
* 0.001);
792 window
.clearInterval(pidScroll
);
796 $scope.clearHistory
= function() {
797 var lastMessage
= $scope.getLastMessage();
798 var lastMessageId
= lastMessage
&& lastMessage
.id
;
799 lastMessageId
= (lastMessageId
-1 >= 2) ? lastMessageId
-1 : lastMessageId
;
800 lastMessageId
&& ($scope.historyFromId
= lastMessageId
);
803 $scope.openModal
= function(e
) {
804 var mi
= $
('#popeditor').modal({backdrop
: "static"});
808 $scope.playAudio
= function() {
809 $scope.beep
&& $scope.beep
.play();
812 $scope.renderMessageBody
= function(html
)
826 border
:1px solid
#6a6a6a;
830 .direct
-chat
-msg
,.direct
-chat
-text
{
832 word
-wrap
: break-word
;
848 .direct
-chat
-messages
,.direct
-chat
-contacts
{
849 -webkit
-transition
:-webkit
-transform
.5s ease
-in
-out
;
850 -moz
-transition
:-moz
-transform
.5s ease
-in
-out
;
851 -o
-transition
:-o
-transform
.5s ease
-in
-out
;
852 transition
:transform
.5s ease
-in
-out
;
854 .direct
-chat
-messages
{
855 -webkit
-transform
:translate(0,0);
856 -ms
-transform
:translate(0,0);
857 -o
-transform
:translate(0,0);
858 transform
:translate(0,0);
860 height
: calc(100vh
- 175px
);
864 word
-wrap
: break-word
;
866 .direct
-chat
-text
:before
{
870 .direct
-chat
-text
:after
{
874 .direct
-chat
-text
:after
,.direct
-chat
-text
:before
{
878 border
:solid
rgba(0,0,0,0);
879 border
-right
-color
:#D2D6DE;
885 .direct
-chat
-warning
.right
>.direct
-chat
-text
{
886 background
: rgba(251, 255, 178, 0.34);
887 border
-color
: #f30d1b;
890 .right
.direct
-chat
-text
{
894 .direct
-chat
-warning
.right
>.direct
-chat
-text
:after
,
895 .direct
-chat
-warning
.right
>.direct
-chat
-text
:before
{
896 border
-left
-color
:#F39C12;
898 .right
.direct
-chat
-text
:after
,.right
.direct
-chat
-text
:before
{
901 border
-right
-color
:rgba(0,0,0,0);
902 border
-left
-color
:#D2D6DE;
904 .right
.direct
-chat
-img
{
908 border
-top
-left
-radius
:0;
909 border
-top
-right
-radius
:0;
910 border
-bottom
-right
-radius
:3px
;
911 border
-bottom
-left
-radius
:3px
;
912 border
-top
:1px solid
#F4F4F4;
914 background
-color
:#FFF;
922 input
,button
,.alert
,.modal
-content
{
923 border
-radius
: 0!important
;
932 background
-color
: ghostwhite
;
937 /*max-height: 730px;*/
938 height
: calc(100vh
- 100px
);
942 background
-color
: ghostwhite
;
946 height
: calc(100vh
- 100px
);
951 padding
: 5px
5px
0 5px
;
954 font
-size
:16px
!important
;
956 label
{display
: block
;}
962 .modal
.modal
-wide
.modal
-dialog
{
965 .modal
-wide
.modal
-body
{
970 <body ng
-controller
="MsgAppCtrl">
971 <div
class="container">
972 <!-- <h2
class="hidden-xs">Secure Chat
</h2
> -->
974 <div
class="col-md-2 sidebar">
975 <h5
><span
class="label label-default"><?php
echo xlt('Current Recipients'); ?
></span
></h5
>
976 <label ng
-repeat
="user in chatusers | unique : 'username'" ng
-if="pusers.indexOf(user.recip_id) !== -1 && user.recip_id != me.sender_id">
977 <input type
="checkbox" data
-checklist
-model
="pusers" data
-checklist
-value
="user.recip_id"> {{user
.username
}}
979 <h5
><span
class="label label-default"><?php
echo xlt('Available Recipients'); ?
></span
></h5
>
981 <button id
="chkall" class="btn btn-xs btn-success" ng
-show
="!isPortal" ng
-click
="checkAll()" type
="button"><?php
echo xlt('All'); ?
></button
>
982 <button id
="chknone" class="btn btn-xs btn-success" ng
-show
="!isPortal" ng
-click
="uncheckAll()" type
="button"><?php
echo xlt('None'); ?
></button
>
984 <label ng
-repeat
="user in chatusers | unique : 'username'" ng
-show
="!isPortal || (isPortal && user.dash)">
985 <input type
="checkbox" data
-checklist
-model
="pusers" data
-checklist
-value
="user.recip_id"> {{user
.username
}}
988 <div
class="col-md-8 fixed-panel">
989 <div
class="panel direct-chat direct-chat-warning">
990 <div
class="panel-heading">
991 <div
class="clearfix">
992 <a
class="btn btn-sm btn-primary ml10" href
=""
993 data
-toggle
="modal" data
-target
="#clear-history"><?php
echo xlt('Clear history'); ?
></a
>
994 <a
class="btn btn-sm btn-success pull-left ml10" href
="./../patient/provider" ng
-show
="!isPortal"><?php
echo xlt('Home'); ?
></a
>
995 <a
class="btn btn-sm btn-success pull-left ml10" href
="./../home.php" ng
-show
="isFullScreen"><?php
echo xlt('Home'); ?
></a
>
998 <div
class="panel-body">
999 <div
class="direct-chat-messages">
1000 <div
class="direct-chat-msg" ng
-repeat
="message in messages" ng
-if="historyFromId < message.id" ng
-class="{'right':!message.me}">
1001 <div
class="direct-chat-info clearfix">
1002 <span
class="direct-chat-name" ng
-class="{'pull-left':message.me,'pull-right':!message.me}">{{message
.username
}}</span
>
1003 <span
class="direct-chat-timestamp " ng
-class="{'pull-left':!message.me,'pull-right':message.me}">{{message
.date
}}</span
>
1005 <i
class="direct-chat-img glyphicon glyphicon-hand-left"
1006 style
="cursor: pointer;font-size:24px" ng
-show
="!message.me"
1007 ng
-click
="makeCurrent(message)"
1008 title
="<?php echo xla('Click to activate and send to this recipient.'); ?>"></i
>
1009 <i
class="direct-chat-img glyphicon glyphicon-hand-right"
1010 style
="cursor: pointer;font-size:24px" ng
-show
="message.me"
1011 ng
-click
="makeCurrent(message)"
1012 title
="<?php echo xla('Click to activate and send to this recipient.'); ?>"></i
>
1014 <div
class="direct-chat-text right">
1015 <div style
="padding-left: 0px; padding-right: 0px;"
1016 title
="<?php echo xla('Click to activate and send to this recipient.'); ?>"
1017 ng
-click
="makeCurrent(message)"
1018 ng
-bind
-html
=renderMessageBody(message
.message
)></div
>
1022 <div
class="panel-footer box-footer-hide">
1023 <form id
='msgfrm' ng
-submit
="saveMessage()">
1024 <div
class="input-group">
1025 <input type
="text" placeholder
="Type message..." id
="msgedit" autofocus
="autofocus"
1026 class="form-control" ng
-model
="me.message" ng
-enter
="saveMessage()">
1027 <span
class="input-group-btn">
1028 <button type
="submit" class="btn btn-danger btn-flat"><?php
echo xlt('Send'); ?
></button
>
1029 <button type
="button" class="btn btn-success btn-flat" ng
-click
="openModal(event)"><?php
echo xlt('Edit'); ?
></button
>
1037 <div
class="col-md-2 rtsidebar">
1038 <h5
><span
class="label label-default"><?php
echo xlt('Whose Online'); ?
> : {{ online
.total ||
'0' }}</span
>
1040 <label ng
-repeat
="ol in onlines | unique : 'username'">
1041 <input type
="checkbox" data
-checklist
-model
="onlines" data
-checklist
-value
="ol"> {{ol
.username
}}
1046 <div
class="modal modal-wide fade" id
="popeditor">
1047 <div
class="modal-dialog modal-lg">
1048 <div
class="modal-content">
1050 <div
class="modal-header">
1051 <button type
="button" class="close" data
-dismiss
="modal">
1052 <span aria
-hidden
="true">×
;</span
>
1053 <span
class="sr-only"><?php
echo xlt('Close'); ?
></span
>
1055 <h4
class="modal-title"><?php
echo xlt('Style your messsage and/or add Image/Video'); ?
></h4
>
1057 <div
class="modal-body">
1058 <summernote config
="options"></summernote
>
1060 <div
class="modal-footer">
1061 <button type
="button" class="btn btn-sm" data
-dismiss
="modal"><?php
echo xlt('Dismiss'); ?
></button
>
1062 <button type
="button" class="btn btn-success" data
-dismiss
="modal" ng
-click
="saveedit()"><?php
echo xlt('Send It'); ?
></button
>
1068 <div
class="modal" id
="clear-history">
1069 <div
class="modal-dialog">
1070 <div
class="modal-content">
1072 <div
class="modal-header">
1073 <button type
="button" class="close" data
-dismiss
="modal">
1074 <span aria
-hidden
="true">×
;</span
>
1075 <span
class="sr-only"><?php
echo xlt('Close'); ?
></span
>
1077 <h4
class="modal-title"><?php
echo xlt('Chat history'); ?
></h4
>
1079 <div
class="modal-body">
1080 <label
class="radio"><?php
echo xlt('Are you sure to clear chat history?'); ?
></label
>
1082 <div
class="modal-footer">
1083 <button type
="button" class="btn btn-sm btn-default" data
-dismiss
="modal"><?php
echo xlt('Cancel'); ?
></button
>
1084 <button type
="button" class="btn btn-sm btn-primary" data
-dismiss
="modal" ng
-click
="clearHistory()"><?php
echo xlt('Accept'); ?
></button
>