Change chat channel to node websocket
This commit is contained in:
parent
aeda77b924
commit
231687e081
12 changed files with 308 additions and 280 deletions
|
@ -1,12 +0,0 @@
|
||||||
"""
|
|
||||||
ASGI entrypoint. Configures Django and then runs the application
|
|
||||||
defined in the ASGI_APPLICATION setting.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import django
|
|
||||||
from channels.routing import get_default_application
|
|
||||||
|
|
||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dmoj.settings")
|
|
||||||
django.setup()
|
|
||||||
application = get_default_application()
|
|
|
@ -1,8 +0,0 @@
|
||||||
from django.urls import re_path
|
|
||||||
|
|
||||||
from . import consumers
|
|
||||||
|
|
||||||
ASGI_APPLICATION = "chat_box.routing.application"
|
|
||||||
websocket_urlpatterns = [
|
|
||||||
re_path(r'ws/chat/', consumers.ChatConsumer),
|
|
||||||
]
|
|
|
@ -1,26 +1,119 @@
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from django.views.generic import ListView
|
from django.views.generic import ListView
|
||||||
from django.http import HttpResponse, JsonResponse
|
from django.http import HttpResponse, JsonResponse, HttpResponseBadRequest
|
||||||
from django.core.paginator import Paginator
|
from django.core.paginator import Paginator
|
||||||
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.forms.models import model_to_dict
|
from django.forms.models import model_to_dict
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from judge import event_poster as event
|
||||||
from judge.jinja2.gravatar import gravatar
|
from judge.jinja2.gravatar import gravatar
|
||||||
from .models import Message, Profile
|
from .models import Message, Profile
|
||||||
import json
|
import json
|
||||||
|
|
||||||
def format_messages(messages):
|
|
||||||
msg_list = [{
|
class ChatView(ListView):
|
||||||
'time': msg.time,
|
context_object_name = 'message'
|
||||||
'author': msg.author,
|
template_name = 'chat/chat.html'
|
||||||
'body': msg.body,
|
title = _('Chat Box')
|
||||||
'image': gravatar(msg.author, 32),
|
paginate_by = 50
|
||||||
'id': msg.id,
|
messages = Message.objects.filter(hidden=False)
|
||||||
'css_class': msg.author.css_class,
|
paginator = Paginator(messages, paginate_by)
|
||||||
} for msg in messages]
|
|
||||||
return json.dumps(msg_list, default=str)
|
def get_queryset(self):
|
||||||
|
return self.messages
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
page = request.GET.get('page')
|
||||||
|
if page == None:
|
||||||
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
cur_page = self.paginator.get_page(page)
|
||||||
|
|
||||||
|
return render(request, 'chat/message_list.html', {
|
||||||
|
'object_list': cur_page.object_list,
|
||||||
|
})
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
context['title'] = self.title
|
||||||
|
context['last_msg'] = event.last()
|
||||||
|
context['online_users'] = get_user_online_status()
|
||||||
|
context['admin_status'] = get_admin_online_status()
|
||||||
|
context['today'] = timezone.now().strftime("%d-%m-%Y")
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
def delete_message(request):
|
||||||
|
ret = {'delete': 'done'}
|
||||||
|
|
||||||
|
if request.method == 'GET':
|
||||||
|
return JsonResponse(ret)
|
||||||
|
|
||||||
|
if request.user.is_staff:
|
||||||
|
try:
|
||||||
|
messid = int(request.POST.get('message'))
|
||||||
|
mess = Message.objects.get(id=messid)
|
||||||
|
except:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
mess.hidden = True
|
||||||
|
mess.save()
|
||||||
|
|
||||||
|
return JsonResponse(ret)
|
||||||
|
|
||||||
|
return JsonResponse(ret)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def post_message(request):
|
||||||
|
ret = {'msg': 'posted'}
|
||||||
|
|
||||||
|
if request.method == 'GET':
|
||||||
|
return JsonResponse(ret)
|
||||||
|
|
||||||
|
new_message = Message(author=request.profile,
|
||||||
|
body=request.POST['body'])
|
||||||
|
new_message.save()
|
||||||
|
|
||||||
|
event.post('chat', {
|
||||||
|
'type': 'new_message',
|
||||||
|
'message': new_message.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
return JsonResponse(ret)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def chat_message_ajax(request):
|
||||||
|
if request.method != 'GET':
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
try:
|
||||||
|
message_id = request.GET['message']
|
||||||
|
except KeyError:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
try:
|
||||||
|
message = Message.objects.filter(hidden=False).get(id=message_id)
|
||||||
|
except Message.DoesNotExist:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
return render(request, 'chat/message.html', {
|
||||||
|
'message': message,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_online_status():
|
||||||
|
last_five_minutes = timezone.now()-timezone.timedelta(minutes=5)
|
||||||
|
return Profile.objects \
|
||||||
|
.filter(display_rank='user',
|
||||||
|
last_access__gte = last_five_minutes)\
|
||||||
|
.order_by('-rating')
|
||||||
|
|
||||||
|
|
||||||
def get_admin_online_status():
|
def get_admin_online_status():
|
||||||
all_admin = Profile.objects.filter(display_rank='admin')
|
all_admin = Profile.objects.filter(display_rank='admin')
|
||||||
|
@ -35,63 +128,10 @@ def get_admin_online_status():
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
class ChatView(ListView):
|
|
||||||
model = Message
|
|
||||||
context_object_name = 'message'
|
|
||||||
template_name = 'chat/chat.html'
|
|
||||||
title = _('Chat Box')
|
|
||||||
paginate_by = 50
|
|
||||||
paginator = Paginator(Message.objects.filter(hidden=False), paginate_by)
|
|
||||||
|
|
||||||
def get_queryset(self):
|
@login_required
|
||||||
return Message.objects.filter(hidden=False)
|
def online_status_ajax(request):
|
||||||
|
return render(request, 'chat/online_status.html', {
|
||||||
def get(self, request, *args, **kwargs):
|
'online_users': get_user_online_status(),
|
||||||
page = request.GET.get('page')
|
'admin_status': get_admin_online_status(),
|
||||||
if (page == None):
|
})
|
||||||
# return render(request, 'chat/chat.html', {'message': format_messages(Message.objects.all())})
|
|
||||||
return super().get(request, *args, **kwargs)
|
|
||||||
|
|
||||||
cur_page = self.paginator.get_page(page)
|
|
||||||
return HttpResponse(format_messages(cur_page.object_list))
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
|
|
||||||
# hard code, should be fixed later
|
|
||||||
address = f'{self.request.get_host()}/ws/chat/'
|
|
||||||
if self.request.is_secure():
|
|
||||||
context['ws_address'] = f'wss://{address}'
|
|
||||||
else:
|
|
||||||
context['ws_address'] = f'ws://{address}'
|
|
||||||
|
|
||||||
context['title'] = self.title
|
|
||||||
last_five_minutes = timezone.now()-timezone.timedelta(minutes=5)
|
|
||||||
context['online_users'] = Profile.objects \
|
|
||||||
.filter(display_rank='user',
|
|
||||||
last_access__gte = last_five_minutes)\
|
|
||||||
.order_by('-rating')
|
|
||||||
context['admin_status'] = get_admin_online_status()
|
|
||||||
return context
|
|
||||||
|
|
||||||
def delete_message(request):
|
|
||||||
ret = {'delete': 'done'}
|
|
||||||
|
|
||||||
if request.method == 'GET':
|
|
||||||
return JsonResponse(ret)
|
|
||||||
|
|
||||||
if request.user.is_staff:
|
|
||||||
messid = int(request.POST.get('messid'))
|
|
||||||
all_mess = Message.objects.all()
|
|
||||||
|
|
||||||
for mess in all_mess:
|
|
||||||
if mess.id == messid:
|
|
||||||
mess.hidden = True
|
|
||||||
mess.save()
|
|
||||||
new_elt = {'time': mess.time, 'content': mess.body}
|
|
||||||
ret = new_elt
|
|
||||||
break
|
|
||||||
|
|
||||||
return JsonResponse(ret)
|
|
||||||
|
|
||||||
return JsonResponse(ret)
|
|
|
@ -1,12 +0,0 @@
|
||||||
from channels.auth import AuthMiddlewareStack
|
|
||||||
from channels.routing import ProtocolTypeRouter, URLRouter
|
|
||||||
import chat_box.routing
|
|
||||||
|
|
||||||
application = ProtocolTypeRouter({
|
|
||||||
# (http->django views is added by default)
|
|
||||||
'websocket': AuthMiddlewareStack(
|
|
||||||
URLRouter(
|
|
||||||
chat_box.routing.websocket_urlpatterns
|
|
||||||
)
|
|
||||||
),
|
|
||||||
})
|
|
|
@ -243,7 +243,6 @@ INSTALLED_APPS += (
|
||||||
'impersonate',
|
'impersonate',
|
||||||
'django_jinja',
|
'django_jinja',
|
||||||
'chat_box',
|
'chat_box',
|
||||||
'channels',
|
|
||||||
'newsletter',
|
'newsletter',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -513,17 +512,6 @@ FILE_UPLOAD_PERMISSIONS = 0o644
|
||||||
|
|
||||||
MESSAGES_TO_LOAD = 15
|
MESSAGES_TO_LOAD = 15
|
||||||
|
|
||||||
ASGI_APPLICATION = 'dmoj.routing.application'
|
|
||||||
CHANNEL_LAYERS = {
|
|
||||||
'default': {
|
|
||||||
'BACKEND': 'channels_redis.core.RedisChannelLayer',
|
|
||||||
'CONFIG': {
|
|
||||||
"hosts": [('0.0.0.0', 6379)],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
NEWSLETTER_CONFIRM_EMAIL = False
|
NEWSLETTER_CONFIRM_EMAIL = False
|
||||||
|
|
||||||
# Amount of seconds to wait between each email. Here 100ms is used.
|
# Amount of seconds to wait between each email. Here 100ms is used.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from chat_box.views import ChatView, delete_message
|
from chat_box.views import ChatView, delete_message, post_message, chat_message_ajax, online_status_ajax
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.conf.urls import include, url
|
from django.conf.urls import include, url
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
@ -372,8 +372,10 @@ urlpatterns = [
|
||||||
url(r'^$',
|
url(r'^$',
|
||||||
login_required(ChatView.as_view()),
|
login_required(ChatView.as_view()),
|
||||||
name='chat'),
|
name='chat'),
|
||||||
url(r'^delete/$', delete_message, name='delete_message')
|
url(r'^delete/$', delete_message, name='delete_chat_message'),
|
||||||
|
url(r'^post/$', post_message, name='post_chat_message'),
|
||||||
|
url(r'^ajax$', chat_message_ajax, name='chat_message_ajax'),
|
||||||
|
url(r'^online_status/ajax$', online_status_ajax, name='online_status_ajax')
|
||||||
])),
|
])),
|
||||||
|
|
||||||
url(r'^notifications/',
|
url(r'^notifications/',
|
||||||
|
|
|
@ -30,9 +30,6 @@ packaging
|
||||||
celery
|
celery
|
||||||
-e git://github.com/DMOJ/ansi2html.git#egg=ansi2html
|
-e git://github.com/DMOJ/ansi2html.git#egg=ansi2html
|
||||||
sqlparse
|
sqlparse
|
||||||
channels==2.4.0
|
|
||||||
channels-redis==2.4.2
|
|
||||||
docker
|
|
||||||
django-newsletter
|
django-newsletter
|
||||||
netaddr
|
netaddr
|
||||||
redis
|
redis
|
||||||
|
|
|
@ -51,11 +51,14 @@
|
||||||
.clear {
|
.clear {
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-message {
|
.content-message {
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
white-space: pre-line;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.content-message p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
#chat-area {
|
#chat-area {
|
||||||
height: 85vh;
|
height: 85vh;
|
||||||
|
|
|
@ -4,90 +4,30 @@
|
||||||
{% block title %} {{_('Chat Box')}} {% endblock %}
|
{% block title %} {{_('Chat Box')}} {% endblock %}
|
||||||
{% block js_media %}
|
{% block js_media %}
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
// change ws to wss if using HTTPS
|
|
||||||
var chatSocket = new WebSocket( "{{ws_address}}" );
|
|
||||||
</script>
|
|
||||||
<script type="text/javascript" src="{{ static('mathjax_config.js') }}"></script>
|
<script type="text/javascript" src="{{ static('mathjax_config.js') }}"></script>
|
||||||
<script type="text/javascript"
|
<script type="text/javascript" src="{{ static('event.js') }}"></script>
|
||||||
src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-AMS_HTML"></script>
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(function() {
|
window.currentPage = 1;
|
||||||
let currentPage = 1;
|
|
||||||
|
function load_page(page) {
|
||||||
|
$.get('?page=' + page)
|
||||||
|
.fail(function() {
|
||||||
|
console.log("Fail to load page " + page);
|
||||||
|
})
|
||||||
|
.done(function(data) {
|
||||||
|
setTimeout(function() {
|
||||||
|
let container = $('#chat-box');
|
||||||
|
let lastMsgPos = scrollTopOfBottom(container)
|
||||||
|
|
||||||
$('#loader').hide();
|
$('#loader').hide();
|
||||||
|
$('#chat-log').prepend(data);
|
||||||
|
remove_day_if_today();
|
||||||
|
|
||||||
chatSocket.onmessage = function(e) {
|
container.scrollTop(scrollTopOfBottom(container) - lastMsgPos);
|
||||||
let data = JSON.parse(e.data);
|
}, 500);
|
||||||
data = data['message'];
|
})
|
||||||
loadMessage(data['body'],
|
|
||||||
data['author'],
|
|
||||||
data['time'],
|
|
||||||
data['id'],
|
|
||||||
data['image'],
|
|
||||||
data['css_class'],
|
|
||||||
true);
|
|
||||||
// console.log(data);
|
|
||||||
$('#chat-box').scrollTop($('#chat-box')[0].scrollHeight);
|
|
||||||
};
|
|
||||||
|
|
||||||
function encodeHTML(content) {
|
|
||||||
return content.replace(/[\u00A0-\u9999<>\&]/gim, function(i) {
|
|
||||||
return '&#'+i.charCodeAt(0)+';';
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
const datesAreOnSameDay = (first, second) =>
|
|
||||||
first.getFullYear() === second.getFullYear() &&
|
|
||||||
first.getMonth() === second.getMonth() &&
|
|
||||||
first.getDate() === second.getDate();
|
|
||||||
|
|
||||||
function loadMessage(content, user, time, messid, image, css_class, isNew) {
|
|
||||||
// if (isNew) content = encodeHTML(content)
|
|
||||||
time = new Date(time);
|
|
||||||
if (datesAreOnSameDay(time, new Date())) {
|
|
||||||
time = moment(time).format("HH:mm");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
time = moment(time).format("HH:mm DD-MM-YYYY");
|
|
||||||
}
|
|
||||||
content = encodeHTML(content);
|
|
||||||
li = `<li class="message">
|
|
||||||
<img src="${image}" class="profile-pic">
|
|
||||||
<div class="body-message">
|
|
||||||
<div class="user-time">
|
|
||||||
<span class="${css_class}">
|
|
||||||
<a href="{{ url('user_page') }}/${user}">
|
|
||||||
${user}
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
<span class="time">${time}</span>
|
|
||||||
{% if request.user.is_staff %}
|
|
||||||
<a class="chatbtn_remove_mess" style="color:red; cursor: pointer;" data-messtime="${time}" data-author="${user}" data-messid="${messid}">Delete</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<span class="content-message">${content} </span>
|
|
||||||
</div>
|
|
||||||
<div class="clear"></div>
|
|
||||||
</li>`
|
|
||||||
ul = $('#chat-log')
|
|
||||||
if (isNew) {
|
|
||||||
ul.append(li)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ul.prepend(li)
|
|
||||||
}
|
|
||||||
MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
|
|
||||||
}
|
|
||||||
|
|
||||||
(function init_chatlog() {
|
|
||||||
ul = $('#chat-log')
|
|
||||||
{% autoescape on %}
|
|
||||||
{% for msg in message %}
|
|
||||||
loadMessage("{{msg.body|safe|escapejs}}", `{{msg.author}}`, `{{msg.time}}`, `{{msg.id}}`, `{{gravatar(msg.author, 32)}}`,`{{msg.author.css_class}}`);
|
|
||||||
{% endfor %}
|
|
||||||
{% endautoescape %}
|
|
||||||
$('#chat-box').scrollTop($('#chat-box')[0].scrollHeight);
|
|
||||||
})()
|
|
||||||
|
|
||||||
function scrollTopOfBottom(container) {
|
function scrollTopOfBottom(container) {
|
||||||
return container[0].scrollHeight - container.innerHeight()
|
return container[0].scrollHeight - container.innerHeight()
|
||||||
|
@ -99,40 +39,71 @@
|
||||||
if (currentPage < {{paginator.num_pages}}) {
|
if (currentPage < {{paginator.num_pages}}) {
|
||||||
currentPage++;
|
currentPage++;
|
||||||
loader.show();
|
loader.show();
|
||||||
$.ajax({
|
load_page(currentPage);
|
||||||
url: `{{request.path}}?page=${currentPage}`,
|
|
||||||
success: function(data) {
|
|
||||||
let lastMsg = $('.message:first')
|
|
||||||
let lastMsgPos = scrollTopOfBottom(container)
|
|
||||||
|
|
||||||
data = JSON.parse(data)
|
|
||||||
setTimeout( () => {
|
|
||||||
for (msg of data) {
|
|
||||||
loadMessage(msg.body, msg.author, msg.time, msg.id, msg.image, msg.css_class)
|
|
||||||
}
|
|
||||||
|
|
||||||
loader.hide()
|
|
||||||
|
|
||||||
// scroll to last msg
|
|
||||||
container.scrollTop(
|
|
||||||
scrollTopOfBottom(container) - lastMsgPos
|
|
||||||
)
|
|
||||||
}, 500)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
function remove_day_if_today() {
|
||||||
|
$('.message_date').each(function() {
|
||||||
|
sent_date = $(this).html()
|
||||||
|
console.log(sent_date);
|
||||||
|
if (sent_date === "{{today}}") {
|
||||||
|
$(this).hide();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
window.load_dynamic_update = function (last_msg) {
|
||||||
|
return new EventReceiver(
|
||||||
|
"{{ EVENT_DAEMON_LOCATION }}", "{{ EVENT_DAEMON_POLL_LOCATION }}",
|
||||||
|
['chat'], last_msg, function (message) {
|
||||||
|
switch (message.type) {
|
||||||
|
case 'new_message':
|
||||||
|
add_new_message(message.message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function add_new_message(message) {
|
||||||
|
// console.log(message);
|
||||||
|
$.get({
|
||||||
|
url: "{{ url('chat_message_ajax') }}",
|
||||||
|
data: {
|
||||||
|
message: message,
|
||||||
|
},
|
||||||
|
success: function (data) {
|
||||||
|
$('#chat-log').append($(data));
|
||||||
|
$('#chat-box').scrollTop($('#chat-box')[0].scrollHeight)
|
||||||
|
MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
|
||||||
|
},
|
||||||
|
error: function (data) {
|
||||||
|
if (data.status === 403)
|
||||||
|
console.log('No right to see: ' + message);
|
||||||
|
else {
|
||||||
|
console.log('Could not load chat message:');
|
||||||
|
console.log(data.responseText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
$('#loader').hide();
|
||||||
|
|
||||||
scrollContainer($('#chat-box'), $('#loader'))
|
scrollContainer($('#chat-box'), $('#loader'))
|
||||||
|
|
||||||
{% if request.user.is_staff %}
|
{% if request.user.is_staff %}
|
||||||
$(document).on("click", ".chatbtn_remove_mess", function() {
|
$(document).on("click", ".chatbtn_remove_mess", function() {
|
||||||
var elt = $(this);
|
var elt = $(this);
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: 'delete/',
|
url: "{{ url('delete_chat_message') }}",
|
||||||
type: 'post',
|
type: 'post',
|
||||||
data: elt.data(),
|
data: {
|
||||||
|
message: elt.attr('value'),
|
||||||
|
},
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
success: function(data){
|
success: function(data){
|
||||||
elt.closest('li').hide();
|
elt.closest('li').hide();
|
||||||
|
@ -147,24 +118,22 @@
|
||||||
$("#chat-submit").click(function() {
|
$("#chat-submit").click(function() {
|
||||||
if ($("#chat-input").val().trim()) {
|
if ($("#chat-input").val().trim()) {
|
||||||
let body = $('#chat-input').val().trim();
|
let body = $('#chat-input').val().trim();
|
||||||
let img = '{{ gravatar(request.user, 32) }}'
|
let img = '{{ gravatar(request.user, 32) }}';
|
||||||
|
|
||||||
message = {
|
message = {
|
||||||
'body': body,
|
body: body,
|
||||||
}
|
};
|
||||||
|
|
||||||
chatSocket.send(JSON.stringify({
|
|
||||||
'message': message
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
$.post("{{ url('post_chat_message') }}", message)
|
||||||
|
.fail(function(res) {
|
||||||
|
console.log('Fail to send message');
|
||||||
|
})
|
||||||
|
.done(function(res, status) {
|
||||||
$('#chat-input').val('').focus();
|
$('#chat-input').val('').focus();
|
||||||
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
chatSocket.onclose = function(e) {
|
|
||||||
console.error('Chat socket closed unexpectedly');
|
|
||||||
};
|
|
||||||
|
|
||||||
$("#chat-log").change(function() {
|
$("#chat-log").change(function() {
|
||||||
$('#chat-log').scrollTop($('#chat-log')[0].scrollHeight);
|
$('#chat-log').scrollTop($('#chat-log')[0].scrollHeight);
|
||||||
});
|
});
|
||||||
|
@ -196,9 +165,6 @@
|
||||||
return true
|
return true
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).ready(function () {
|
|
||||||
$('.chat-right-panel').hide();
|
$('.chat-right-panel').hide();
|
||||||
$('#chat-tab').find('a').click(function (e) {
|
$('#chat-tab').find('a').click(function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -214,17 +180,46 @@
|
||||||
$('.chat-left-panel').hide();
|
$('.chat-left-panel').hide();
|
||||||
$('.chat-right-panel').show();
|
$('.chat-right-panel').show();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#refresh-button').on('click', function() {
|
||||||
|
$.get("{{url('online_status_ajax')}}")
|
||||||
|
.fail(function() {
|
||||||
|
console.log("Fail to get online status");
|
||||||
|
})
|
||||||
|
.done(function(data) {
|
||||||
|
if (data.status == 403) {
|
||||||
|
console.log("Fail to retrieve data");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$('#chat-online-content').html(data);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
$('#chat-box').scrollTop($('#chat-box')[0].scrollHeight);
|
||||||
|
remove_day_if_today();
|
||||||
|
load_dynamic_update({{last_msg}});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% endblock js_media %}
|
{% endblock js_media %}
|
||||||
|
|
||||||
{% block media %}
|
{% block media %}
|
||||||
<style>
|
<style>
|
||||||
#content {
|
#content {
|
||||||
margin-top: -0.5em;
|
margin-top: -0.5em;
|
||||||
}
|
}
|
||||||
</style>
|
#refresh-button {
|
||||||
|
padding: 0 0.1em 0 0.1em;
|
||||||
|
margin: 0.1em;
|
||||||
|
border-radius: 0.1em;
|
||||||
|
background: goldenrod;
|
||||||
|
}
|
||||||
|
#refresh-button:hover {
|
||||||
|
background: lightgreen;
|
||||||
|
color: grey !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
{% endblock media %}
|
{% endblock media %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
@ -244,6 +239,7 @@
|
||||||
<div id="chat-box">
|
<div id="chat-box">
|
||||||
<img src="http://opengraphicdesign.com/wp-content/uploads/2009/01/loader64.gif" id="loader">
|
<img src="http://opengraphicdesign.com/wp-content/uploads/2009/01/loader64.gif" id="loader">
|
||||||
<ul id="chat-log">
|
<ul id="chat-log">
|
||||||
|
{% include 'chat/message_list.html' %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<textarea id="chat-input" placeholder="{{_('Enter your message')}}"></textarea>
|
<textarea id="chat-input" placeholder="{{_('Enter your message')}}"></textarea>
|
||||||
|
@ -252,33 +248,10 @@
|
||||||
<div id="chat-online" class="chat-right-panel sidebox">
|
<div id="chat-online" class="chat-right-panel sidebox">
|
||||||
<h3>
|
<h3>
|
||||||
{{_('Online Users')}}
|
{{_('Online Users')}}
|
||||||
<i class="fa fa-wifi"></i>
|
<button id="refresh-button" class="fa fa-rotate-right" title="{{_('Refresh')}}"></button>
|
||||||
</h3>
|
</h3>
|
||||||
<ul id="chat-online-content">
|
<ul id="chat-online-content">
|
||||||
<h4>{{_('Admins')}}: </h4>
|
{% include "chat/online_status.html" %}
|
||||||
<hr/>
|
|
||||||
{% for user in admin_status %}
|
|
||||||
<li style="padding-left: 1em">
|
|
||||||
{% if user.is_online %}
|
|
||||||
<span class="green-dot"></span>
|
|
||||||
{% else %}
|
|
||||||
<span class="red-dot"></span>
|
|
||||||
{% endif %}
|
|
||||||
<span style="padding-left:0.25em">
|
|
||||||
{{ link_user(user.user) }}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
<h4 style="margin-top:1em;">{{_('Users')}}: </h4>
|
|
||||||
<hr/>
|
|
||||||
{% for user in online_users %}
|
|
||||||
<li style="padding-left: 1em">
|
|
||||||
<span class="green-dot"></span>
|
|
||||||
<span style="padding-left:0.25em">
|
|
||||||
{{ link_user(user.user) }}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
25
templates/chat/message.html
Normal file
25
templates/chat/message.html
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<li class="message" id="message-{{ message.id }}">
|
||||||
|
<img src="{{ gravatar(message.author, 32) }}" class="profile-pic">
|
||||||
|
<div class="body-message">
|
||||||
|
<div class="user-time">
|
||||||
|
<span class="username {{ message.author.css_class }}">
|
||||||
|
<a href="{{ url('user_page', message.author.user.username) }}">
|
||||||
|
{{message.author}}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<span class="time">
|
||||||
|
{{ message.time|date('TIME_FORMAT') }}
|
||||||
|
<span class="message_date">{{ message.time|date('d-m-Y') }}</span>
|
||||||
|
</span>
|
||||||
|
{% if request.user.is_staff %}
|
||||||
|
<a class="chatbtn_remove_mess" value="{{message.id}}" style="color:red; cursor: pointer;">
|
||||||
|
Delete
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<span class="content-message">
|
||||||
|
{{message.body | markdown('comment', MATH_ENGINE)|reference|str|safe }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="clear"></div>
|
||||||
|
</li>
|
8
templates/chat/message_list.html
Normal file
8
templates/chat/message_list.html
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{% for message in object_list | reverse%}
|
||||||
|
{% include "chat/message.html" %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% if REQUIRE_JAX %}
|
||||||
|
{% include "mathjax-load.html" %}
|
||||||
|
{% endif %}
|
||||||
|
{% include "comments/math.html" %}
|
24
templates/chat/online_status.html
Normal file
24
templates/chat/online_status.html
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<h4>{{_('Admins')}}: </h4>
|
||||||
|
<hr/>
|
||||||
|
{% for user in admin_status %}
|
||||||
|
<li style="padding-left: 1em">
|
||||||
|
{% if user.is_online %}
|
||||||
|
<span class="green-dot"></span>
|
||||||
|
{% else %}
|
||||||
|
<span class="red-dot"></span>
|
||||||
|
{% endif %}
|
||||||
|
<span style="padding-left:0.25em">
|
||||||
|
{{ link_user(user.user) }}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
<h4 style="margin-top:1em;">{{_('Users')}}: </h4>
|
||||||
|
<hr/>
|
||||||
|
{% for user in online_users %}
|
||||||
|
<li style="padding-left: 1em">
|
||||||
|
<span class="green-dot"></span>
|
||||||
|
<span style="padding-left:0.25em">
|
||||||
|
{{ link_user(user.user) }}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
Loading…
Reference in a new issue