Richtig geiler shit

This commit is contained in:
Holger Trampe 2020-05-19 17:44:41 +02:00
parent 1c26681068
commit 233206e77e
27 changed files with 906 additions and 55 deletions

4
.gitignore vendored
View File

@ -37,6 +37,10 @@ cloud/migrations/*
!cloud/migrations/__init__.py
cloud/__pycache__/*
chat/migrations/*
!chat/migrations/__init__.py
chat/__pycache__/*
notificsys/migrations/*
!notificsys/migrations/__init__.py
notificsys/__pycache__/*

View File

@ -7,6 +7,8 @@ from rest_framework import serializers
from .serializers import StandardsSerializer
from rest_framework.decorators import api_view, permission_classes
from rest_framework import status
from rest_framework.authentication import SessionAuthentication, BasicAuthentication, TokenAuthentication
from rest_framework.decorators import authentication_classes
class HelloView(APIView):
permission_classes = (IsAuthenticated,) # <-- And here

0
chat/__init__.py Normal file
View File

3
chat/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

5
chat/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class ChatConfig(AppConfig):
name = 'chat'

View File

56
chat/models.py Normal file
View File

@ -0,0 +1,56 @@
from django.db import models
from django.contrib.auth.models import User
from users.models import Agency
from django.urls import reverse
from django.utils import timezone
'''
MODEL ChatMessage
'''
class ChatMessage(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.CharField(max_length=5000, blank=False, default="")
sendtime = models.DateTimeField(default=timezone.now, blank=True)
room = models.ForeignKey("ChatRoom", on_delete=models.CASCADE)
'''
Model ChatRoom
'''
class ChatRoom(models.Model):
creator = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
'''
chatroomtype
0 - User-User-Chat
1 - Group-Chat
'''
chatroomtype = models.IntegerField(default=0)
roomname = models.CharField(max_length=200, blank=False, default="")
# This field is for random-String Django Channels
roomname_channel = models.CharField(max_length=200, blank=False)
chatmembers = models.ManyToManyField(User, blank=True, related_name='users_in_chatroom')
chatroom_createddate = models.DateTimeField(blank=True)
chatmember_single = models.ForeignKey(User, related_name='singleuserchat', on_delete=models.CASCADE, null=True, blank=True)
'''
VIEWSTATUS
0 - Hide and Close
1 - Hide, but visible in base.html
2 - Full visible
'''
viewstatus = models.IntegerField(default=True)
messages = models.ManyToManyField("ChatMessage", blank=True, related_name='all_chatmessages')
def __str__(self):
return f'{self.roomname}'

View File

@ -0,0 +1,47 @@
<style type="text/css">
.roundimg {
border-radius: 50%;
z-index: 999;
height: 100%;
width: 100%;
border-radius: 50%;
}
.icon-container {
width: 50px;
height: 50px;
position: relative;
}
.status-circle {
width: 15px;
height: 15px;
border-radius: 50%;
bottom: 0;
right: 0;
position: absolute;
}
</style>
<div class="card col-2" style="position: fixed; right: 35px; bottom: 75px;">
<div class="card-body">
<h4>Chat starten
<button class="btn btn-sm btn-secondary" style="float: right;" onclick="javascript:$('#chat_alluserscontent').fadeOut()"><small><i class="fas fa-times"></i></small></button>
</h4>
<hr>
{% for user in usersofagency %}
<span>
<div class='icon-container'>
<img class="img-profile roundimg" src="{{ user.profile.get_photo_url }}">
<div class='status-circle' style="background-color: {% if user in onlineusers %} green {% else %} grey {% endif %};">
</div>
</div>
{{user.first_name}} {{user.last_name}}
<button class="btn btn-sm btn-secondary" style="float: right;"><small><i class="far fa-comment"></i></small></button>
</span>
<br />
{% endfor %}
</div>
</div>
<div class="chat-popup">

View File

@ -0,0 +1,215 @@
{% if roomdata.creator == user %}
<h4 id="chattitle">Gespräch mit {{roomdata.chatmember_single.first_name}} {{roomdata.chatmember_single.last_name}}</h4>
{% else %}
<h4 id="chattitle">Gespräch mit {{roomdata.creator.first_name}} {{roomdata.creator.last_name}}</h4>
{% endif %}
<hr>
<div class="card" >
<div class="card-body scroll" id="chatcontentcomplete">
<div id="roomstart" style="min-width: 100%; text-align: center;">
<small>Unterhaltung gestartet am {{roomdata.chatroom_createddate}}</small>
</div>
<hr>
<div id="chatmessages">
{% for message in roomdata.messages.all %}
{% if message.author == request.user %}
<div style="" class="chatmessageele_me col-7 mb-3 ">
<div class='icon-container ml-2 mt-1 ' style="float: right;">
<img class="img-profile roundimg" src="{{ message.author.profile.get_photo_url }}">
</div>
<h6 class="mt-3"><small>{{message.author.first_name}} {{message.author.last_name}},&nbsp;{{message.sendtime}}</small></h6>
<div style="text-align: left;" class="mt-1">
<span style="float: right !important; font-size: 1.0em;">
{{message.content}}
</span>
</div>
</div>
{% else %}
<div style="" class="chatmessageele_other col-7 mb-3">
<div class='icon-container mr-2 mt-1 ' style="float: left;">
<img class="img-profile roundimg" src="{{ message.author.profile.get_photo_url }}">
</div>
<h6 class="mt-3"><small>{{message.author.first_name}} {{message.author.last_name}},&nbsp;{{message.sendtime}}</small></h6>
<div style="text-align: left;" class="mt-1">
<span style="float: left !important; font-size: 1.0em;">
{{message.content}}
</span>
</div>
</div>
{% endif %}
{% endfor %}
<!-- SINGLE MESSAGE -->
<!--
<div style="" class="chatmessageele_me col-7 mb-3 ">
<div class='icon-container ml-2 mt-1 ' style="float: right;">
<img class="img-profile roundimg" src="{{ user.profile.get_photo_url }}">
</div>
<h6 class="mt-3"><small>{{user.first_name}} {{user.last_name}},&nbsp;13:45 18.05.2020</small></h6>
<div style="text-align: left;" class="mt-1">
<span style="float: right !important; font-size: 1.0em;">
Ich finde schon, dass wir hier langsam mal mit den Daten arbeiten sollten...
</span>
</div>
</div>
<div style="" class="chatmessageele_other col-7 mb-3 ">
<div class='icon-container mr-2 mt-1 ' style="float: left;">
<img class="img-profile roundimg" src="{{ user.profile.get_photo_url }}">
</div>
<h6 class="mt-3"><small>{{user.first_name}} {{user.last_name}},&nbsp;13:45 18.05.2020</small></h6>
<div style="text-align: left;" class="mt-1">
<span style="float: left !important; font-size: 1.0em;">
Ich finde schon, dass wir hier langsam mal mit den Daten arbeiten sollten...
</span>
</div>
</div>
-->
</div><!-- END CHAT MESSAGES -->
<span id="scrolltarget">&nbsp;</span>
</div><!-- END CARD BODY-->
<div id="is_typing" class="ml-2">
<div class="spinner-grow spinner-border-sm" id="typingspinner" role="status" style="display: none;">
<span class="sr-only">Jemand tippt...</span>
</div>
<small id="is_typing_name" class="">&nbsp;
</small>
</div>
<div class="card-footer bg-transparent border-success">
<div class="input-group">
<input type="text" class="form-control" placeholder="Neue Nachricht" id="message" aria-describedby="">
<div class="input-group-append">
<button class="btn btn-primary" id="sendNewMessgeButton" onclick="javascript:sendNewMessage()" type="button"><i class="fas fa-location-arrow"></i></button>
</div>
</div>
</div>
</div>
<script type="text/javascript">
function scrollDown(){
//$('#chatmessages').animate({scrollTop: $('#chatmessages').prop("scrollHeight")}, 500);
}
$(document).ready(function(){
$('#chatcontentcomplete').scrollTop( $('#chatcontentcomplete').height()*200 )
});
userownid = "{{user.pk}}";
creator_id = {{roomdata.creator.pk}};
chatmember_id = {{roomdata.chatmember_single.pk}};
ws_string = 'wss://'
if (location.protocol !== 'https:') {
ws_string = 'ws://'
}
if(typeof chatwebsocket != "undefined"){
chatwebsocket = new WebSocket(ws_string+window.location.host+"/chat/{{roomdata.creator.pk}}/{{roomdata.chatmember_single.pk}}/")
}
else{
chatwebsocket = new WebSocket(ws_string+window.location.host+"/chat/{{roomdata.creator.pk}}/{{roomdata.chatmember_single.pk}}/")
}
chatwebsocket.onmessage = function(e) {
datainfo = e["data"].split("__");
if(datainfo[0] == "starttyping")
{
typingname = (datainfo[1].split("_"))[1];
typingid = (datainfo[1].split("_"))[0];
if(typingid != userownid){
$("#is_typing_name").html("" + typingname);
$("#typingspinner").show();
}
}
if(datainfo[0] == "stoptyping")
{
$("#is_typing_name").html("&nbsp;");
$("#typingspinner").hide();
}
if(datainfo[0] == "reloadmessages")
{
$.ajax(
{
type: "GET",
url: "{% url 'chat:chat-ajax' %}",
data : {
action : "loadnewestmessage",
room : {{roomdata.pk}}
},
success: function( data )
{
$("#chatmessages").append(data);
$('#chatcontentcomplete').scrollTop( $('#chatcontentcomplete').height()*200 );
}
});
}
};
chatwebsocket.onclose = function(e) {
console.error('Chat socket closed unexpectedly');
};
$("#message").keyup(function(){
if($("#message").val().length > 0){
chatwebsocket.send("starttyping__{{user.pk}}__privatechat_{{roomdata.creator.pk}}_{{roomdata.chatmember_single.pk}}");
}
else{
chatwebsocket.send("stoptyping__{{user.pk}}__privatechat_{{roomdata.creator.pk}}_{{roomdata.chatmember_single.pk}}");
}
})
function sendNewMessage(){
$.ajax(
{
type: "GET",
url: "{% url 'chat:chat-ajax' %}",
data : {
action : "addnewmessage",
new_chat_userid : userownid,
message : $("#message").val(),
room : {{roomdata.pk}}
},
success: function( data )
{
$("#message").val("");
chatwebsocket.send("load__{{user.pk}}__privatechat_{{roomdata.creator.pk}}_{{roomdata.chatmember_single.pk}}");
chatwebsocket.send("stoptyping__{{user.pk}}__privatechat_{{roomdata.creator.pk}}_{{roomdata.chatmember_single.pk}}");
}
});
}
$(document).on('keypress',function(e) {
if(e.which == 13) {
if($("#message").val().length > 0){
sendNewMessage();
}
}
});
</script>

View File

@ -0,0 +1,11 @@
<div style="" class="chatmessageele_other col-7 mb-3 ">
<div class='icon-container mr-2 mt-1 ' style="float: left;">
<img class="img-profile roundimg" src="{{ newmessage.author.profile.get_photo_url }}">
</div>
<h6 class="mt-3"><small>{{newmessage.author.first_name}} {{newmessage.author.last_name}},&nbsp;{{newmessage.sendtime}}</small></h6>
<div style="text-align: left;" class="mt-1">
<span style="float: left !important; font-size: 1.0em;">
{{newmessage.content}}
</span>
</div>
</div>

View File

@ -0,0 +1,11 @@
<div style="" class="chatmessageele_me col-7 mb-3 ">
<div class='icon-container ml-2 mt-1 ' style="float: right;">
<img class="img-profile roundimg" src="{{ newmessage.author.profile.get_photo_url }}">
</div>
<h6 class="mt-3"><small>{{newmessage.author.first_name}} {{newmessage.author.last_name}},&nbsp;{{newmessage.sendtime}}</small></h6>
<div style="text-align: left;" class="mt-1">
<span style="float: right !important; font-size: 1.0em;">
{{newmessage.content}}
</span>
</div>
</div>

View File

@ -0,0 +1,152 @@
{% extends "users/base.html" %}
{% block content %}
{% if request.user.profile.agency.module_chat %}
<style type="text/css">
.roundimg {
border-radius: 50%;
z-index: 999;
height: 100%;
width: 100%;
border-radius: 50%;
}
.icon-container {
width: 50px;
height: 50px;
position: relative;
float: left;
}
.status-circle {
width: 15px;
height: 15px;
border-radius: 50%;
bottom: 0;
right: 0;
position: absolute;
}
.chatmessageele_me
{
padding: 5px;
border-radius: 15px;
background-color: #f8f9fc;
float: right;
text-align: right;
}
.chatmessageele_other
{
padding: 5px;
border-radius: 15px;
background-color: #858796;
float: left;
text-align: left;
color: #ffffff;
}
.scroll {
max-height: 600px;
min-height: 600px;
overflow-y: auto;
}
</style>
<div class="content-section col-12">
<h3>Chat&nbsp;<small><i data-toggle="tooltip" data-placement="top" title="Verwalten Sie hier Ihre Chatverläufe und starten Sie neue Unterhaltungen mit Mitarbeitern und anderen Agenturen." class="far fa-question-circle"></i></small>
</h3>
<hr>
</div>
<div class="row col">
<div class="col-3">
<h4>Mitarbeiter und Räume</h4>
<hr>
{% for user in usersofagency %}
<div class="card mb-2 hoverchatcard" id="userchat_{{user.pk}}">
<div class="card-body">
<div style="float: left;" class="col-12 ">
<div class='icon-container mr-2'>
<img class="img-profile roundimg" src="{{ user.profile.get_photo_url }}">
<div class='status-circle' id="userstatus_circle_{{user.pk}}" style="background-color: {% if user in onlineusers %} green {% else %} grey {% endif %};">
</div>
</div>
<h5 class="mt-3">{{user.first_name}} {{user.last_name}}</h5>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="col-9" style="" id="mainchatcontent">
</div><!-- END CHATAREA -->
</div>
<script type="text/javascript">
creator_id = false;
chatmember_id = false;
$(".hoverchatcard").hover(function(){
var $this = $(this);
$this.data('bgcolor', $this.css('background-color')).css('background-color', '#f8f9fc');
}, function(){
var $this = $(this);
$this.data('bgcolor', $this.css('background-color')).css('background-color', '#FFFFFF');
}
)
$(".hoverchatcard").click(function(){
$.ajax(
{
type: "GET",
url: "{% url 'chat:chat-ajax' %}",
data : {
action : "startnewchat_user_user",
new_chat_userid : $(this)[0]["id"].split("_")[1]
},
success: function( data )
{
if(creator_id != false && chatmember_id != false){
chatwebsocket.close();
}
$("#mainchatcontent").html(data);
}
});
});
function updatePresenceLive(e) {
$.ajax(
{
type: "GET",
url: "{% url 'chat:chtaajax-getloggedusers-data' %}",
data : {
action : "getloggedusers"
},
success: function( data )
{
console.log(data);
$( ".status-circle" ).each(function( index ) {
if(data["onlineusers"].indexOf($(this)[0]["id"].split("_")[2]) !== -1){
$("#" + $(this)[0]["id"]).css("background-color", "green");
}
else{
$("#" + $(this)[0]["id"]).css("background-color", "grey");
}
});
}
});
};
</script>
{% else %}
<h3>Das Module Chat wurde in ihrer Agentur deaktiviert.</h3>
{% endif %}
{% endblock content %}

3
chat/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

11
chat/urls.py Normal file
View File

@ -0,0 +1,11 @@
from django.urls import path
from . import views
app_name = 'chat'
urlpatterns = [
path('managemenet/', views.chatmanagement, name='chat-management'),
path('ajaxchat', views.chatajaxmain, name="chat-ajax"),
path('ajaxchat/getloggedusers', views.getloggedusers, name="chtaajax-getloggedusers"),
path('ajaxchat/getloggedusersdata', views.getloggedusersdata, name="chtaajax-getloggedusers-data")
]

115
chat/views.py Normal file
View File

@ -0,0 +1,115 @@
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from channels_presence.models import Presence
from django.http import HttpResponseRedirect,HttpResponse, JsonResponse
from django.contrib.auth.models import User
from channels_presence.models import Room
from channels_presence.models import Presence
import channels.layers
from django.utils import timezone
from .models import ChatRoom, ChatMessage
# Create your views here.
@login_required
def chatmanagement(request):
users_online = Room.objects.get(channel_name="agency_" + str(request.user.profile.agency.pk))
context = {
'active_link' : 'chat',
"usersofagency" : User.objects.filter(profile__agency=request.user.profile.agency).exclude(pk=request.user.pk).order_by("last_name"),
"onlineusers" : users_online.get_users()
}
return render(request, 'chat/chatmanagement.html', context)
@login_required
def getloggedusers(request):
if request.method == "GET":
users_online = Room.objects.get(channel_name="agency_" + str(request.user.profile.agency.pk))
context = {
"usersofagency" : User.objects.filter(profile__agency=request.user.profile.agency).exclude(pk=request.user.pk).oder_by("last_name"),
"onlineusers" : users_online.get_users()
}
return render(request, "chat/chat_allusers.html", context)
else:
return JsonResponse({})
@login_required
def getloggedusersdata(request):
if request.method == "GET":
users_online = Room.objects.get(channel_name="agency_" + str(request.user.profile.agency.pk)).get_users()
users_agency = User.objects.filter(profile__agency=request.user.profile.agency).exclude(pk=request.user.pk)
user_online_final = []
for u in users_agency:
if(u in users_online):
user_online_final.append("" + str(u.pk))
return JsonResponse({"onlineusers" : user_online_final})
else:
return JsonResponse({})
@login_required
def chatajaxmain(request):
if request.method == "GET":
context = {}
choosenroom = ""
if request.GET["action"] == "startnewchat_user_user":
singleuserid = request.GET["new_chat_userid"]
getroom = ChatRoom.objects.filter(chatmember_single__pk=singleuserid, creator=request.user) | ChatRoom.objects.filter(creator__pk=singleuserid, chatmember_single=request.user)
singleuser = User.objects.get(pk=singleuserid)
# NO PRIVATE CHAT THERE, CREATE ONE!
if(len(getroom) == 0):
newchatroom = ChatRoom(creator=request.user, chatroomtype=0, roomname="Gespräch mit " + singleuser.first_name + " " + singleuser.last_name, roomname_channel="privatechat_" + str(request.user.pk) + "_" + singleuserid, chatmember_single=singleuser, chatroom_createddate=timezone.now(), viewstatus=0)
newchatroom.save()
context = {
"roomdata" : newchatroom
}
else:
context = {
"roomdata" : list(getroom)[0]
}
return render(request, "chat/chat_content.html", context)
elif request.GET["action"] == "addnewmessage":
room = ChatRoom.objects.get(pk=request.GET["room"])
if(request.user == room.creator or request.user == room.chatmember_single):
newmessage = ChatMessage(room=room, author=request.user, content=request.GET["message"])
newmessage.save()
room.messages.add(newmessage)
room.save()
return render(request, "chat/chat_ownmessage.html", {"newmessage" : newmessage})
else:
JsonResponse({"status" : "Error on CHATAJAXMAIN"})
elif request.GET["action"] == "loadnewestmessage":
room = ChatRoom.objects.get(pk=request.GET["room"])
last_message = list(room.messages.order_by('-sendtime'))[0]
if(last_message.author == request.user):
return render(request, "chat/chat_ownmessage.html", {"newmessage" : last_message})
else:
return render(request, "chat/chat_othermessage.html", {"newmessage" : last_message})
else:
return JsonResponse({"status" : "Error on CHATAJAXMAIN"})
'''
author = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.CharField(max_length=5000, blank=False, default="")
sendtime = models.DateField(default=timezone.now, blank=True)
room = models.ForeignKey("ChatRoom", on_delete=models.CASCADE)
'''

View File

@ -10,7 +10,7 @@ For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.2/ref/settings/
"""
import os
from datetime import datetime, timedelta
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@ -49,6 +49,8 @@ REDIS_URL = ("localhost", 6379)
# Nach zehn Stunden läuft der Cookie ab!
SESSION_COOKIE_AGE = 8*60*60
CHANNELS_PRESENCE_MAX_AGE = 30
# FOR SUMMERNOTE ORIGIN
X_FRAME_OPTIONS = 'SAMEORIGIN'
@ -67,6 +69,7 @@ INSTALLED_APPS = [
'dasettings.apps.DASettingsConfig',
'areas.apps.AreasConfig',
'orga.apps.OrgaConfig',
'chat.apps.ChatConfig',
'message.apps.MessageConfig',
'cloud.apps.CloudConfig',
'tasks.apps.TasksConfig',
@ -90,7 +93,8 @@ INSTALLED_APPS = [
'django_user_agents',
'rest_framework',
'rest_framework.authtoken',
'channels'
'channels',
'channels_presence',
]
MIDDLEWARE = [
@ -125,7 +129,6 @@ TEMPLATES = [
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',

View File

@ -7,21 +7,7 @@ from users.views import AgencyCreateView, registerNewAgency
from . import views
from django.contrib.auth.decorators import login_required
from rest_framework.authtoken.views import obtain_auth_token
'''
Main URLS
Apps:
areas
tasks
standards
orga
news
quicklinkgs
-> Rest ist von Django
'''
urlpatterns = [
path('login/', auth_views.LoginView.as_view(template_name='users/login.html'), name='login'),
path('', include('users.urls'), name="dashboard-first"),
@ -46,6 +32,7 @@ urlpatterns = [
path('notifications/', include('notificsys.urls'), name="notifications"),
path('tm/', include('timemanagement.urls'), name="timemanagement"),
path('api/', include('api.urls', namespace='api')),
path('chat/', include('chat.urls'), name='chat'),
path('api-token-auth/', obtain_auth_token, name='api-token-auth'),
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
if settings.DEBUG:

View File

@ -1,6 +1,11 @@
import json
from channels.generic.websocket import WebsocketConsumer
from asgiref.sync import async_to_sync
from channels_presence.models import Room
from channels_presence.decorators import touch_presence, remove_presence
from channels_presence.models import Presence
import channels
from django.contrib.auth.models import User
class UsersConsumer(WebsocketConsumer):
@ -12,17 +17,17 @@ class UsersConsumer(WebsocketConsumer):
auch nur Clients innerhalb der Agentur treffen!
'''
def connect(self):
loggeduser = self.scope["user"]
async_to_sync(self.channel_layer.group_add)(
"agency_" + str(loggeduser.profile.agency.pk),
self.channel_name
)
self.accept()
super().connect()
loggeduser = self.scope["user"]
Presence.objects.touch(self.channel_name)
Room.objects.add("agency_" + str(loggeduser.profile.agency.pk), self.channel_name, self.scope["user"])
def disconnect(self, close_code):
pass
loggeduser = self.scope["user"]
Room.objects.remove("agency_" + str(loggeduser.profile.agency.pk), self.channel_name)
Presence.objects.touch(self.channel_name)
'''
def receive(self, text_data):
@ -36,6 +41,9 @@ class UsersConsumer(WebsocketConsumer):
)
'''
# WEBSOCKET-DATA-CONTENT
def receive(self, text_data=None, bytes_data=None):
if text_data == '"heartbeat"':
Presence.objects.touch(self.channel_name)
# UPDATET STANDARD
def update_standard(self, event):
@ -43,5 +51,54 @@ class UsersConsumer(WebsocketConsumer):
# NEW AGENCY NEWS
def agency_newnews(self, event):
print(event)
self.send("agency_newnews")
self.send("Neue Agenturnews!")
# SOMETHING IN PRESENCE CHANGED
def update_presence_live(self, event):
self.send("presence_update")
class UsersChat(UsersConsumer):
'''
CONNECT A WEBSOCKET
Die Clients werden in Channel-Layer pro Agentur gepackt, damit gesendete Websocket-Nachrichten
auch nur Clients innerhalb der Agentur treffen!
'''
def connect(self):
super().connect()
loggeduser = self.scope["user"]
roomname = "privatechat_" + str(self.scope["url_route"]["kwargs"]["creator"]) + "_" + str(self.scope["url_route"]["kwargs"]["single"])
Room.objects.add(roomname, self.channel_name, self.scope["user"])
def disconnect(self, close_code):
Room.objects.remove("", self.channel_name)
# WEBSOCKET-DATA-CONTENT
def receive(self, text_data=None, bytes_data=None):
datainfo = text_data.split("__")
typinguserid = datainfo[1]
if datainfo[0] == 'starttyping':
channel_layer = channels.layers.get_channel_layer()
async_to_sync(channel_layer.group_send)(datainfo[2], {'type' : 'start_typing', 'typingname' : typinguserid})
elif datainfo[0] == 'stoptyping':
channel_layer = channels.layers.get_channel_layer()
async_to_sync(channel_layer.group_send)(datainfo[2], {'type' : 'stop_typing'})
elif datainfo[0] == 'load':
channel_layer = channels.layers.get_channel_layer()
async_to_sync(channel_layer.group_send)(datainfo[2], {'type' : 'reloadmessages'})
def start_typing(self, event):
useristyping = User.objects.get(pk=event["typingname"])
self.send("starttyping__" + str(useristyping.pk) + "_" + useristyping.first_name + " " + useristyping.last_name + " tippt...")
def stop_typing(self, event):
self.send("stoptyping")
def reloadmessages(self, event):
self.send("reloadmessages")

View File

@ -101,6 +101,9 @@ class Agency(models.Model):
dynamicprofile = models.BooleanField(default=True)
module_messages = models.BooleanField(default=True)
module_chat = models.BooleanField(default=True)
# KOSTENPFLICHTIGE MODULE

View File

@ -2,6 +2,7 @@ from django.urls import re_path
from . import mainwebsocket
websocket_urlpatterns = [
re_path(r'', mainwebsocket.UsersConsumer),
websocket_urlpatterns = [
re_path(r'chat/(?P<creator>\w+)/(?P<single>\w+)/$', mainwebsocket.UsersChat),
re_path('main/', mainwebsocket.UsersConsumer),
]

View File

@ -22,6 +22,8 @@ import requests, csv, os
from django.templatetags.static import static
from django.conf import settings
from datetime import date
import channels.layers
from asgiref.sync import async_to_sync
def loadingFreeDays(plz):
# Getting land
@ -192,14 +194,9 @@ def adjust_group_notifications(instance, action, reverse, model, pk_set, using,
)
import channels.layers
from asgiref.sync import async_to_sync
# SIGNAL FOR STANDARDS POST SAVE
@receiver(post_save, sender=Standards)
def save_standard(sender, instance, **kwargs):
print(kwargs)
GLOBALSENDMAILS = True
# NEW STANDARD AND DIRECT PUBLIC
if(kwargs["created"]):
@ -283,6 +280,9 @@ def save_news(sender, instance, **kwargs):
else:
instance.agnotify = False
instance.save()
else:
channel_layer = channels.layers.get_channel_layer()
async_to_sync(channel_layer.group_send)("agency_" + str(instance.agency.pk), {'type' : 'agency_newnews'})
@ -344,4 +344,23 @@ def adjust_group_notifications_task(instance, action, reverse, model, pk_set, us
@receiver(signal=post_save, sender=AgencyNetworkPreperation)
def save_agjoin_prep(sender, instance, **kwargs):
newnotification = UserNotification(touser=instance.target_network.creator, notificationtext="Eine Agentur möchte Ihrem Verbund beitreten.", notificationtype="wantedag", elementid=instance.pk)
newnotification.save()
newnotification.save()
from django.core.signals import request_started
from channels_presence.models import Room
from channels_presence.models import Presence
from channels_presence.signals import presence_changed
# REQUEST MAIN STUFF
@receiver(signal=request_started)
def receiver_function(sender, **kwargs):
# DELETES ALL PRESENCE-OBJETS LOWER THAN 15 MINUTES
now_minus = datetime.datetime.now() - datetime.timedelta(minutes=2)
Presence.objects.filter(last_seen__lt=now_minus).delete()
# PREENCE CHANGED
@receiver(signal=presence_changed)
def update_presence_live(sender, **kwargs):
channel_layer = channels.layers.get_channel_layer()
async_to_sync(channel_layer.group_send)(str(kwargs["room"]), {'type' : 'update_presence_live'})

View File

@ -61,6 +61,7 @@
</head>
<body>
<!-- Page Wrapper -->
<div id="wrapper">
<!-- Sidebar -->
@ -72,7 +73,6 @@
<i class="fas fa-laptop"></i>
<div class="sidebar-brand-text mx-2" style="">Digitale Agentur</div>
</a>
<!-- Divider -->
<hr class="sidebar-divider my-0">
@ -161,6 +161,20 @@
</li>
{% endif %}
{% if request.user.profile.agency.module_chat %}
{% if active_link == 'chat' %}
<li class="nav-item active">
{% else%}
<li class="nav-item">
{%endif%}
<a class="nav-link " href="{% url 'chat:chat-management' %}" aria-expanded="true">
<i class="fas fa-comments"></i>
<span>Chat</span>
</a>
</li>
{% endif %}
{% if request.user.profile.agency.module_timemanagement %}
{% if active_link == 'abscence' %}
<li class="nav-item active">
@ -379,8 +393,17 @@
<div style="height: 300px">&nbsp;</div>
</div> <!-- End of Main Content CONTAINER FLUID-->
<!-- End of Content Wrapper -->
{% if active_link != 'chat' %}
<div id="chat_alluserscontent" style="position: fixed; bottom: 75px; right: 36px; z-index: 999;"></div>
<button id="chatButton" class="btn btn-primary" style="position: fixed; right: 36px; bottom: 30px;"><i class="far fa-comments"></i></button>
{% endif %}
</div>
</div>
<!-- CHAT BUTTON -->
<!-- End of Page Wrapper -->
<!--
<footer class="sticky-footer bg-white" style="width: 86.2%;position: absolute;
@ -619,17 +642,15 @@ function removeNotification(notifyid){
//$("#allnotificationsarea").show();
}
$(document).ready(function(){
$("#notification_items").html("");
loadUnsendNotifications();
loadUnviewnNotifications();
});
$(window).click(function() {
$("#notification_items").html("");
loadUnsendNotifications();
loadUnviewnNotifications();
$(document).on('click', function (e) {
if(e.target["id"] != 'chatButton'){
if ($(e.target).closest("#chat_alluserscontent").length === 0) {
$("#chat_alluserscontent").fadeOut();
}
}
});
</script>
@ -637,16 +658,95 @@ $(window).click(function() {
<!-- WEBSOCKETS -->
<script type="text/javascript">
$(document).ready(function(){
const mainwebsocket = new WebSocket('wss://'+window.location.host)
$("#chat_alluserscontent").hide();
ws_string = 'wss://'
if (location.protocol !== 'https:') {
ws_string = 'ws://'
}
const mainwebsocket = new WebSocket(ws_string+window.location.host+"/main/")
mainwebsocket.onmessage = function(e) {
console.log(e);
mainwebsocket.onmessage = function(e) {
if(e["data"] != "presence_update")
{
var notify = new Notification('Digitale Agentur', {
body: e["data"]
});
loadUnsendNotifications();
loadUnviewnNotifications();
}
else{
{% if active_link == "chat" %}
updatePresenceLive(e);
{% endif %}
}
};
mainwebsocket.onclose = function(e) {
console.error('Chat socket closed unexpectedly');
};
//HEARTBEAT every minute
setInterval(function()
{
mainwebsocket.send(JSON.stringify("heartbeat"));
console.log("heartbeat is alive...");
},60000);
});
window.onerror = function (msg, url, line) {
return false;
}
if (!window.Notification) {
console.log('Browser does not support notifications.');
} else {
// check if permission is already granted
if (Notification.permission === 'granted') {
// show notification here
} else {
// request permission from user
Notification.requestPermission().then(function(p) {
if(p === 'granted') {
// show notification here
console.log("OK!")
} else {
console.log('User blocked notifications.');
}
}).catch(function(err) {
console.error(err);
});
}
}
$("#chatButton").click(function(){
$.ajax(
{
type: "GET",
url: "{% url 'chat:chtaajax-getloggedusers' %}",
data : {
action : "getloggedusers"
},
success: function( data )
{
$("#chat_alluserscontent").fadeToggle();
$("#chat_alluserscontent").html(data);
}
});
});
</script>

View File

@ -0,0 +1,47 @@
<style type="text/css">
.roundimg {
border-radius: 50%;
z-index: 999;
height: 100%;
width: 100%;
border-radius: 50%;
}
.icon-container {
width: 50px;
height: 50px;
position: relative;
}
.status-circle {
width: 15px;
height: 15px;
border-radius: 50%;
bottom: 0;
right: 0;
position: absolute;
}
</style>
<div class="card col-2" style="position: fixed; right: 35px; bottom: 75px;">
<div class="card-body">
<h4>Chat starten
<button class="btn btn-sm btn-secondary" style="float: right;" onclick="javascript:$('#chat_alluserscontent').fadeOut()"><small><i class="fas fa-times"></i></small></button>
</h4>
<hr>
{% for user in usersofagency %}
<span>
<div class='icon-container'>
<img class="img-profile roundimg" src="{{ user.profile.get_photo_url }}">
<div class='status-circle' style="background-color: {% if user in onlineusers %} green {% else %} grey {% endif %};">
</div>
</div>
{{user.first_name}} {{user.last_name}}
<button class="btn btn-sm btn-secondary" style="float: right;"><small><i class="far fa-comment"></i></small></button>
</span>
<br />
{% endfor %}
</div>
</div>
<div class="chat-popup">

View File

@ -35,7 +35,7 @@ urlpatterns = [
path('impressum/', views.impressum, name="impressumda"),
path('setuserparent/', views.setuserparent, name="users-setuserparent"),
path('sendpassmail/', views.sendpassmail, name="users-sendpassmail"),
path('dacron/<slug:code>', views.cronactions, name="cronmain")
path('dacron/<slug:code>', views.cronactions, name="cronmain")
]

View File

@ -35,9 +35,11 @@ from cloud.models import DataDir
from message.models import Message
from notificsys.models import UserNotification
from organizer.models import AGContacts, AGPassword
import socket
import sys, os
from asgiref.sync import async_to_sync
from channels_presence.models import Room
from channels_presence.models import Presence
import channels.layers
def randomString(stringLength=10):
"""Generate a random string of fixed length """
@ -853,6 +855,3 @@ def cronactions(request, code):
print("API CODE FAILED")
data.update({"status" : "failed"})
return JsonResponse(data)
def index(request):
return render(request, 'users/websocket.html', {})