4 * Copyright (C) 2016-2017 Jerry Padgett <sjpadgett@gmail.com>
6 * LICENSE: This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as
8 * published by the Free Software Foundation, either version 3 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * @author Jerry Padgett <sjpadgett@gmail.com>
21 * @link http://www.open-emr.org
27 if (isset($_SESSION['pid']) && isset($_SESSION['patient_portal_onsite_two'])) {
28 $pid = $_SESSION['pid'];
30 require_once(dirname(__FILE__
) . "/../../interface/globals.php");
31 define('IS_DASHBOARD', false);
32 define('IS_PORTAL', $_SESSION['pid']);
36 require_once(dirname(__FILE__
) . "/../../interface/globals.php");
37 if (! isset($_SESSION['authUserID'])) {
38 $landingpage = "index.php";
39 header('Location: '.$landingpage);
43 define('IS_DASHBOARD', $_SESSION['authUserID']);
44 define('IS_PORTAL', false);
45 $_SERVER[REMOTE_ADDR
] = 'admin::' . $_SERVER[REMOTE_ADDR
];
48 define('C_USER', IS_PORTAL ? IS_PORTAL
: IS_DASHBOARD
);
50 if (isset($_REQUEST['fullscreen'])) {
51 $_SESSION['whereto'] = 'messagespanel';
52 define('IS_FULLSCREEN', true);
54 define('IS_FULLSCREEN', false);
57 define('CHAT_HISTORY', '150');
58 define('CHAT_ONLINE_RANGE', '1');
59 define('ADMIN_USERNAME_PREFIX', 'adm_');
65 public function __construct()
67 //$this->db = new \mysqli(DB_HOST, DB_USERNAME, DB_PASSWORD, DB_NAME);
71 abstract class Controller
73 private $_request, $_response, $_query, $_post, $_server, $_cookies, $_session;
74 protected $_currentAction, $_defaultModel;
76 const ACTION_POSTFIX
= 'Action';
77 const ACTION_DEFAULT
= 'indexAction';
79 public function __construct()
81 $this->_request
= &$_REQUEST;
82 $this->_query
= &$_GET;
83 $this->_post
= &$_POST;
84 $this->_server
= &$_SERVER;
85 $this->_cookies
= &$_COOKIE;
86 $this->_session
= &$_SESSION;
90 public function init()
92 $this->dispatchActions();
96 public function dispatchActions()
98 $action = $this->getQuery('action');
99 if ($action && $action .= self
::ACTION_POSTFIX
) {
100 if (method_exists($this, $action)) {
102 call_user_func(array($this, $action), array())
105 $this->setHeader("HTTP/1.0 404 Not Found");
109 call_user_func(array($this, self
::ACTION_DEFAULT
), array())
113 return $this->_response
;
116 public function render()
118 if ($this->_response
) {
119 if (is_scalar($this->_response
)) {
120 echo $this->_response
;
122 throw new \
Exception('Response content must be scalar');
129 public function indexAction()
134 public function setResponse($content)
136 $this->_response
= $content;
139 public function setHeader($params)
141 if (! headers_sent()) {
142 if (is_scalar($params)) {
145 foreach ($params as $key => $value) {
146 header(sprintf('%s: %s', $key, $value));
154 public function setModel($namespace)
156 $this->_defaultModel
= $namespace;
160 public function setSession($key, $value)
162 $_SESSION[$key] = $value;
166 public function setCookie($key, $value, $seconds = 3600)
168 $this->_cookies
[$key] = $value;
169 if (! headers_sent()) {
170 setcookie($key, $value, time() +
$seconds);
175 public function getRequest($param = null, $default = null)
178 return isset($this->_request
[$param]) ?
179 $this->_request
[$param] : $default;
182 return $this->_request
;
185 public function getQuery($param = null, $default = null)
188 return isset($this->_query
[$param]) ?
189 $this->_query
[$param] : $default;
192 return $this->_query
;
195 public function getPost($param = null, $default = null)
198 return isset($this->_post
[$param]) ?
199 $this->_post
[$param] : $default;
205 public function getServer($param = null, $default = null)
208 return isset($this->_server
[$param]) ?
209 $this->_server
[$param] : $default;
212 return $this->_server
;
215 public function getSession($param = null, $default = null)
218 return isset($this->_session
[$param]) ?
219 $this->_session
[$param] : $default;
222 return $this->_session
;
225 public function getCookie($param = null, $default = null)
228 return isset($this->_cookies
[$param]) ?
229 $this->_cookies
[$param] : $default;
232 return $this->_cookies
;
235 public function getUser()
237 return $this->_session
['ptName'] ?
$this->_session
['ptName'] : $this->_session
['authUser'];
239 public function getIsPortal()
243 public function getIsFullScreen()
245 return IS_FULLSCREEN
;
247 public function getModel()
249 if ($this->_defaultModel
&& class_exists($this->_defaultModel
)) {
250 return new $this->_defaultModel
;
254 public function sanitize($string, $quotes = ENT_QUOTES
, $charset = 'utf-8')
256 return htmlentities($string, $quotes, $charset);
260 abstract class Helper
267 // @codingStandardsIgnoreStart
269 // @codingStandardsIgnoreEnd
270 class Model
extends SMA_Common\Model
272 public function getAuthUsers()
274 $response = sqlStatementNoLog("SELECT patient_data.pid as recip_id, Concat_Ws(' ', patient_data.fname, patient_data.lname) as username FROM patient_data WHERE allow_patient_portal = 'YES'");
275 $resultpd = array ();
276 while ($row = sqlFetchArray($response)) {
280 $response = sqlStatementNoLog("SELECT users.id as recip_id, users.authorized as dash, CONCAT(users.fname,' ',users.lname) as username FROM users WHERE authorized = 1");
282 while ($row = sqlFetchArray($response)) {
286 return json_encode(array_merge($result, $resultpd));
288 public function getMessages($limit = CHAT_HISTORY
, $reverse = true)
290 $response = sqlStatementNoLog("(SELECT * FROM onsite_messages
291 ORDER BY `date` DESC LIMIT {$limit}) ORDER BY `date` ASC");
294 while ($row = sqlFetchArray($response)) {
296 $u = json_decode($row['recip_id'], true);
301 if ((in_array(C_USER
, $u)) ||
$row['sender_id'] == C_USER
) {
302 $result[] = $row; // only current patient messages
305 $result[] = $row; // admin gets all
312 public function addMessage($username, $message, $ip, $senderid = 0, $recipid = '')
314 return sqlQueryNoLog("INSERT INTO onsite_messages VALUES (NULL, ?, ?, ?, NOW(), ?, ?)", array($username,$message,$ip,$senderid,$recipid));
317 public function removeMessages()
319 return sqlQueryNoLog("TRUNCATE TABLE onsite_messages");
322 public function removeOldMessages($limit = CHAT_HISTORY
)
324 /* @todo Patched out to replace with soft delete. Besides this query won't work with current ado(or any) */
325 /* return sqlStatementNoLog("DELETE FROM onsite_messages
326 WHERE id NOT IN (SELECT id FROM onsite_messages
327 ORDER BY date DESC LIMIT {$limit})"); */
330 public function getOnline($count = true, $timeRange = CHAT_ONLINE_RANGE
)
333 $response = sqlStatementNoLog("SELECT count(*) as total FROM onsite_online");
334 return sqlFetchArray($response);
337 $response = sqlStatementNoLog("SELECT * FROM onsite_online");
339 while ($row = sqlFetchArray($response)) {
346 public function updateOnline($hash, $ip, $username = '', $userid = 0)
348 return sqlStatementNoLog("REPLACE INTO onsite_online
349 VALUES ( ?, ?, NOW(), ?, ? )", array($hash, $ip, $username, $userid)) or die(mysql_error());
352 public function clearOffline($timeRange = CHAT_ONLINE_RANGE
)
354 return sqlStatementNoLog("DELETE FROM onsite_online
355 WHERE last_update <= (NOW() - INTERVAL {$timeRange} MINUTE)");
358 public function __destruct()
363 class Controller
extends SMA_Common\Controller
367 public function __construct()
369 $this->setModel('SMA_Msg\Model');
370 parent
::__construct();
373 public function indexAction()
376 public function authusersAction()
378 return $this->getModel()->getAuthUsers(true);
380 public function listAction()
382 $this->setHeader(array('Content-Type' => 'application/json'));
383 $messages = $this->getModel()->getMessages();
384 foreach ($messages as &$message) {
385 $message['me'] = C_USER
=== $message['sender_id']; // $this->getServer('REMOTE_ADDR') === $message['ip'];
388 return json_encode($messages);
391 public function saveAction()
393 $username = $this->getPost('username');
394 $message = $this->getPost('message');
395 $ip = $this->getServer('REMOTE_ADDR');
396 $this->setCookie('username', $username, 9999 * 9999);
397 $recipid = $this->getPost('recip_id');
400 $senderid = IS_PORTAL
;
402 $senderid = IS_DASHBOARD
;
405 $result = array('success' => false);
406 if ($username && $message) {
407 $cleanUsername = preg_replace('/^'.ADMIN_USERNAME_PREFIX
.'/', '', $username);
409 'success' => $this->getModel()->addMessage($cleanUsername, $message, $ip, $senderid, $recipid)
413 if ($this->_isAdmin($username)) {
414 $this->_parseAdminCommand($message);
417 $this->setHeader(array('Content-Type' => 'application/json'));
418 return json_encode($result);
421 private function _isAdmin($username)
423 return IS_DASHBOARD?
true:false;
424 //return preg_match('/^'.ADMIN_USERNAME_PREFIX.'/', $username);
427 private function _parseAdminCommand($message)
429 if (strpos($message, '/clear') !== false) {
430 $this->getModel()->removeMessages();
434 if (strpos($message, '/online') !== false) {
435 $online = $this->getModel()->getOnline(false);
437 foreach ($online as $item) {
438 $ipArr[] = $item->ip
;
441 $message = 'Online: ' . implode(", ", $ipArr);
442 $this->getModel()->addMessage('Admin Command', $message, '0.0.0.0');
447 private function _getMyUniqueHash()
449 $unique = $this->getServer('REMOTE_ADDR');
450 $unique .= $this->getServer('HTTP_USER_AGENT');
451 $unique .= $this->getServer('HTTP_ACCEPT_LANGUAGE');
456 public function pingAction()
458 $ip = $this->getServer('REMOTE_ADDR');
459 $hash = $this->_getMyUniqueHash();
460 $user = $this->getRequest('username', 'No Username');
461 if ($user == 'currentol') {
462 $onlines = $this->getModel()->getOnline(false);
463 $this->setHeader(array('Content-Type' => 'application/json'));
464 return json_encode($onlines);
470 $userid = IS_DASHBOARD
;
473 $this->getModel()->updateOnline($hash, $ip, $user, $userid);
474 $this->getModel()->clearOffline();
475 // $this->getModel()->removeOldMessages(); // @todo For soft delete when I decide. DO NOT REMOVE
477 $onlines = $this->getModel()->getOnline();
479 $this->setHeader(array('Content-Type' => 'application/json'));
480 return json_encode($onlines);
484 $msgApp = new Controller();
487 <html ng
-app
="MsgApp">
489 <meta name
="viewport" content
="initial-scale=1.0, user-scalable=no">
490 <meta charset
="utf-8">
492 <title
><?php
echo xlt('Secure Patient Chat'); ?
></title
>
493 <meta name
="author" content
="Jerry Padgett sjpadgett{{at}} gmail {{dot}} com">
495 <script type
='text/javascript' src
="<?php echo $GLOBALS['assets_static_relative']; ?>/jquery-min-1-11-3/index.js"></script
>
497 <link href
="<?php echo $GLOBALS['assets_static_relative']; ?>/bootstrap-3-3-4/dist/css/bootstrap.min.css" rel
="stylesheet" type
="text/css" />
498 <?php
if ($_SESSION['language_direction'] == 'rtl') { ?
>
499 <link href
="<?php echo $GLOBALS['assets_static_relative']; ?>/bootstrap-rtl-3-3-4/dist/css/bootstrap-rtl.min.css" rel
="stylesheet" type
="text/css" />
502 <script type
='text/javascript' src
="<?php echo $GLOBALS['assets_static_relative']; ?>/bootstrap-3-3-4/dist/js/bootstrap.min.js"></script
>
504 <link rel
="stylesheet" href
="<?php echo $GLOBALS['assets_static_relative']; ?>/summernote-0-8-2/dist/summernote.css" />
505 <script type
='text/javascript' src
="<?php echo $GLOBALS['assets_static_relative']; ?>/summernote-0-8-2/dist/summernote.js"></script
>
507 <script type
='text/javascript' src
="<?php echo $GLOBALS['assets_static_relative']; ?>/angular-1-5-8/angular.min.js"></script
>
508 <script type
='text/javascript' src
="<?php echo $GLOBALS['assets_static_relative']; ?>/angular-summernote-0-8-1/dist/angular-summernote.js"></script
>
509 <script type
='text/javascript' src
="<?php echo $GLOBALS['assets_static_relative']; ?>/angular-sanitize-1-5-8/angular-sanitize.min.js"></script
>
510 <script src
='<?php echo $GLOBALS['assets_static_relative
']; ?>/checklist-model-0-10-0/checklist-model.js'></script
>
513 <script type
="text/javascript">
515 var MsgApp
= angular
.module('MsgApp',['ngSanitize','summernote',"checklist-model"]);
516 MsgApp
.config(function( $compileProvider ) {
517 $compileProvider.aHrefSanitizationWhitelist(/^\s
*(https?|file|ftp|blob
):|data
:image\
//);
520 MsgApp
.directive('ngEnter', function () {
521 return function (scope
, element
, attrs
) {
522 element
.bind("keydown keypress", function (event
) {
523 if (event
.which
=== 13) {
524 scope
.$apply(function (){
525 scope
.$eval(attrs
.ngEnter
);
527 event
.preventDefault();
532 MsgApp
.directive('tooltip', function(e
){
535 link
: function(scope
, element
, attrs
){
536 $
(element
).hover(function(){
537 $
(element
).tooltip('show');
539 $
(element
).tooltip('hide');
544 MsgApp
.filter('unique', function() {
545 return function(collection
, keyname
) {
548 angular
.forEach(collection
, function(item
) {
549 var key
= item
[keyname
];
550 if(keys
.indexOf(key
) === -1) {
559 MsgApp
.controller('MsgAppCtrl', ['$scope', '$http', '$filter', function($scope, $http, $filter) {
560 $scope.urlListMessages
= '?action=list'; // actions for restful
561 $scope.urlSaveMessage
= '?action=save';
562 $scope.urlListOnlines
= '?action=ping';
563 $scope.urlGetAuthUsers
= '?action=authusers';
565 $scope.pidMessages
= null;
566 $scope.pidPingServer
= null;
568 $scope.beep
= new Audio('beep.ogg'); // you've got mail!!!! really just a beep
569 $scope.messages
= [];
570 $scope.online
= null;
571 $scope.lastMessageId
= null;
572 $scope.historyFromId
= null;
573 $scope.onlines
= []; // all online users id and ip's
574 $scope.user
= "<?php echo $_SESSION['ptName'] ? $_SESSION['ptName'] : $_SESSION['authUser'];?>" // current user - dashboard user is from session authUserID
575 $scope.userid
= "<?php echo IS_PORTAL ? $_SESSION['pid'] : $_SESSION['authUserID'];?>"
576 $scope.isPortal
= "<?php echo IS_PORTAL;?>" ;
577 $scope.isFullScreen
= "<?php echo IS_FULLSCREEN; ?>";
578 $scope.pusers
= []; // selected recipients for chat
579 $scope.chatusers
= []; // authorize chat recipients for dashboard user
581 username
: $scope.user
,
583 sender_id
: $scope.userid
,
589 placeholder
: 'Start typing your message...',
592 ['style', ['bold', 'italic', 'underline', 'clear']],
593 ['fontsize', ['fontsize']],
594 ['color', ['color']],
595 ['para', ['ul', 'ol', 'paragraph']],
596 ['insert', ['link','picture', 'video', 'hr']],
597 ['view', ['fullscreen', 'codeview']]
600 $scope.checkAll
= function() {
602 $scope.pusers
= $scope.chatusers
.map(function(item
) { return item
.recip_id
; });
603 $scope.getAuthUsers();
605 $scope.uncheckAll
= function() {
607 $scope.getAuthUsers();
609 $scope.makeCurrent
= function(sel
) {
611 $scope.pusers
.splice(0, $scope.pusers
.length
);
612 $scope.pusers
.push(sel
.sender_id
);
615 $scope.pageTitleNotificator
= {
617 originalTitle
: window
.document
.title
,
621 on
: function(title
, intervalSpeed
) {
623 if (! self
.vars
.status
) {
624 self
.vars
.interval
= window
.setInterval(function() {
625 window
.document
.title
= (self
.vars
.originalTitle
== window
.document
.title
) ?
626 title
: self
.vars
.originalTitle
;
627 }, intervalSpeed ||
500);
628 self
.vars
.status
= 1;
632 window
.clearInterval(this
.vars
.interval
);
633 window
.document
.title
= this
.vars
.originalTitle
;
634 this
.vars
.status
= 0;
638 $scope.editmsg
= function() {
639 $
('.summernote').summernote();
642 $scope.saveedit
= function() {
643 var makrup
= $
('.summernote').summernote('code');
644 $scope.me
.message
= makrup
;
645 $scope.saveMessage();
646 $
('.summernote').summernote('code', ''); //add this options to reset editor or not-default is persistant content
647 //$('.summernote').summernote('destroy');
650 $scope.saveMessage
= function(form
, callback
) {
651 $scope.me
.recip_id
= JSON
.stringify(angular
.copy($scope.pusers
));
652 var data
= $
.param($scope.me
);
653 if (! ($scope.me
.username
&& $scope.me
.username
.trim())) {
654 return $scope.openModal();
656 if (! ($scope.me
.message
&& $scope.me
.message
.trim() &&
657 $scope.me
.username
&& $scope.me
.username
.trim())) {
660 $scope.me
.message
= '';
663 url
: $scope.urlSaveMessage
,
665 headers
: {'Content-Type': 'application/x-www-form-urlencoded'}
666 }).success(function(data
) {
667 $scope.listMessages(true);
671 $scope.replaceShortcodes
= function(message
) {
673 msg
= message
.toString().replace(/(\
[img
])(.*)(\
[\
/img
])/, "<img class='img-responsive' src='$2' />");
674 msg
= msg
.toString().replace(/(\
[url
])(.*)(\
[\
/url
])/, "<a href='$2'>$2</a>");
675 msg
= message
.toString().replace("<img ", "<img class='img-responsive' ");
679 $scope.notifyLastMessage
= function() {
680 if (typeof window
.Notification
=== 'undefined') {
683 window
.Notification
.requestPermission(function (permission
) {
684 var lastMessage
= $scope.getLastMessage();
685 if (permission
== 'granted' && lastMessage
&& lastMessage
.username
) {
686 var notify
= new window
.Notification('Message notification from ' + lastMessage
.username +
' : ', {
687 body
: 'New message' //lastMessage.message
689 notify
.onclick
= function() {
692 notify
.onclose
= function() {
693 $scope.pageTitleNotificator
.off();
695 var timmer
= setInterval(function() {
696 notify
&& notify
.close();
697 typeof timmer
!== 'undefined' && window
.clearInterval(timmer
);
703 $scope.getLastMessage
= function() {
704 return $scope.messages
[$scope.messages
.length
- 1];
707 $scope.listMessages
= function(wasListingForMySubmission
) {
708 return $http.post($scope.urlListMessages
, {}).success(function(data
) {
709 $scope.messages
= [];
710 angular
.forEach(data
, function(message
) {
711 message
.message
= $scope.replaceShortcodes(message
.message
);
712 $scope.messages
.push(message
);
715 var lastMessage
= $scope.getLastMessage();
716 var lastMessageId
= lastMessage
&& lastMessage
.id
;
718 if ($scope.lastMessageId
!== lastMessageId
) {
719 $scope.onNewMessage(wasListingForMySubmission
);
721 $scope.lastMessageId
= lastMessageId
;
722 if($scope.pusers
== ''){ // overkill but may need later
723 angular
.forEach($filter('unique')($scope.messages
,'sender_id'), function(m
,k
){
725 angular
.forEach($scope.pusers
, function(id
) {
726 if(id
== m
.sender_id
){ flg
= true; }
728 if(!flg
) $scope.pusers
.push(m
.sender_id
);
735 $scope.onNewMessage
= function(wasListingForMySubmission
) {
736 if ($scope.lastMessageId
&& !wasListingForMySubmission
) {
738 $scope.pageTitleNotificator
.on('New message');
739 $scope.notifyLastMessage();
742 window
.addEventListener('focus', function() {
743 $scope.pageTitleNotificator
.off();
746 $scope.getAuthUsers
= function() {
747 $scope.chatusers
= [];
748 return $http.post($scope.urlGetAuthUsers
, {}).success(function(data
) {
749 $scope.chatusers
= data
;
752 $scope.pingServer
= function(msgItem
) {
753 return $http.post($scope.urlListOnlines+
'&username='+
$scope.user
, {}).success(function(data
) {
754 $scope.online
= data
;
759 $scope.getOnlines
= function() {
760 return $http.post($scope.urlListOnlines+
'&username=currentol', {}).success(function(data
) {
761 $scope.onlines
= data
;
765 $scope.init
= function() {
766 $scope.listMessages();
767 $scope.pidMessages
= window
.setInterval($scope.listMessages
, 6000);
768 $scope.pidPingServer
= window
.setInterval($scope.pingServer
, 10000);
769 $scope.getAuthUsers();
770 $
("#popeditor").on("show.bs.modal", function() {
771 var height
= $
(window
).height() - 200;
772 $
(this
).find(".modal-body").css("max-height", height
);
776 $scope.scrollDown
= function() {
778 pidScroll
= window
.setInterval(function() {
779 $
('.direct-chat-messages').scrollTop(window
.Number
.MAX_SAFE_INTEGER
* 0.001);
780 window
.clearInterval(pidScroll
);
784 $scope.clearHistory
= function() {
785 var lastMessage
= $scope.getLastMessage();
786 var lastMessageId
= lastMessage
&& lastMessage
.id
;
787 lastMessageId
= (lastMessageId
-1 >= 2) ? lastMessageId
-1 : lastMessageId
;
788 lastMessageId
&& ($scope.historyFromId
= lastMessageId
);
791 $scope.openModal
= function(e
) {
792 var mi
= $
('#popeditor').modal({backdrop
: "static"});
796 $scope.playAudio
= function() {
797 $scope.beep
&& $scope.beep
.play();
800 $scope.renderMessageBody
= function(html
)
814 border
:1px solid
#C2C6CE;
818 .direct
-chat
-msg
,.direct
-chat
-text
{
820 word
-wrap
: break-word
;
836 .direct
-chat
-messages
,.direct
-chat
-contacts
{
837 -webkit
-transition
:-webkit
-transform
.5s ease
-in
-out
;
838 -moz
-transition
:-moz
-transform
.5s ease
-in
-out
;
839 -o
-transition
:-o
-transform
.5s ease
-in
-out
;
840 transition
:transform
.5s ease
-in
-out
;
842 .direct
-chat
-messages
{
843 -webkit
-transform
:translate(0,0);
844 -ms
-transform
:translate(0,0);
845 -o
-transform
:translate(0,0);
846 transform
:translate(0,0);
848 height
: calc(100vh
- 175px
);
852 word
-wrap
: break-word
;
854 .direct
-chat
-text
:before
{
858 .direct
-chat
-text
:after
{
862 .direct
-chat
-text
:after
,.direct
-chat
-text
:before
{
866 border
:solid
rgba(0,0,0,0);
867 border
-right
-color
:#D2D6DE;
873 .direct
-chat
-warning
.right
>.direct
-chat
-text
{
875 border
-color
:#F39C12;
878 .right
.direct
-chat
-text
{
882 .direct
-chat
-warning
.right
>.direct
-chat
-text
:after
,
883 .direct
-chat
-warning
.right
>.direct
-chat
-text
:before
{
884 border
-left
-color
:#F39C12;
886 .right
.direct
-chat
-text
:after
,.right
.direct
-chat
-text
:before
{
889 border
-right
-color
:rgba(0,0,0,0);
890 border
-left
-color
:#D2D6DE;
892 .right
.direct
-chat
-img
{
896 border
-top
-left
-radius
:0;
897 border
-top
-right
-radius
:0;
898 border
-bottom
-right
-radius
:3px
;
899 border
-bottom
-left
-radius
:3px
;
900 border
-top
:1px solid
#F4F4F4;
902 background
-color
:#FFF;
910 input
,button
,.alert
,.modal
-content
{
911 border
-radius
: 0!important
;
920 background
-color
: lightgrey
;
929 background
-color
: lightgrey
;
936 padding
: 5px
5px
0 5px
;
938 label
{display
: block
;}
944 .modal
.modal
-wide
.modal
-dialog
{
947 .modal
-wide
.modal
-body
{
952 <body ng
-controller
="MsgAppCtrl">
953 <div
class="container">
954 <!-- <h2
class="hidden-xs">Secure Chat
</h2
> -->
956 <div
class="col-md-2 sidebar">
957 <h4
><span
class="label label-danger"><?php
echo xlt('In Current Chat'); ?
></span
></h4
>
958 <label ng
-repeat
="user in chatusers | unique : 'username'" ng
-if="pusers.indexOf(user.recip_id) !== -1 && user.recip_id != me.sender_id">
959 <input type
="checkbox" data
-checklist
-model
="pusers" data
-checklist
-value
="user.recip_id"> {{user
.username
}}
961 <h4
><span
class="label label-warning"><?php
echo xlt('Authorized Users'); ?
></span
></h4
>
963 <button id
="chkall" class="btn btn-xs btn-success" ng
-show
="!isPortal" ng
-click
="checkAll()" type
="button"><?php
echo xlt('All'); ?
></button
>
964 <button id
="chknone" class="btn btn-xs btn-success" ng
-show
="!isPortal" ng
-click
="uncheckAll()" type
="button"><?php
echo xlt('None'); ?
></button
>
966 <label ng
-repeat
="user in chatusers | unique : 'username'" ng
-show
="!isPortal || (isPortal && user.dash)">
967 <input type
="checkbox" data
-checklist
-model
="pusers" data
-checklist
-value
="user.recip_id"> {{user
.username
}}
970 <div
class="col-md-8 fixed-panel">
971 <div
class="panel panel-warning direct-chat direct-chat-warning">
972 <div
class="panel-heading">
973 <div
class="clearfix">
974 <a
class="btn btn-sm btn-primary pull-right ml10" href
=""
975 data
-toggle
="modal" data
-target
="#clear-history"><?php
echo xlt('Clear history'); ?
></a
>
976 <a
class="btn btn-sm btn-success pull-left ml10" href
="./../patient/provider" ng
-show
="!isPortal"><?php
echo xlt('Home'); ?
></a
>
977 <a
class="btn btn-sm btn-success pull-left ml10" href
="./../home.php" ng
-show
="isFullScreen"><?php
echo xlt('Home'); ?
></a
>
980 <div
class="panel-body">
981 <div
class="direct-chat-messages">
982 <div
class="direct-chat-msg" ng
-repeat
="message in messages" ng
-if="historyFromId < message.id" ng
-class="{'right':!message.me}">
983 <div
class="direct-chat-info clearfix">
984 <span
class="direct-chat-name"
985 ng
-class="{'pull-left':message.me, 'pull-right':!message.me}">{{message
.username
}}</span
>
986 <span
class="direct-chat-timestamp "
987 ng
-class="{'pull-left':!message.me, 'pull-right':message.me}">{{message
.date
}}</span
>
989 <img
class="direct-chat-img" ng
-show
="!message.me"
990 src
="./../images/Unknown-person.gif"
992 <img
class="direct-chat-img" ng
-show
="message.me")
993 src
="<?php echo $GLOBALS['images_static_relative']; ?>/favicon-32x32.png"
995 <div
class="direct-chat-text right">
996 <div style
="padding-left: 0px; padding-right: 0px;" title
="<?php echo xlt('Click to make chat this current recipient only...'); ?>" ng
-click
="makeCurrent(message)" ng
-bind
-html
=renderMessageBody(message
.message
)></div
>
1000 <div
class="panel-footer box-footer-hide">
1001 <form id
='msgfrm' ng
-submit
="saveMessage()">
1002 <div
class="input-group">
1003 <input type
="text" placeholder
="Type message..." id
="msgedit" autofocus
="autofocus" class="form-control" ng
-model
="me.message" ng
-enter
="saveMessage()">
1004 <span
class="input-group-btn">
1005 <button type
="submit" class="btn btn-danger btn-flat"><?php
echo xlt('Send'); ?
></button
>
1006 <button type
="button" class="btn btn-success btn-flat" ng
-click
="openModal(event)"><?php
echo xlt('Edit'); ?
></button
>
1014 <div
class="col-md-2 rtsidebar">
1015 <h4
><span
class="label label-info"><?php
echo xlt('Online'); ?
> : {{ online
.total ||
'0' }}</span
></h4
>
1016 <label ng
-repeat
="ol in onlines | unique : 'username'">
1017 <input type
="checkbox" data
-checklist
-model
="onlines" data
-checklist
-value
="ol"> {{ol
.username
}}
1022 <div
class="modal modal-wide fade" id
="popeditor">
1023 <div
class="modal-dialog modal-lg">
1024 <div
class="modal-content">
1026 <div
class="modal-header">
1027 <button type
="button" class="close" data
-dismiss
="modal">
1028 <span aria
-hidden
="true">×
;</span
>
1029 <span
class="sr-only"><?php
echo xlt('Close'); ?
></span
>
1031 <h4
class="modal-title"><?php
echo xlt('Style your messsage and/or add Image/Video'); ?
></h4
>
1033 <div
class="modal-body">
1034 <summernote config
="options"></summernote
>
1036 <div
class="modal-footer">
1037 <button type
="button" class="btn btn-sm" data
-dismiss
="modal"><?php
echo xlt('Dismiss'); ?
></button
>
1038 <button type
="button" class="btn btn-success" data
-dismiss
="modal" ng
-click
="saveedit()"><?php
echo xlt('Send It'); ?
></button
>
1044 <div
class="modal" id
="clear-history">
1045 <div
class="modal-dialog">
1046 <div
class="modal-content">
1048 <div
class="modal-header">
1049 <button type
="button" class="close" data
-dismiss
="modal">
1050 <span aria
-hidden
="true">×
;</span
>
1051 <span
class="sr-only"><?php
echo xlt('Close'); ?
></span
>
1053 <h4
class="modal-title"><?php
echo xlt('Chat history'); ?
></h4
>
1055 <div
class="modal-body">
1056 <label
class="radio"><?php
echo xlt('Are you sure to clear chat history?'); ?
></label
>
1058 <div
class="modal-footer">
1059 <button type
="button" class="btn btn-sm btn-default" data
-dismiss
="modal"><?php
echo xlt('Cancel'); ?
></button
>
1060 <button type
="button" class="btn btn-sm btn-primary" data
-dismiss
="modal" ng
-click
="clearHistory()"><?php
echo xlt('Accept'); ?
></button
>