Initial commit

This commit is contained in:
Joey Kimsey
2024-08-04 21:15:59 -04:00
parent c9d1fb983f
commit 0d469501ee
695 changed files with 70184 additions and 71 deletions

View File

@ -0,0 +1,37 @@
<?php
/**
* app/plugins/chat/controllers/admin/chat.php
*
* This is the chat admin controller.
*
* @package TP Chat
* @version 3.0
* @author Joey Kimsey <Joey@thetempusproject.com>
* @link https://TheTempusProject.com
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
*/
namespace TheTempusProject\Controllers\Admin;
use TheTempusProject\Bedrock\Functions\Input;
use TheTempusProject\Houdini\Classes\Issues;
use TheTempusProject\Houdini\Classes\Views;
use TheTempusProject\Houdini\Classes\Navigation;
use TheTempusProject\Houdini\Classes\Components;
use TheTempusProject\Classes\AdminController;
use TheTempusProject\Models\Chat as ChatModel;
class Chat extends AdminController {
protected static $chat;
public function __construct() {
parent::__construct();
self::$title = 'Admin - Chat';
self::$chat = new ChatModel;
$view = Navigation::activePageSelect( 'nav.admin', '/admin/chat' );
Components::set( 'ADMINNAV', $view );
}
public function index() {
// Views::view( 'chat.admin.list', self::$chat->list() );
}
}

View File

@ -0,0 +1,45 @@
<?php
/**
* app/controllers/api/users.php
*
* This is the users' api controller.
*
* @version 3.0
* @author Joey Kimsey <Joey@thetempusproject.com>
* @link https://TheTempusProject.com
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
*/
namespace TheTempusProject\Controllers\Api;
use TheTempusProject\Models\User;
use TheTempusProject\Classes\ApiController;
use TheTempusProject\Houdini\Classes\Views;
use TheTempusProject\Bedrock\Functions\Input;
use TheTempusProject\Classes\Forms;
use TheTempusProject\Houdini\Classes\Template;
use TheTempusProject\Models\Chat;
class Messages extends ApiController {
public static $chat;
public function __construct() {
parent::__construct();
self::$chat = new Chat;
Template::setTemplate( 'api' );
}
public function sendMessage() {
$response = true;
if ( ! Forms::check( 'newChatMessage' ) ) {
$response = 'Invalid Form';
} else {
$response = self::$chat->create( Input::post( 'chatMessage' ) );
}
Views::view( 'api.response', ['response' => json_encode( [ 'data' => $response ], true )]);
}
public function getMessages() {
$response = Views::simpleView( 'chat.chat', self::$chat->recent( 50 ) );
Views::view( 'api.response', ['response' => json_encode( [ 'data' => $response ], true )]);
}
}

View File

@ -0,0 +1,191 @@
<?php
/**
* app/plugins/chat/controllers/chat.php
*
* This is the public chat controller.
*
* @package TP Chat
* @version 3.0
* @author Joey Kimsey <Joey@thetempusproject.com>
* @link https://TheTempusProject.com
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
*/
namespace TheTempusProject\Controllers;
use TheTempusProject\Hermes\Functions\Redirect;
use TheTempusProject\Bedrock\Functions\Upload;
use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Bedrock\Functions\Input;
use TheTempusProject\Bedrock\Functions\Session;
use TheTempusProject\Houdini\Classes\Issues;
use TheTempusProject\Houdini\Classes\Views;
use TheTempusProject\Classes\Controller;
use TheTempusProject\Classes\Forms;
use TheTempusProject\Models\Chat as ChatModel;
use TheTempusProject\Models\Upload as UploadModel;
use TheTempusProject\TheTempusProject as App;
use TheTempusProject\Houdini\Classes\Components;
use TheTempusProject\Houdini\Classes\Template;
use TheTempusProject\Canary\Canary as Debug;
class Chat extends Controller {
protected static $chat;
public function __construct() {
parent::__construct();
self::$chat = new ChatModel;
Template::setTemplate( 'chat' );
}
public function index() {
if ( !App::$isMember ) {
Session::flash( 'error', 'You do not have permission to view this page.' );
return Redirect::home();
}
self::$title = '{SITENAME} Chat';
self::$pageDescription = 'One of the privleges of membership is the ability to chat with your fellow members.';
if ( App::$isLoggedIn ) {
Components::set( 'CREATE_MESSAGE', Views::simpleView( 'chat.create' ) );
} else {
Components::set( 'CREATE_MESSAGE', '' );
}
$upload = '';
$sharePlugin = 'TheTempusProject\Plugins\Fileshare';
if ( class_exists( $sharePlugin ) ) {
$plugin = new $sharePlugin;
if ( $plugin->checkEnabled() ) {
$upload = Views::simpleView( 'chat.upload' );
}
}
Components::set( 'FILE_UPLOAD', $upload );
Components::set( 'CHAT', Views::simpleView( 'chat.chat' ) );
return Views::view( 'chat.index' );
}
public function sendMessage() {
Template::setTemplate( 'api' );
$out = [ 'response' => true ];
if ( !Forms::check( 'newChatMessage' ) ) {
$out = [ 'response' => false ];
echo json_encode($out);
return;
}
self::$chat->create(
Input::post( 'chatMessage' ),
);
echo json_encode($out);
return;
}
public function uploadFile() {
Template::setTemplate( 'api' );
$out = [ 'response' => true ];
if ( ! Forms::check( 'newFileUpload' ) ) {
$out = [ 'response' => false ];
echo json_encode($out);
return;
}
$sharePlugin = 'TheTempusProject\Plugins\Fileshare';
if ( ! class_exists( $sharePlugin ) ) {
$out = [ 'error' => 'Fileshare must be installed and enabled for this feature to work1.' ];
echo json_encode($out);
return;
}
$plugin = new $sharePlugin;
if ( ! $plugin->checkEnabled() ) {
$out = [ 'error' => 'Fileshare must be installed and enabled for this feature to work2.' ];
echo json_encode($out);
return;
}
$folder = UPLOAD_DIRECTORY . App::$activeUser->username . DIRECTORY_SEPARATOR;
if ( ! Upload::image( 'file', $folder ) ) {
$out = [ 'error' => 'could not upload image' ];
echo json_encode($out);
return;
}
$route = str_replace( APP_ROOT_DIRECTORY, '', $folder );
$location = $route . Upload::last();
$uploads = new UploadModel;
$result = $uploads->create( 'Chat Upload', $location );
if ( ! $result ) {
$out = [ 'error' => 'could not add upload to fileshare.' ];
echo json_encode( $out );
return;
}
self::$chat->create(
'Shared a file with the chat: ' .
'<a href="/' . $location . '" target="_blank">Upload</a>'
);
echo json_encode($out);
return;
}
public function getMessages() {
Template::setTemplate( 'api' );
echo Views::simpleView( 'chat.chat', self::$chat->recent( 50 ) );
return;
}
public function getMessageEvents() {
if ($_SERVER['HTTP_ACCEPT'] !== 'text/event-stream') {
Debug::info( 'connection refused, wrong HTTP_ACCEPT' );
exit();
}
header("X-Accel-Buffering: no");
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
// Ensure unlimited script execution time
set_time_limit(0);
// Disable output buffering
ini_set('output_buffering', 'off');
ini_set('zlib.output_compression', false);
if (Input::exists('lastId')) {
$lastId = Input::get('lastId');
} else {
$lastId = 0;
}
while ( true ) {
echo "id: 0\n";
echo "event: ping\n";
echo 'data: {"time": "' . time() . '"}';
echo "\n\n";
if ( connection_aborted() ) {
Debug::info( 'getMessageEvents connection aborted' );
break;
}
$newMessages = self::$chat->sinceMessage( $lastId );
if ( ! empty( $newMessages )) {
foreach ( $newMessages as $message ) {
$lastId = $message->ID;
echo "id: {$message->ID}\n";
echo "data: " . json_encode($message) . "\n\n";
}
}
// If there were any messages added, flush the output buffer
if (ob_get_contents()) {
ob_end_flush();
}
flush();
// sessions will block the end-user from sending messages unless we close the session
session_write_close();
sleep(1);
}
}
}

View File

@ -0,0 +1,116 @@
* {
margin: 0;
padding: 0;
}
body {
margin: 20px auto;
font-family: "Lato";
font-weight: 300;
}
form {
padding: 15px 25px;
display: flex;
gap: 10px;
justify-content: center;
}
form label {
font-size: 1.5rem;
font-weight: bold;
}
input {
font-family: "Lato";
}
a {
color: #0000ff;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
#chatMessages {
text-align: left;
margin: 0 auto;
padding: 10px;
height: 300px;
border: 1px solid #a7a7a7;
overflow: auto;
}
#usermsg {
flex: 1;
border-radius: 4px;
border: 1px solid #ff9800;
}
#name {
border-radius: 4px;
border: 1px solid #ff9800;
padding: 2px 8px;
}
#submitmsg,
#enter{
background: #ff9800;
border: 2px solid #e65100;
color: white;
padding: 4px 10px;
font-weight: bold;
border-radius: 4px;
}
.error {
color: #ff0000;
}
#menu {
padding: 15px 25px;
display: flex;
}
#menu p.welcome {
flex: 1;
}
a#exit {
color: white;
background: #c62828;
padding: 4px 8px;
border-radius: 4px;
font-weight: bold;
}
.msgln {
margin: 0 0 5px 0;
}
.span.left-info {
color: orangered;
}
span .chat-time {
color: #666;
font-size: 80%;
vertical-align: super;
width: 100px;
}
.msgln b.user-name, .msgln b.user-name-left {
font-weight: bold;
background: #546e7a;
color: white;
padding: 2px 4px;
font-size: 90%;
border-radius: 4px;
margin: 0 5px 0 0;
}
.msgln b.user-name-left {
background: orangered;
}

View File

@ -0,0 +1,52 @@
<?php
/**
* app/plugins/chat/forms.php
*
* This houses all of the form checking functions for this plugin.
*
* @package TP Chat
* @version 3.0
* @author Joey Kimsey <Joey@thetempusproject.com>
* @link https://TheTempusProject.com
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
*/
namespace TheTempusProject\Plugins\Chat;
use TheTempusProject\Bedrock\Functions\Input;
use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Classes\Forms;
class ChatForms extends Forms {
/**
* Adds these functions to the form list.
*/
public function __construct() {
self::addHandler( 'newChatMessage', __CLASS__, 'newChatMessage' );
self::addHandler( 'newFileUpload', __CLASS__, 'newFileUpload' );
}
/**
* Validates the new blog post form.
*
* @return {bool}
*/
public static function newChatMessage() {
if ( !Input::exists( 'chatMessage' ) ) {
self::addUserError( 'You must includes a message' );
return false;
}
// if (!self::token()) {
// return false;
// }
return true;
}
public static function newFileUpload() {
if ( !Input::exists( 'file' ) ) {
self::addUserError( 'You must includes a file' );
return false;
}
return true;
}
}
new ChatForms;

116
app/plugins/chat/js/chat.js Normal file
View File

@ -0,0 +1,116 @@
$(document).ready(function() {
var chatform = $('#sendChatMessage');
var uploadForm = $('#uploadFile');
chatform.bind('submit', function(event) {
event.preventDefault(); // Prevent page reload
var msg = $("#chatMessage").val();
console.log("Submitting message:", msg);
var ajax_params = {
url: '/chat/sendMessage',
type: 'POST',
data: { chatMessage: msg },
success: function(response) {
console.log("Message sent successfully:", response);
$("#chatMessage").val("");
},
error: function(xhr, status, error) {
console.error("Error sending message:", error, status, xhr);
},
complete: function() {
console.log("AJAX request complete");
}
};
$.ajax(ajax_params);
return false;
});
uploadForm.bind('submit', function(event) {
event.preventDefault(); // Prevent page reload
var formData = new FormData(this); // Create FormData object
$.ajax({
url: '/chat/uploadFile',
type: 'POST',
data: formData,
processData: false, // Don't process the files
contentType: false, // Set content type to false as jQuery will tell the server its a query string request
success: function(response) {
console.log("File uploaded successfully:", response);
$("#file").val("");
},
error: function(xhr, status, error) {
console.error("Error uploading file:", error, status, xhr);
},
complete: function() {
console.log("AJAX request complete");
}
});
return false;
});
const eventSource = new EventSource('/chat/getMessageEvents');
eventSource.onmessage = function( event ) {
const message = JSON.parse( event.data );
var userPopup = "<div class='media'>" +
"<span class='pull-left'>" +
"<img class='media-object avatar-round-40' src='/"+message.avatar+"' alt=''>" +
"</span>" +
"<div class='media-body'>" +
"<h5 class='media-heading'>" +
"<strong>"+message.submittedByName+"</strong>" +
"</h5>" +
"<a href='/home/profile/"+message.submittedByName+"' class='btn btn-sm btn-primary' role='button'><i class='glyphicon glyphicon-open'></i> View Profile</a>" +
"</div>" +
"</div>";
$("#chatMessages").append(
"<span class='chat-time'>" +
new Date(message.submittedAt * 1000).toLocaleString('en-US', { month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric', hour12: true }) +
// new Date(message.submittedAt * 1000).toLocaleString() +
"</span> " +
'<a tabindex="0" role="button" data-toggle="popover" data-html="true" data-trigger="focus" title="'+message.submittedByName+'" data-content="'+userPopup+'">' +
message.submittedByName +
'</a> ' +
message.chatMessage + "<br>"
);
$("[data-toggle=popover]").popover();
$("#chatMessages").scrollTop($("#chatMessages")[0].scrollHeight);
};
eventSource.onerror = function(event) {
console.error('EventSource failed:', event);
};
window.addEventListener('beforeunload', function() {
eventSource.close();
});
window.addEventListener('unload', function() {
eventSource.close();
});
// function getUserProfile( id ) {
// var ajax_params = {
// url: '/api/users/find/' + id,
// type: 'GET',
// success: function(response) {
// console.log("User retrieved:", response);
// $("#chatMessage").val("");
// },
// error: function(xhr, status, error) {
// console.error("Error retrieved user:", error, status, xhr);
// },
// complete: function() {
// console.log("AJAX request complete");
// }
// };
// $.ajax(ajax_params);
// }
});

View File

@ -0,0 +1,145 @@
<?php
/**
* app/plugins/chat/models/chat.php
*
* This class is used for the manipulation of the chat database table.
*
* @todo make this send a confirmation email
*
* @package TP Chat
* @version 3.0
* @author Joey Kimsey <Joey@thetempusproject.com>
* @link https://TheTempusProject.com
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
*/
namespace TheTempusProject\Models;
use TheTempusProject\Bedrock\Classes\Config;
use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Canary\Canary as Debug;
use TheTempusProject\Classes\DatabaseModel;
use TheTempusProject\TheTempusProject as App;
use TheTempusProject\Houdini\Classes\Filters;
class Chat extends DatabaseModel {
public $tableName = 'chat';
public $databaseMatrix = [
[ 'submittedAt', 'int', '10' ],
[ 'submittedBy', 'int', '11' ],
[ 'chatMessage', 'text', '' ],
];
/**
* The model constructor.
*/
public function __construct() {
parent::__construct();
}
/**
* Saves a chat form to the db.
*
* @param string $message -contents of the chat form.
* @return bool
*/
public function create( $message ) {
$fields = [
'submittedBy' => App::$activeUser->ID,
'submittedAt' => time(),
'chatMessage' => $message,
];
if ( !self::$db->insert( $this->tableName, $fields ) ) {
Debug::info( 'Chat::create - failed to insert to db' );
return false;
}
return self::$db->lastId();
}
public function filter( $data, $params = [] ) {
foreach ( $data as $instance ) {
if ( !is_object( $instance ) ) {
$instance = $data;
$end = true;
}
$instance->chatMessage = Filters::applyOne( 'mentions.0', $instance->chatMessage, true );
$instance->chatMessage = Filters::applyOne( 'hashtags.0', $instance->chatMessage, true );
$user = self::$user->findById( $instance->submittedBy );
if ( ! empty( $user ) ) {
$instance->submittedByName = $user->username;
$instance->profileUrl = '/home/profile/' . $user->username;
} else {
$instance->submittedByName = 'Unknown';
$instance->profileUrl = '#';
}
$instance->avatar = self::$user->getAvatar( $instance->submittedBy );
$out[] = $instance;
if ( !empty( $end ) ) {
$out = $out[0];
break;
}
}
return $out;
}
/**
* Function to clear chat from the DB.
*
* @todo is there a way i could check for success here I'm pretty sure this is just a bad idea?
* @return bool
*/
public function clear() {
if ( empty( self::$log ) ) {
self::$log = new Log;
}
self::$db->delete( $this->tableName, ['ID', '>=', '0'] );
self::$log->admin( 'Cleared Chat' );
Debug::info( 'Chat Cleared' );
return true;
}
public function recent( $limit = null ) {
if ( empty( $limit ) ) {
$postData = self::$db->get( $this->tableName, '*' );
} else {
$postData = self::$db->get( $this->tableName, '*', 'ID', 'ASC', [0, $limit] );
}
if ( !$postData->count() ) {
Debug::info( 'No messages found.' );
return false;
}
return $this->filter( $postData->results() );
}
public function sinceMessage($id = 0) {
if (empty($id)) {
$postData = self::$db->get($this->tableName, '*', 'ID', 'DESC', [0, 20]);
} else {
$postData = self::$db->get($this->tableName, ['ID', '>', $id], 'ID', 'ASC', [0, 20]);
}
if ( ! $postData->count() ) {
Debug::debug( 'No messages found.' );
return false;
}
if (empty($id)) {
$results = array_reverse($postData->results()); // Reverse the order to get ascending IDs
} else {
$results = $postData->results();
}
return $this->filter($results);
}
public function sinceMessageSingle( $id = 0 ) {
if ( empty( $id ) ) {
$postData = self::$db->get( $this->tableName, '*', 'ID', 'ASC', [0, 1] );
} else {
$postData = self::$db->get( $this->tableName, [ 'ID', '>', $id ], 'ID', 'ASC', [0, 1] );
}
if ( ! $postData->count() ) {
Debug::debug( 'No messages found.' );
return false;
}
return $this->filter( $postData->results() );
}
}

View File

@ -0,0 +1,55 @@
<?php
/**
* app/plugins/chat/plugin.php
*
* This houses all of the main plugin info and functionality.
*
* @package TP Chat
* @version 3.0
* @author Joey Kimsey <Joey@thetempusproject.com>
* @link https://TheTempusProject.com
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
*/
namespace TheTempusProject\Plugins;
use TheTempusProject\Classes\Plugin;
use TheTempusProject\TheTempusProject as App;
class Chat extends Plugin {
public $pluginName = 'TP Chat';
public $configName = 'chat';
public $pluginAuthor = 'JoeyK';
public $pluginWebsite = 'https://TheTempusProject.com';
public $modelVersion = '1.0';
public $pluginVersion = '3.0';
public $pluginDescription = 'A simple plugin which adds a site wide chat system.';
public $permissionMatrix = [
'chat' => [
'pretty' => 'Can use chat',
'default' => false,
],
];
public $admin_links = [
[
'text' => '<i class="fa fa-fw fa-copy"></i> Chat',
'url' => '{ROOT_URL}admin/chat',
],
];
public $main_links = [
[
'text' => 'Chat',
'url' => '{ROOT_URL}chat/index',
'filter' => 'loggedin',
],
];
public $configMatrix = [
'enabled' => [
'type' => 'radio',
'pretty' => 'Enable Chat.',
'default' => true,
],
];
public function __construct( $load = false ) {
parent::__construct( $load );
}
}

View File

@ -0,0 +1,32 @@
<?php
/**
* app/plugins/blog/templates/blog.inc.php
*
* This is the loader for the blog template.
*
* @package TP Blog
* @version 3.0
* @author Joey Kimsey <Joey@thetempusproject.com>
* @link https://TheTempusProject.com
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
*/
namespace TheTempusProject\Templates;
use TheTempusProject\Plugins\Calendar;
use TheTempusProject\Houdini\Classes\Components;
use TheTempusProject\Houdini\Classes\Navigation;
use TheTempusProject\Houdini\Classes\Views;
use TheTempusProject\Houdini\Classes\Template;
use TheTempusProject\Bedrock\Functions\Input;
class ChatLoader extends DefaultLoader {
/**
* This is the function used to generate any components that may be
* needed by this template.
*/
public function __construct() {
$this->addJs( '<script language="JavaScript" crossorigin="anonymous" type="text/javascript" src="{ROOT_URL}app/plugins/chat/js/chat.js"></script>' );
parent::__construct();
}
}

View File

@ -0,0 +1,88 @@
<!DOCTYPE html>
<html lang="en">
<!--
* app/templates/default/default.tpl
*
* @version 3.0
* @author Joey Kimsey <Joey@thetempusproject.com>
* @link https://TheTempusProject.com
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
-->
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta property="og:url" content="{CURRENT_URL}">
<meta name='twitter:card' content='summary_large_image' />
<title>{TITLE}</title>
<meta itemprop="name" content="{TITLE}">
<meta name="twitter:title" content="{TITLE}">
<meta property="og:title" content="{TITLE}">
<meta name="description" content="{PAGE_DESCRIPTION}">
<meta itemprop="description" content="{PAGE_DESCRIPTION}">
<meta name="twitter:description" content="{PAGE_DESCRIPTION}">
<meta property="og:description" content="{PAGE_DESCRIPTION}">
<meta itemprop="image" content="{META_IMAGE}">
<meta name="twitter:image" content="{META_IMAGE}">
<meta property="og:image" content="{META_IMAGE}">
<meta name="viewport" content="width=device-width, initial-scale=1">
{AUTHOR}
{ROBOT}
<link rel="icon" href="{ROOT_URL}images/favicon.ico">
<!-- Required CSS -->
<link rel="stylesheet" href="{FONT_AWESOME_URL}font-awesome.min.css" crossorigin="anonymous">
<link rel="stylesheet" href="{BOOTSTRAP_CDN}css/bootstrap-theme.min.css" crossorigin="anonymous">
<link rel="stylesheet" href="{BOOTSTRAP_CDN}css/bootstrap.min.css" crossorigin="anonymous">
<!-- Custom styles for this template -->
{TEMPLATE_CSS_INCLUDES}
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<!--Brand and toggle should get grouped for better mobile display but I had to account for additional menus-->
<div class="navbar-header">
<a href="{ROOT_URL}" class="navbar-brand">{SITENAME}</a>
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse" style="">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</div>
<div class="container-fluid">
<div class="collapse navbar-collapse navbar-ex1-collapse">
{topNavLeft}
<div class="navbar-right">
<ul class="nav navbar-nav">
{topNavRight}
</ul>
</div>
</div>
</div>
</nav>
<div class="container-fluid top-pad foot-pad">
{ISSUES}
<div class="container">
<div class="row">
{ERROR}
{NOTICE}
{SUCCESS}
{INFO}
</div>
</div>
{/ISSUES}
<div class="container">
<div class="row">
{CONTENT}
</div>
</div>
</div>
<footer>
{FOOT}
{COPY}
</footer>
<!-- Bootstrap core JavaScript and jquery -->
<script language="JavaScript" crossorigin="anonymous" type="text/javascript" src="{JQUERY_CDN}jquery.min.js"></script>
<script language="JavaScript" crossorigin="anonymous" type="text/javascript" src="{BOOTSTRAP_CDN}js/bootstrap.min.js"></script>
<!-- Custom javascript for this template -->
{TEMPLATE_JS_INCLUDES}
</body>
</html>

View File

View File

@ -0,0 +1,10 @@
{LOOP}
<div class='msgln'>
<span class='chat-time'>{DTC time}{submittedAt}{/DTC}</span> <b class='user-name'>{submittedByName}</b>: {chatMessage}<br>
</div>
{/LOOP}
{ALT}
<div class="msgln">
No messages Found.<br>
</div>
{/ALT}

View File

@ -0,0 +1,9 @@
<form class="form-horizontal" id="sendChatMessage">
<input type="hidden" name="token" value="{TOKEN}">
<div class="form-group col-lg-10">
<input type="text" class="form-control" name="chatMessage" id="chatMessage">
</div>
<div class="form-group col-lg-2">
<button id="submitChatMessage" name="submitChatMessage" value="button" class="btn btn-sm btn-primary center-block ">Send</button>
</div>
</form>

View File

@ -0,0 +1,18 @@
<link rel="stylesheet" href="{ROOT_URL}app/plugins/chat/css/chat.css" crossorigin="anonymous">
<span class="col-lg-12" id="Chat">
<div class="panel panel-default" role="tab">
<div class="panel-heading">
<button type="button" class="btn btn-default btn-sm" data-target="#Collapse${id}" data-toggle="collapse" aria-expanded="true" aria-controls="#Collapse${id}">
<i class="glyphicon glyphicon-th-list"></i> {USERNAME}
</button>
</div>
<div id="Collapse${id}" class="panel-collapse collapse in" style="width:100%; position: relative;" role="tabpanel" aria-expanded="true">
<div class="panel-body" id="chatMessages">
</div>
<div class="panel-footer">
{CREATE_MESSAGE}
{FILE_UPLOAD}
</div>
</div>
</div>
</span>

View File

@ -0,0 +1,9 @@
<form action="" method="post" class="form-horizontal" enctype="multipart/form-data" id="uploadFile">
<input type="hidden" name="token" value="{TOKEN}">
<div class="form-group col-lg-3 col-lg-offset-6">
<input type="file" class="form-control" name="file" id="file">
</div>
<div class="form-group col-lg-3">
<button id="submitUpload" name="submit" value="submit" class="btn btn-sm btn-primary center-block">Upload</button>
</div>
</form>