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/migrations/__init__.py
cloud/__pycache__/* cloud/__pycache__/*
chat/migrations/*
!chat/migrations/__init__.py
chat/__pycache__/*
notificsys/migrations/* notificsys/migrations/*
!notificsys/migrations/__init__.py !notificsys/migrations/__init__.py
notificsys/__pycache__/* notificsys/__pycache__/*

View File

@ -7,6 +7,8 @@ from rest_framework import serializers
from .serializers import StandardsSerializer from .serializers import StandardsSerializer
from rest_framework.decorators import api_view, permission_classes from rest_framework.decorators import api_view, permission_classes
from rest_framework import status from rest_framework import status
from rest_framework.authentication import SessionAuthentication, BasicAuthentication, TokenAuthentication
from rest_framework.decorators import authentication_classes
class HelloView(APIView): class HelloView(APIView):
permission_classes = (IsAuthenticated,) # <-- And here 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/ https://docs.djangoproject.com/en/2.2/ref/settings/
""" """
import os import os
from datetime import datetime, timedelta
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 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! # Nach zehn Stunden läuft der Cookie ab!
SESSION_COOKIE_AGE = 8*60*60 SESSION_COOKIE_AGE = 8*60*60
CHANNELS_PRESENCE_MAX_AGE = 30
# FOR SUMMERNOTE ORIGIN # FOR SUMMERNOTE ORIGIN
X_FRAME_OPTIONS = 'SAMEORIGIN' X_FRAME_OPTIONS = 'SAMEORIGIN'
@ -67,6 +69,7 @@ INSTALLED_APPS = [
'dasettings.apps.DASettingsConfig', 'dasettings.apps.DASettingsConfig',
'areas.apps.AreasConfig', 'areas.apps.AreasConfig',
'orga.apps.OrgaConfig', 'orga.apps.OrgaConfig',
'chat.apps.ChatConfig',
'message.apps.MessageConfig', 'message.apps.MessageConfig',
'cloud.apps.CloudConfig', 'cloud.apps.CloudConfig',
'tasks.apps.TasksConfig', 'tasks.apps.TasksConfig',
@ -90,7 +93,8 @@ INSTALLED_APPS = [
'django_user_agents', 'django_user_agents',
'rest_framework', 'rest_framework',
'rest_framework.authtoken', 'rest_framework.authtoken',
'channels' 'channels',
'channels_presence',
] ]
MIDDLEWARE = [ MIDDLEWARE = [
@ -125,7 +129,6 @@ TEMPLATES = [
REST_FRAMEWORK = { REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': ( 'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication', 'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
), ),
'DEFAULT_PERMISSION_CLASSES': [ 'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated', 'rest_framework.permissions.IsAuthenticated',

View File

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

View File

@ -1,6 +1,11 @@
import json import json
from channels.generic.websocket import WebsocketConsumer from channels.generic.websocket import WebsocketConsumer
from asgiref.sync import async_to_sync 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): class UsersConsumer(WebsocketConsumer):
@ -12,17 +17,17 @@ class UsersConsumer(WebsocketConsumer):
auch nur Clients innerhalb der Agentur treffen! auch nur Clients innerhalb der Agentur treffen!
''' '''
def connect(self): def connect(self):
loggeduser = self.scope["user"] super().connect()
async_to_sync(self.channel_layer.group_add)( loggeduser = self.scope["user"]
"agency_" + str(loggeduser.profile.agency.pk), Presence.objects.touch(self.channel_name)
self.channel_name Room.objects.add("agency_" + str(loggeduser.profile.agency.pk), self.channel_name, self.scope["user"])
)
self.accept()
def disconnect(self, close_code): 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): def receive(self, text_data):
@ -36,6 +41,9 @@ class UsersConsumer(WebsocketConsumer):
) )
''' '''
# WEBSOCKET-DATA-CONTENT # WEBSOCKET-DATA-CONTENT
def receive(self, text_data=None, bytes_data=None):
if text_data == '"heartbeat"':
Presence.objects.touch(self.channel_name)
# UPDATET STANDARD # UPDATET STANDARD
def update_standard(self, event): def update_standard(self, event):
@ -43,5 +51,54 @@ class UsersConsumer(WebsocketConsumer):
# NEW AGENCY NEWS # NEW AGENCY NEWS
def agency_newnews(self, event): def agency_newnews(self, event):
print(event) self.send("Neue Agenturnews!")
self.send("agency_newnews")
# 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) dynamicprofile = models.BooleanField(default=True)
module_messages = models.BooleanField(default=True) module_messages = models.BooleanField(default=True)
module_chat = models.BooleanField(default=True)
# KOSTENPFLICHTIGE MODULE # KOSTENPFLICHTIGE MODULE

View File

@ -2,6 +2,7 @@ from django.urls import re_path
from . import mainwebsocket from . import mainwebsocket
websocket_urlpatterns = [ websocket_urlpatterns = [
re_path(r'', mainwebsocket.UsersConsumer), 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.templatetags.static import static
from django.conf import settings from django.conf import settings
from datetime import date from datetime import date
import channels.layers
from asgiref.sync import async_to_sync
def loadingFreeDays(plz): def loadingFreeDays(plz):
# Getting land # 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 # SIGNAL FOR STANDARDS POST SAVE
@receiver(post_save, sender=Standards) @receiver(post_save, sender=Standards)
def save_standard(sender, instance, **kwargs): def save_standard(sender, instance, **kwargs):
print(kwargs)
GLOBALSENDMAILS = True GLOBALSENDMAILS = True
# NEW STANDARD AND DIRECT PUBLIC # NEW STANDARD AND DIRECT PUBLIC
if(kwargs["created"]): if(kwargs["created"]):
@ -283,6 +280,9 @@ def save_news(sender, instance, **kwargs):
else: else:
instance.agnotify = False instance.agnotify = False
instance.save() 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) @receiver(signal=post_save, sender=AgencyNetworkPreperation)
def save_agjoin_prep(sender, instance, **kwargs): 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 = 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> </head>
<body> <body>
<!-- Page Wrapper --> <!-- Page Wrapper -->
<div id="wrapper"> <div id="wrapper">
<!-- Sidebar --> <!-- Sidebar -->
@ -72,7 +73,6 @@
<i class="fas fa-laptop"></i> <i class="fas fa-laptop"></i>
<div class="sidebar-brand-text mx-2" style="">Digitale Agentur</div> <div class="sidebar-brand-text mx-2" style="">Digitale Agentur</div>
</a> </a>
<!-- Divider --> <!-- Divider -->
<hr class="sidebar-divider my-0"> <hr class="sidebar-divider my-0">
@ -161,6 +161,20 @@
</li> </li>
{% endif %} {% 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 request.user.profile.agency.module_timemanagement %}
{% if active_link == 'abscence' %} {% if active_link == 'abscence' %}
<li class="nav-item active"> <li class="nav-item active">
@ -379,8 +393,17 @@
<div style="height: 300px">&nbsp;</div> <div style="height: 300px">&nbsp;</div>
</div> <!-- End of Main Content CONTAINER FLUID--> </div> <!-- End of Main Content CONTAINER FLUID-->
<!-- End of Content Wrapper --> <!-- 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>
</div> </div>
<!-- CHAT BUTTON -->
<!-- End of Page Wrapper --> <!-- End of Page Wrapper -->
<!-- <!--
<footer class="sticky-footer bg-white" style="width: 86.2%;position: absolute; <footer class="sticky-footer bg-white" style="width: 86.2%;position: absolute;
@ -619,17 +642,15 @@ function removeNotification(notifyid){
//$("#allnotificationsarea").show(); //$("#allnotificationsarea").show();
} }
$(document).ready(function(){
$("#notification_items").html("");
loadUnsendNotifications();
loadUnviewnNotifications();
});
$(window).click(function() {
$("#notification_items").html(""); $(document).on('click', function (e) {
loadUnsendNotifications();
loadUnviewnNotifications(); if(e.target["id"] != 'chatButton'){
if ($(e.target).closest("#chat_alluserscontent").length === 0) {
$("#chat_alluserscontent").fadeOut();
}
}
}); });
</script> </script>
@ -637,16 +658,95 @@ $(window).click(function() {
<!-- WEBSOCKETS --> <!-- WEBSOCKETS -->
<script type="text/javascript"> <script type="text/javascript">
$(document).ready(function(){ $(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) { mainwebsocket.onmessage = function(e) {
console.log(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) { mainwebsocket.onclose = function(e) {
console.error('Chat socket closed unexpectedly'); 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> </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('impressum/', views.impressum, name="impressumda"),
path('setuserparent/', views.setuserparent, name="users-setuserparent"), path('setuserparent/', views.setuserparent, name="users-setuserparent"),
path('sendpassmail/', views.sendpassmail, name="users-sendpassmail"), 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 message.models import Message
from notificsys.models import UserNotification from notificsys.models import UserNotification
from organizer.models import AGContacts, AGPassword from organizer.models import AGContacts, AGPassword
import socket
import sys, os 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): def randomString(stringLength=10):
"""Generate a random string of fixed length """ """Generate a random string of fixed length """
@ -853,6 +855,3 @@ def cronactions(request, code):
print("API CODE FAILED") print("API CODE FAILED")
data.update({"status" : "failed"}) data.update({"status" : "failed"})
return JsonResponse(data) return JsonResponse(data)
def index(request):
return render(request, 'users/websocket.html', {})