Bug Cronjob

This commit is contained in:
holger.trampe 2020-10-07 11:40:58 +02:00
parent 6b94978115
commit 746e797ac6
20 changed files with 277 additions and 20 deletions

View File

@ -156,9 +156,19 @@ AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
#{
# 'NAME' : "passwords.auth_password_validators.ComplexityValidator",
#}
]
PASSWORD_COMPLEXITY = { # You can omit any or all of these for no limit for that particular set
"UPPER": 1, # Uppercase
"LOWER": 1, # Lowercase
"LETTERS": 1, # Either uppercase or lowercase letters
"DIGITS": 1, # Digits
"SPECIAL": 1, # Not alphanumeric, space or punctuation character
"WORDS": 0 # Words (alphanumeric sequences separated by a whitespace or punctuation character)
}
# Internationalization
# https://docs.djangoproject.com/en/2.2/topics/i18n/

View File

@ -2,6 +2,9 @@ from django import forms
from django.forms import ModelForm
from .models import *
from bootstrap_datepicker_plus import DatePickerInput
from django.contrib.auth.password_validation import validate_password
from django.core import validators
from passwords.validators import *
class PersLetterForm(forms.ModelForm):
@ -18,6 +21,22 @@ class PersLetterForm(forms.ModelForm):
super(PersLetterForm, self).__init__(*args, **kwargs)
self.fields['text'] = forms.CharField(label="Ihr persönlicher Text", widget=forms.Textarea(attrs={"rows":15, "cols":35}))
class LoginRDForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['pass'] = forms.CharField(widget=forms.PasswordInput, label="Passwort", required=True)
class RecoverDirSettingForm(forms.ModelForm):
class Meta:
model = RecoverDirSetting
fields = ['logpass']
def __init__(self, *args, **kwargs):
super(RecoverDirSettingForm, self).__init__(*args, **kwargs)
self.fields['logpass'] = forms.CharField(widget=forms.PasswordInput, label="Passwort", required=True, validators=[dictionary_words, complexity, validate_length])
self.fields['logpass_check'] = forms.CharField(widget=forms.PasswordInput, label="Passwort wiederholen", required=True,validators=[dictionary_words, complexity, validate_length])
# Notfallhilfe FORMS
# 1 Handlungsleitfaden

View File

@ -18,6 +18,12 @@ def rd_path_agency(instance, filename):
return 'agencydata/agency_{0}/rd/{1}'.format(instance.agency.pk, filename)
# MAIN RECOVERDIR PASSWORD AND CONFIG
class RecoverDirSetting(models.Model):
agency = models.ForeignKey(Agency, on_delete=models.CASCADE)
logpass = encrypt(models.CharField(max_length=500, blank=True, default="", null=True))
recoverkey = encrypt(models.CharField(max_length=500, blank=True, default="", null=True))
# Create your models here.
class PersLetter(models.Model):

View File

@ -1,7 +1,6 @@
<h4>Personal, interne und externe Partner inkl. Funktionen usw.
<a class="btn btn-primary btn btn-sm mb-3" href="{% url 'rd-a5-addpersonal' %}" style="float: right;"><i class="fas fa-plus"></i>&nbsp;Personal</a>
</h4>
<small>Beim externen Aufrufen der Notfallhilfe werden alle aktuell angelegten Mitarbeiter inkl. Kontaktdaten angezeigt. Erstellen Sie daher hier Mitarbeiter, die nicht in der digitalen Agentur angelegt sind.</small>
<hr>
<table class="table table-hover" id="a5_personal" >
<thead>
@ -29,6 +28,17 @@
</td>
</tr>
{% endfor %}
{% for us in users_of_agency %}
<tr>
<td>{{us.get_full_name}}</td>
<td>{{us.profile.func|default:""}}</td>
<td>
Intern
</td>
<td>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<hr>
@ -101,7 +111,8 @@ $(document).ready(function(){
"pageLength": 50,
"buttons" : {
"className" : "btn-danger"
}
},
"order": [[ 1, "desc" ]]
});
});

View File

@ -0,0 +1,26 @@
{% extends "users/base.html" %}
{% load counter_tag %}
{% load crispy_forms_tags %}
{% block content %}
{% if request.user.profile.agency.module_recoverdir %}
<div class="content-section col-12">
<h3>Notfallhilfe{% if request.user.profile.showtooltips %}&nbsp;<small><i data-toggle="tooltip" data-placement="top" title="Legen Sie hier die wichtigsten Dokumente für sich und Ihre Agentur an." class="far fa-question-circle"></i></small>{% endif %}
<!-- TASK: Video für Notfallhilfe <small><i onclick="javascript:$('#youtubevideoinformation').modal('toggle');" class="far fa-play-circle"></i></small>-->
</h3>
<hr>
<div class="col-6">
Bitte melden Sie sich mit dem Passwort für die Notfallhilfe an!
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{form.media}}
{{form|crispy}}
<hr>
<button type="submit" class="btn btn-primary" style="float: right;">Anmelden</button>&nbsp;
</form>
</div>
</div>
{% else %}
Sie haben keinen Zugriff auf das Modul Notfallhilfe. Bitte wenden Sie sich an den Support!
{% endif %}
{% endblock content %}

View File

@ -5,6 +5,7 @@
<div class="content-section col-12">
<h3>Notfallhilfe{% if request.user.profile.showtooltips %}&nbsp;<small><i data-toggle="tooltip" data-placement="top" title="Legen Sie hier die wichtigsten Dokumente für sich und Ihre Agentur an." class="far fa-question-circle"></i></small>{% endif %}
<!-- TASK: Video für Notfallhilfe <small><i onclick="javascript:$('#youtubevideoinformation').modal('toggle');" class="far fa-play-circle"></i></small>-->
<a style="float: right" class="btn btn-secondary btn-sm mr-2 " href="{% url 'closerecoverdir' %}"><i class="fas fa-lock"></i></a>
</h3>
<hr>
<div class="row">
@ -63,7 +64,10 @@
</div>
<div class="tab-pane fade" id="t_settings" role="tabpanel" aria-labelledby="settings">
Einstellungen
<h5 class="mt-3">Einstellungen{% if request.user.profile.showtooltips %}&nbsp;<small><i data-toggle="tooltip" data-placement="top" title="Verwalten Sie hier die Einstellungen für den Zugang zur Notfallhilfe." class="far fa-question-circle"></i></small>{% endif %}</h5>
{% block settings_content %}
{% include "recoverdir/rd_viewsettings.html" %}
{% endblock %}
</div>
<div class="tab-pane fade" id="t_help" role="tabpanel" aria-labelledby="help">

View File

@ -125,7 +125,7 @@
<div class="card-header" id="headingOne">
<h5 class="mb-0">
<button class="btn btn-link tabelement" onclick="javascript:newTabOpen(5)" data-toggle="collapse" data-target="#rd_5" aria-expanded="true" aria-controls="rd_5">
<h4 style="margin-top: 11px;">5. Gewerbe: Vorsorgedokumente</h4>
<h4 style="margin-top: 11px;">5. Vorsorgedokumente</h4>
</button>
<span style="float: right;">
<span class="square_company_arrow"><h2 class="h2_in_arrow">#5</h2></span>
@ -147,7 +147,7 @@
<div class="card-header" id="headingOne">
<h5 class="mb-0">
<button class="btn btn-link tabelement" onclick="javascript:newTabOpen(6)" data-toggle="collapse" data-target="#rd_6" aria-expanded="true" aria-controls="rd_6">
<h4 style="margin-top: 11px;">6. Gewerbe: Vorsorge und Finanzen</h4>
<h4 style="margin-top: 11px;">6. Vorsorge und Finanzen</h4>
</button>
<span style="float: right;">
<span class="square_company_arrow"><h2 class="h2_in_arrow">#6</h2></span>
@ -169,7 +169,7 @@
<div class="card-header" id="headingOne">
<h5 class="mb-0">
<button class="btn btn-link tabelement" onclick="javascript:newTabOpen(7)" data-toggle="collapse" data-target="#rd_7" aria-expanded="true" aria-controls="rd_7">
<h4 style="margin-top: 11px;">7. Gewerbe: Immobilien</h4>
<h4 style="margin-top: 11px;">7. Immobilien</h4>
</button>
<span style="float: right;">
<span class="square_company_arrow"><h2 class="h2_in_arrow">#7</h2></span>
@ -191,7 +191,7 @@
<div class="card-header" id="headingOne">
<h5 class="mb-0">
<button class="btn btn-link tabelement" onclick="javascript:newTabOpen(8)" data-toggle="collapse" data-target="#rd_8" aria-expanded="true" aria-controls="rd_8">
<h4 style="margin-top: 11px;">8. Gewerbe: Verträge</h4>
<h4 style="margin-top: 11px;">8. Verträge</h4>
</button>
<span style="float: right;">
<span class="square_company_arrow"><h2 class="h2_in_arrow">#8</h2></span>

View File

@ -0,0 +1,21 @@
{% extends "users/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
{% if request.user.profile.agency.module_recoverdir %}
<div class="content-section col-9">
<h3>Einstellungen verwalten</h3>
<hr>
<form method="POST" enctype="multipart/form-data">
<small>Beachten Sie bitte, dass dieses Passwort allen Personen sicher zugänglich gemacht werden muss, die auf die Notfallhilfe zugreifen können! Zusätzlich benötigen diese Personen den Sicherheitssschlüssel. Dieser wird automatisch generiert und kann nicht geändert werden. Sie finden den Sicherheitsschlüssel unter Notfallhilfe, Einstellungen.</small>
{% csrf_token %}
{{normalForm.media}}
{{ form|crispy }}
<hr>
<a class="btn" href="{% url 'recoverdir' %} ">Abbrechen</a>
<button type="submit" class="btn btn-primary" style="float: right;">Einstellungen speichern</button>&nbsp;
</form>
</div>
{% else %}
<h3>Das Modul Notfallhilfe wurden in ihrer Agentur deaktiviert.</h3>
{% endif %}
{% endblock content %}

View File

@ -61,6 +61,9 @@
{% elif hisotryelementinfo.1 == 11 %}
<a href="{% url 'recoverdir-personalhistory-single' ele.pk rdele.pk %}">{{hisotryelementinfo.0}}</a>
{% elif hisotryelementinfo.1 == 20 %}
<a href="{% url 'recoverdir-elsehistory-single' ele.pk rdele.pk %}">{{hisotryelementinfo.0}}</a>
{% endif %}
</td>
<td>{{rdele.history_date|date:"d.m.Y H:i"}}</td>

View File

@ -0,0 +1,20 @@
<hr>
<table>
<tr>
<td>Sicherheitsschlüssel (nicht veränderbar):</td><td><b>{{rd_settings.recoverkey}}</b></td>
</tr>
<tr>
<td>Passwort für die Notfallhilfe:</td><td><span id="pw_hidden">***************</span><span id="pw" style="display: none;"><b>{{rd_settings.logpass}}</b></span></td>
<td>
<button style="float: right" class="btn btn-secondary btn-sm mr-2 " onclick="javascript:showPW()"><i class="far fa-eye"></i></button>&nbsp;
<a style="float: right" class="btn btn-secondary btn-sm mr-2 " href="{% url 'recoverdir-updatesettings' rd_settings.pk %}"><small><i class="fas fa-pen"></i></small></a>&nbsp;
</td>
</tr>
</tr>
</table>
<script type="text/javascript">
function showPW(){
$("#pw_hidden").toggle();
$("#pw").toggle();
}
</script>

View File

@ -12,6 +12,13 @@ Permissions definiert in models.py bei USERS und dann hier vor die View geschrie
urlpatterns = [
path('', permission_required('users.recoverdirmanager')(RecoverDirManagement.as_view(template_name="recoverdir/rd_management.html")), name='recoverdir'),
# SETTINGS
path('rdsettings/', permission_required('users.recoverdirmanager')(RecoverDirAddSettings.as_view()), name='recoverdir-addsettings'),
path('rdsettings/update/<int:pk>', permission_required('users.recoverdirmanager')(RecoverDirUpdateSettings.as_view()), name='recoverdir-updatesettings'),
path('rdlogin/', permission_required('users.recoverdirmanager')(RecoverDirLog.as_view()), name='recoverdir-login'),
path('close/', permission_required('users.recoverdirmanager')(CloseRecoverDir), name='closerecoverdir'),
# Persönliches Schreiben
path('addpl/', permission_required('users.recoverdirmanager')(RecoverDirAddPL.as_view(template_name="recoverdir/rd_pers_add.html")), name='recoverdir-addpl'),
path('updatepl/<int:pk>', permission_required('users.recoverdirmanager')(RecoverDirUpdatePL.as_view(template_name="recoverdir/rd_pers_update.html")), name='recoverdir-updatepl'),

View File

@ -1,9 +1,11 @@
from django.shortcuts import render
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import CreateView, ListView, UpdateView, DetailView, DeleteView
from django.views.generic import CreateView, ListView, UpdateView, DetailView, DeleteView, FormView
from .models import *
from .forms import *
from django.urls import reverse_lazy
from django.shortcuts import redirect
from django.contrib import messages
from django.contrib.auth.decorators import login_required
import os
import sys
@ -12,11 +14,27 @@ from django.http import JsonResponse, HttpResponse, Http404
from django_encrypted_filefield.views import FetchView
import string, random
from itertools import chain
from django.contrib.auth.password_validation import *
from datetime import datetime
from django.contrib.auth.models import User
# Create your views here.
class RecoverDirManagement(LoginRequiredMixin, ListView):
model = PersLetter
# First Method!
def dispatch(self, request, *args, **kwargs):
# Check, if initial config exist and user has rights
if(len(RecoverDirSetting.objects.filter(agency=self.request.user.profile.agency)) == 0 and self.request.user.has_perm('users.recoverdirmanager')):
# Settings not found
return redirect('recoverdir-addsettings')
# TASK: Hier noch einstellen, dass der Nutzer weniger als 30 Minuten nicht inaktiv war!
elif(self.request.user.profile.rd_login == None and self.request.user.has_perm('users.recoverdirmanager')):
return redirect('recoverdir-login')
else:
return super(RecoverDirManagement, self).get(request)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({'active_link' : 'recoverdir'})
@ -61,6 +79,8 @@ class RecoverDirManagement(LoginRequiredMixin, ListView):
finalupdatelist = chain(persletters, handlungsleitfaden, contactfc, contactstrust, handlungsleitfadenvf, depistvollmacht, ergodigi, onlinebank, streamingabo, digitalaccount, personal, onlinebank6, elseele)
context.update({"history" : finalupdatelist})
users_of_agency = User.objects.filter(profile__agency=self.request.user.profile.agency)
context.update({"users_of_agency" : users_of_agency})
# DOCUMENTS NOT WORKING Weil das "alte" nicht gespeichert wird sondern lediglich der Datensatz
#documents = Documents.objects.filter(agency=self.request.user.profile.agency, area=1)
@ -117,8 +137,93 @@ class RecoverDirManagement(LoginRequiredMixin, ListView):
# A9
context.update({'area_9_doc' : Documents.objects.filter(agency=self.request.user.profile.agency, area=9).order_by('-document_date')})
# Load Settings
context.update({'rd_settings' : RecoverDirSetting.objects.filter(agency=self.request.user.profile.agency)[0]})
return context
def CloseRecoverDir(request):
request.user.profile.rd_login = None
request.user.profile.save()
return redirect('recoverdir')
class RecoverDirLog(FormView):
template_name = "recoverdir/rd_elements_forms/rd_mainlogin.html"
form_class = LoginRDForm
success_url = reverse_lazy('recoverdir')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'active_link' : 'recoverdir',
'form' : LoginRDForm()
})
return context
def form_valid(self, form):
# Load Settings-Data
settings = RecoverDirSetting.objects.filter(agency=self.request.user.profile.agency)[0]
if form.cleaned_data.get("pass") == settings.logpass and self.request.user.has_perm("users.recoverdirmanager") and settings.agency == self.request.user.profile.agency:
self.request.user.profile.rd_login = datetime.now()
self.request.user.profile.save()
else:
messages.warning(self.request, f'Passwort nicht korrekt!')
return super().form_valid(form)
class RecoverDirAddSettings(CreateView):
model = RecoverDirSetting
success_url = reverse_lazy('recoverdir')
form_class = RecoverDirSettingForm
template_name = "recoverdir/rd_settings.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'active_link' : 'recoverdir',
})
return context
def form_valid(self, form):
form.instance.agency = self.request.user.profile.agency
# Initial Recover-Key
if(len(RecoverDirSetting.objects.filter(agency=self.request.user.profile.agency)) == 0 and self.request.user.has_perm('users.recoverdirmanager')):
form.instance.recoverkey = randomStringRecoverKey()
if(form.cleaned_data.get('logpass') == form.cleaned_data.get('logpass_check')):
messages.warning(self.request, f'Daten gespeichert!')
else:
messages.warning(self.request, f'Die Passwörter stimmen nicht überein. Bitte neu eingeben!')
return redirect('recoverdir-addsettings')
return super().form_valid(form)
class RecoverDirUpdateSettings(UpdateView):
model = RecoverDirSetting
success_url = reverse_lazy('recoverdir')
form_class = RecoverDirSettingForm
template_name = "recoverdir/rd_settings.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'active_link' : 'recoverdir',
})
return context
def form_valid(self, form):
# Initial Recover-Key
if(form.cleaned_data.get('logpass') == form.cleaned_data.get('logpass_check')):
messages.warning(self.request, f'Daten gespeichert!')
else:
messages.warning(self.request, f'Die Passwörter stimmen nicht überein. Bitte neu eingeben!')
return redirect('recoverdir-addsettings')
return super().form_valid(form)
class RecoverDirAddPL(CreateView):
model = PersLetter
success_url = reverse_lazy('recoverdir')
@ -160,6 +265,18 @@ def randomString(stringLength=40):
letters = string.ascii_lowercase
return ''.join(random.choice(letters) for i in range(stringLength))
def randomStringRecoverKey():
letters = string.ascii_uppercase
key = ""
for run in range(10):
key += ''.join(random.choice(letters) for i in range(5))
if(run < 9):
key += "-"
return key
# Notfallhilfe ELEMENTE
# ABNSCHNITT 1
@ -1070,7 +1187,7 @@ class ElseSingleHistory(DetailView):
shown_element = he
context.update({
'ele' : shown_element,
'else' : shown_element,
'history' : True
})
return context

View File

@ -31,3 +31,4 @@ python-dateutil==2.8.1
django-simple-history==2.11.0
django-encrypted-filefield==0.2.2
more-itertools==8.5.0
django-passwords==0.3.12

View File

@ -799,7 +799,7 @@ def getHistoryClassOfObject(value):
finalclass[0] = "Personal und Partner"
finalclass[1] = 11
# ELSE
elif(objectClass == 'HistoricalElse'):
elif(objectClass == 'HistoricalRDElse'):
finalclass[0] = "Sonstiges"
finalclass[1] = 20

View File

@ -8,7 +8,7 @@ from cloud.models import DataFile
from organizer.models import AGContacts
from timemanagement.models import Workday, Breaks, AbsenceReason, FreeDays, Absence
from chat.models import ChatRoom
from recoverdir.models import PersLetter, Documents, Handlungsleitfaden, RDContact
from recoverdir.models import PersLetter, Documents, Handlungsleitfaden, RDContact, RecoverDirSetting
from simple_history.admin import SimpleHistoryAdmin
@ -40,3 +40,4 @@ admin.site.register(PersLetter, SimpleHistoryAdmin)
admin.site.register(Documents, SimpleHistoryAdmin)
admin.site.register(Handlungsleitfaden)
admin.site.register(RDContact)
admin.site.register(RecoverDirSetting)

View File

@ -262,6 +262,9 @@ class Profile(models.Model):
'''
onlinestatus = models.IntegerField(default=0)
# RECOVER DIR LAST LOGIN FIELD - AFTER LOGIN SET TO NONE
rd_login = models.DateTimeField(null=True, blank=True, default=None)
def __str__(self):
return f'{self.user.last_name}'

View File

@ -59,12 +59,14 @@ def loadingFreeDays(plz, year):
@receiver(signal=user_logged_out, sender=User)
def checkForFreeDays(sender, user, request, **kwargs):
user.profile.onlinestatus = 3
user.profile.rd_login = None
user.save()
# CHECK SOMETHING WHEN USER LOGGED IN
@receiver(signal=user_logged_in, sender=User)
def checkDefaultAbsenceReasons(sender, user, request, **kwargs):
user.profile.onlinestatus = 0
user.profile.rd_login = None
user.save()
ar = AbsenceReason.objects.filter(agency=user.profile.agency)
if(len(ar) == 0):

View File

@ -47,7 +47,7 @@ import filetype
from django.db.models.signals import m2m_changed
from django.contrib.auth.models import User, Group
from users.signals import adjust_group_notifications_permission
from django.core.exceptions import ObjectDoesNotExist
def randomString(stringLength=10):
"""Generate a random string of fixed length """
@ -1084,11 +1084,17 @@ def cronactionsdaily(request, code):
day_tocheck = usertimedata.loose_holidedate.split(".")[0]
month_tocheck = usertimedata.loose_holidedate.split(".")[1]
month = today.month
day = today.day
if month < 10:
month = "0" + str(month)
day = today.day
else:
month = month
if day < 10:
day = "0" + str(day)
else:
day = day
# Restetag erreicht, Reste ins nächste Jahr übertragen
if(str(day_tocheck) == str(day) and str(month_tocheck) == str(month)):
sourceyear = today.year
@ -1144,10 +1150,10 @@ def cronactionsdaily(request, code):
channel_layer = channels.layers.get_channel_layer()
async_to_sync(channel_layer.group_send)("user_" + str(user.pk), {'type' : 'pushhandler', 'pushtext' : "pushnotification__Abwesenheit | In einer Woche startet Ihre Vertretung für " + r.user.first_name + " " + r.user.last_name + "!"})
data.update({"status" : "ok"})
except:
pass
data.update({"status " + str(user.pk) : "ok"})
except ObjectDoesNotExist:
data.update({"status" + str(user.pk) : "no usertime found for " + user.get_full_name()})
else:
print("API CODE FAILED")
data.update({"status" : "failed"})