diff --git a/.gitignore b/.gitignore index f46a329..a70c028 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,10 @@ standards/migrations/* !standards/migrations/__init__.py standards/__pycache__/* +api/migrations/* +!api/migrations/__init__.py +api/__pycache__/* + tasks/migrations/* !tasks/migrations/__init__.py tasks/__pycache__/* diff --git a/api/__init__.py b/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/admin.py b/api/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/api/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/api/apps.py b/api/apps.py new file mode 100644 index 0000000..d87006d --- /dev/null +++ b/api/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class ApiConfig(AppConfig): + name = 'api' diff --git a/api/migrations/__init__.py b/api/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/models.py b/api/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/api/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/api/serializers.py b/api/serializers.py new file mode 100644 index 0000000..22c12d9 --- /dev/null +++ b/api/serializers.py @@ -0,0 +1,19 @@ +from django.contrib.auth.models import User +from rest_framework import serializers +from rest_framework.validators import UniqueTogetherValidator +from standards.models import Standards + +class StandardsSerializer(serializers.ModelSerializer): + + username = serializers.SerializerMethodField('getCreatedByUser') + last_modified_on = serializers.SerializerMethodField('getFormatedLastModified') + + class Meta: + model = Standards + fields = ["id", "name", "last_modified_on", "username", "content"] + + def getCreatedByUser(self, standard): + return standard.created_standard_by.first_name + " " + standard.created_standard_by.first_name + + def getFormatedLastModified(self, standard): + return standard.last_modified_on.strftime("%d.%m.%Y %H:%M") \ No newline at end of file diff --git a/api/tests.py b/api/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/api/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/api/urls.py b/api/urls.py new file mode 100644 index 0000000..e95f90b --- /dev/null +++ b/api/urls.py @@ -0,0 +1,11 @@ +from django.urls import path +from .views import HelloView +from . import views + +app_name = 'api' +urlpatterns = [ + path('helloview/', HelloView.as_view(), name='api-helloview'), + path('getstandards/', views.getStandardList, name='api-getstandards'), + path('getsinglestandard/', views.getSingleStandard, name='api-getsinglestandards'), + +] \ No newline at end of file diff --git a/api/views.py b/api/views.py new file mode 100644 index 0000000..1b9af62 --- /dev/null +++ b/api/views.py @@ -0,0 +1,52 @@ +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework.permissions import IsAuthenticated # <-- Here +import json +from standards.models import Standards +from rest_framework import serializers +from .serializers import StandardsSerializer +from rest_framework.decorators import api_view, permission_classes +from rest_framework import status + +class HelloView(APIView): + permission_classes = (IsAuthenticated,) # <-- And here + + def post(self, request): + content = {'message': 'Hello, World!'} + return Response(content) + +''' +class GiveMeStandards(APIView): + + + def post(self, request): + standards = Standards.objects.filter(agency=request.user.profile.agency) + + content = {} + i = 0 + for s in standards: + content.update({i : s.name.encode("utf-8")}) + i += 1 + + content = {'standards': content} + return Response(content) + + + def post(self, request): + ser = StandardsSerializer(data=Standards.objects.filter(agency=self.request.user.profile.agency)) + return Response(ser) +''' + +@api_view(['POST', ]) +@permission_classes((IsAuthenticated,)) +def getStandardList(request): + standards = Standards.objects.filter(agency=request.user.profile.agency) + ser = StandardsSerializer(standards, many=True) + return Response(ser.data, status=status.HTTP_200_OK) + +@api_view(['POST', ]) +@permission_classes((IsAuthenticated,)) +def getSingleStandard(request, pk): + standard = Standards.objects.get(pk=int(pk)) + ser = StandardsSerializer(standard, many=False) + return Response(ser.data, status=status.HTTP_200_OK) \ No newline at end of file diff --git a/digitaleagentur/__pycache__/settings.cpython-38.pyc b/digitaleagentur/__pycache__/settings.cpython-38.pyc index 65edeee..3e83899 100644 Binary files a/digitaleagentur/__pycache__/settings.cpython-38.pyc and b/digitaleagentur/__pycache__/settings.cpython-38.pyc differ diff --git a/digitaleagentur/__pycache__/urls.cpython-38.pyc b/digitaleagentur/__pycache__/urls.cpython-38.pyc index 2a17a2e..4086f1b 100644 Binary files a/digitaleagentur/__pycache__/urls.cpython-38.pyc and b/digitaleagentur/__pycache__/urls.cpython-38.pyc differ diff --git a/digitaleagentur/__pycache__/wsgi.cpython-38.pyc b/digitaleagentur/__pycache__/wsgi.cpython-38.pyc index eb6019f..f44f0f3 100644 Binary files a/digitaleagentur/__pycache__/wsgi.cpython-38.pyc and b/digitaleagentur/__pycache__/wsgi.cpython-38.pyc differ diff --git a/digitaleagentur/routing.py b/digitaleagentur/routing.py new file mode 100644 index 0000000..f67ce3e --- /dev/null +++ b/digitaleagentur/routing.py @@ -0,0 +1,12 @@ +from channels.auth import AuthMiddlewareStack +from channels.routing import ProtocolTypeRouter, URLRouter +import users.routing + +application = ProtocolTypeRouter({ + # Empty for now (http->django views is added by default) + 'websocket': AuthMiddlewareStack( + URLRouter( + users.routing.websocket_urlpatterns + ) + ), +}) \ No newline at end of file diff --git a/digitaleagentur/settings.py b/digitaleagentur/settings.py index 6403933..6ad9545 100644 --- a/digitaleagentur/settings.py +++ b/digitaleagentur/settings.py @@ -14,32 +14,35 @@ import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -############################################## PROD ##################################### -BASE_URL = "https://digitale-agentur.com/" +############################################## DEV ##################################### +BASE_URL = "https://dev01.digitale-agentur.com/" CRONAPIKEY = "gCddsaz6NOnE9QbXZM5LasdEk122D" MAILINFOKEY = "jka7sd8iukashdna78skduJAHDsu6dilaksdjba65a68iadbhjak" # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = False +DEBUG = True -# MAIL PROD +# MAIL DEV EMAIL_HOST = 'smtp.strato.de' EMAIL_PORT = 587 EMAIL_USE_TLS = True -EMAIL_HOST_USER = "noreply@digitale-agentur.com" -EMAIL_HOST_PASSWORD = "48c3n6YggZBuPyShtqOQ" -DEFAULT_FROM_EMAIL = "noreply@digitale-agentur.com" +EMAIL_HOST_USER = "support@dev01.digitale-agentur.com" +EMAIL_HOST_PASSWORD = "n2xd7emyKZFb6UREzvbintuUIG" +DEFAULT_FROM_EMAIL = "support@dev01.digitale-agentur.com" -# PROD +# DEV DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', - 'NAME' : 'digitaleagentur', - 'USER' : 'digitaleagentur', - 'PASSWORD' : 'H9hzbzyBqtUCnZlIwL1qSrzh', + 'NAME' : 'digitaleagentur_dev01', + 'USER' : 'digitaleagentur_dev01', + 'PASSWORD' : 't3TvtGAOkFHYXdJlUMIu9u3U', 'PORT' : 3306 } } -############################################## PROD ##################################### + +# REDIS +REDIS_URL = ("localhost", 6379) +############################################## DEV ##################################### @@ -55,7 +58,7 @@ X_FRAME_OPTIONS = 'SAMEORIGIN' # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = '_qv2t2lmsctjxpbb4rrp=op%_20_hxzonv^mvty1o85c)l$s^q' -ALLOWED_HOSTS = ['digitale-agentur.com', 'www.digitale-agentur.com', 'localhost', 'dev01.digitale-agentur.com'] +ALLOWED_HOSTS = ['digitale-agentur.com', 'www.digitale-agentur.com', 'localhost', 'dev01.digitale-agentur.com', '10.0.2.2'] # Application definition INSTALLED_APPS = [ @@ -72,7 +75,7 @@ INSTALLED_APPS = [ 'timemanagement.apps.TimemanagementConfig', 'news.apps.NewsConfig', 'crispy_forms', - 'colorful', + 'colorful', 'django_summernote', 'django.contrib.admin', 'mathfilters', @@ -85,6 +88,9 @@ INSTALLED_APPS = [ 'bootstrap_datepicker_plus', 'django_cleanup', 'django_user_agents', + 'rest_framework', + 'rest_framework.authtoken', + 'channels' ] MIDDLEWARE = [ @@ -116,10 +122,25 @@ TEMPLATES = [ }, ] -WSGI_APPLICATION = 'digitaleagentur.wsgi.application' - - +REST_FRAMEWORK = { + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework.authentication.TokenAuthentication', + ), + 'DEFAULT_PERMISSION_CLASSES': [ + 'rest_framework.permissions.IsAuthenticated', + ], +} +#WSGI_APPLICATION = 'digitaleagentur.wsgi.application' +ASGI_APPLICATION = "digitaleagentur.routing.application" +CHANNEL_LAYERS = { + 'default': { + 'BACKEND': 'channels_redis.core.RedisChannelLayer', + 'CONFIG': { + "hosts": [REDIS_URL], + }, + }, +} # Password validation diff --git a/digitaleagentur/urls.py b/digitaleagentur/urls.py index 764d28c..4ed0e56 100644 --- a/digitaleagentur/urls.py +++ b/digitaleagentur/urls.py @@ -6,6 +6,7 @@ from django.conf.urls.static import static 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 @@ -43,7 +44,9 @@ urlpatterns = [ path('register/done', views.registerdone, name='register-done'), path('summernote/', include('django_summernote.urls')), 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-token-auth/', obtain_auth_token, name='api-token-auth'), ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) if settings.DEBUG: urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \ No newline at end of file diff --git a/users/mainwebsocket.py b/users/mainwebsocket.py new file mode 100644 index 0000000..ee33275 --- /dev/null +++ b/users/mainwebsocket.py @@ -0,0 +1,47 @@ +import json +from channels.generic.websocket import WebsocketConsumer +from asgiref.sync import async_to_sync + +class UsersConsumer(WebsocketConsumer): + + ''' + + 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): + loggeduser = self.scope["user"] + async_to_sync(self.channel_layer.group_add)( + "agency_" + str(loggeduser.profile.agency.pk), + self.channel_name + ) + self.accept() + + def disconnect(self, close_code): + pass + + ''' + def receive(self, text_data): + loggeduser = self.scope["user"] + async_to_sync(self.channel_layer.group_send)( + 'allusers', + { + 'type': 'chat_message', + 'message': 'von mainwebsocket.py' + } + ) + ''' + # WEBSOCKET-DATA-CONTENT + + # UPDATET STANDARD + def update_standard(self, event): + self.send("standard_update") + + # NEW AGENCY NEWS + def agency_newnews(self, event): + print(event) + self.send("agency_newnews") \ No newline at end of file diff --git a/users/routing.py b/users/routing.py new file mode 100644 index 0000000..b4a7cc9 --- /dev/null +++ b/users/routing.py @@ -0,0 +1,7 @@ +from django.urls import re_path + +from . import mainwebsocket + +websocket_urlpatterns = [ + re_path(r'', mainwebsocket.UsersConsumer), +] \ No newline at end of file diff --git a/users/signals.py b/users/signals.py index 9c7a33a..1077735 100644 --- a/users/signals.py +++ b/users/signals.py @@ -82,6 +82,9 @@ def checkDefaultAbsenceReasons(sender, user, request, **kwargs): new_ar_school = AbsenceReason(agency=user.profile.agency, name="Berufsschule", need_confirm=False, need_rep=False, is_holiday=False) new_ar_school.save() + + + @receiver(signal=user_logged_in, sender=User) def checkAllFreeDaysLoaded(sender, user, request, **kwargs): pass @@ -189,11 +192,14 @@ 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"]): @@ -229,7 +235,9 @@ def save_standard(sender, instance, **kwargs): if(user.has_perm("users.standardmanager")): newnotification = UserNotification(touser=user, notificationtext="Neuer unveröffentlichter Agenturstandard: " + instance.name, notificationtype="newstandard", elementid=instance.pk) newnotification.save() - + else: + channel_layer = channels.layers.get_channel_layer() + async_to_sync(channel_layer.group_send)("agency_" + str(instance.agency.pk), {'type' : 'update_standard'}) # SIGNAL FOR NEWS @@ -237,6 +245,11 @@ def save_standard(sender, instance, **kwargs): def save_news(sender, instance, **kwargs): GLOBALSENDMAILS = True if(kwargs["created"]): + + # Hier wird allen verbundenne Agenturmitgliedern die Info geschickt, dass es neue Agenturnews gibt + channel_layer = channels.layers.get_channel_layer() + async_to_sync(channel_layer.group_send)("agency_" + str(instance.agency.pk), {'type' : 'agency_newnews'}) + usersofagency = User.objects.filter(profile__agency__pk=instance.agency.pk) targeturl = settings.BASE_URL + "news/news/" + str(instance.pk) + "/single" @@ -265,9 +278,12 @@ def save_news(sender, instance, **kwargs): if(user.profile.news_push): newnotification = UserNotification(touser=user, notificationtext="Neue Agenturnews: " + instance.name, notificationtype="agencynews", elementid=instance.pk) newnotification.save() + + else: instance.agnotify = False - instance.save() + instance.save() + # SIGNALS FOR TASK diff --git a/users/templates/users/base.html b/users/templates/users/base.html index 1a14ad3..a41524e 100644 --- a/users/templates/users/base.html +++ b/users/templates/users/base.html @@ -14,7 +14,7 @@ - + @@ -40,23 +40,23 @@ - + - - + + - + - + - - - + + + @@ -399,15 +399,15 @@ - + - + - - + + - + @@ -423,7 +423,7 @@ - + @@ -573,8 +573,6 @@ function loadUnsendNotifications(){ $("#notificationcounter").html(""); if(i > 0){$("#notificationcounter").html(i);} - - } }); } @@ -633,9 +631,22 @@ $(window).click(function() { loadUnsendNotifications(); loadUnviewnNotifications(); }); + + + \ No newline at end of file diff --git a/users/templates/users/dashboard.html b/users/templates/users/dashboard.html index 6df32a9..85e0732 100644 --- a/users/templates/users/dashboard.html +++ b/users/templates/users/dashboard.html @@ -5,8 +5,7 @@ Letzter Login: {{ request.user.last_login }}
Agentur: {{ request.user.profile.agency.name }}
-
- +
{% if request.user.profile.agency.module_news %}
@@ -142,7 +141,6 @@ $(document).ready(function(){
- {% endblock content %} diff --git a/users/urls.py b/users/urls.py index 17a3bf6..7e83199 100644 --- a/users/urls.py +++ b/users/urls.py @@ -35,8 +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/', views.cronactions, name="cronmain"), - + path('dacron/', views.cronactions, name="cronmain") ] diff --git a/users/views.py b/users/views.py index 737593a..67d4408 100644 --- a/users/views.py +++ b/users/views.py @@ -854,5 +854,5 @@ def cronactions(request, code): data.update({"status" : "failed"}) return JsonResponse(data) - - +def index(request): + return render(request, 'users/websocket.html', {}) \ No newline at end of file