937 lines
30 KiB
Python
937 lines
30 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
import datetime
|
||
|
import django
|
||
|
from django.db import utils, IntegrityError
|
||
|
from django.db import models
|
||
|
from django.db.models import *
|
||
|
from django.contrib.auth import get_user_model
|
||
|
from django.contrib.auth.models import AbstractUser, PermissionsMixin
|
||
|
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
|
||
|
|
||
|
from django.core.exceptions import ValidationError
|
||
|
|
||
|
from django.utils.translation import ugettext_lazy as _
|
||
|
|
||
|
from django.conf import settings as appSettings
|
||
|
|
||
|
from keycloak_oidc import exceptions as sso_exc
|
||
|
|
||
|
|
||
|
class AppUser(AbstractUser):
|
||
|
"""
|
||
|
Prepare an empty User class just in case it will be needed later.
|
||
|
"""
|
||
|
### Jiz definovano v predkovi
|
||
|
#first_name = CharField(_('firstName'),max_length=100, default='')
|
||
|
#last_name = CharField(_('lastName'),max_length=100, default='')
|
||
|
#email = CharField(_('email'),max_length=100, default='', blank=True, null=True)
|
||
|
|
||
|
# Keycloak user ID
|
||
|
ssoUid = CharField(_(u'Keycloak SSO user ID'), max_length=40, default=None,
|
||
|
blank=True, null=True)
|
||
|
createdStamp = DateTimeField(_('Uživatel vytvořen'),
|
||
|
default=datetime.datetime.now, blank=False, null=False, editable=False)
|
||
|
# Datum a čas poslední změny údajů, které se mají propisovat do LDAP.
|
||
|
# Podle tohoto údaje se v synchronizačním skriptu pozná, jaké
|
||
|
# záznamy je potřeba aktualizovat v LDAP.
|
||
|
|
||
|
# Email login token
|
||
|
emailToken = CharField(_(u'E-mail login token'), max_length=120, default=None,
|
||
|
blank=True, null=True)
|
||
|
etStamp = DateTimeField(_('E-mail token timestamp'), editable=False,
|
||
|
default=None, blank=True, null=True)
|
||
|
|
||
|
##
|
||
|
# Constants
|
||
|
##
|
||
|
DISTRICT_PHA = 0
|
||
|
DISTRICT_JHC = 1
|
||
|
DISTRICT_JHM = 2
|
||
|
DISTRICT_KVK = 3
|
||
|
DISTRICT_VYS = 4
|
||
|
DISTRICT_KHK = 5
|
||
|
DISTRICT_LBK = 6
|
||
|
DISTRICT_MSK = 7
|
||
|
DISTRICT_OLK = 8
|
||
|
DISTRICT_PAK = 9
|
||
|
DISTRICT_PLK = 10
|
||
|
DISTRICT_STC = 11
|
||
|
DISTRICT_ULK = 12
|
||
|
DISTRICT_ZLK = 13
|
||
|
|
||
|
DISTRICT_CHOICES = (
|
||
|
(DISTRICT_PHA, 'Hlavní město Praha'),
|
||
|
(DISTRICT_JHC, 'Jihočeský kraj'),
|
||
|
(DISTRICT_JHM, 'Jihomoravský kraj'),
|
||
|
(DISTRICT_KVK, 'Karlovarský kraj'),
|
||
|
(DISTRICT_VYS, 'Kraj Vysočina'),
|
||
|
(DISTRICT_KHK, 'Královéhradecký kraj'),
|
||
|
(DISTRICT_LBK, 'Liberecký kraj'),
|
||
|
(DISTRICT_MSK, 'Moravskoslezský kraj'),
|
||
|
(DISTRICT_OLK, 'Olomoucký kraj'),
|
||
|
(DISTRICT_PAK, 'Pardubický kraj'),
|
||
|
(DISTRICT_PLK, 'Plzeňský kraj'),
|
||
|
(DISTRICT_STC, 'Středočeský kraj'),
|
||
|
(DISTRICT_ULK, 'Ústecký kraj'),
|
||
|
(DISTRICT_ZLK, 'Zlínský kraj'),
|
||
|
)
|
||
|
DISTRICT_ROLES = {
|
||
|
'sso_kraj_pha' : DISTRICT_PHA,
|
||
|
'sso_kraj_jhc' : DISTRICT_JHC,
|
||
|
'sso_kraj_jhm' : DISTRICT_JHM,
|
||
|
'sso_kraj_kvk' : DISTRICT_KVK,
|
||
|
'sso_kraj_vys' : DISTRICT_VYS,
|
||
|
'sso_kraj_khk' : DISTRICT_KHK,
|
||
|
'sso_kraj_lbk' : DISTRICT_LBK,
|
||
|
'sso_kraj_msk' : DISTRICT_MSK,
|
||
|
'sso_kraj_olk' : DISTRICT_OLK,
|
||
|
'sso_kraj_pak' : DISTRICT_PAK,
|
||
|
'sso_kraj_plk' : DISTRICT_PLK,
|
||
|
'sso_kraj_stc' : DISTRICT_STC,
|
||
|
'sso_kraj_ulk' : DISTRICT_ULK,
|
||
|
'sso_kraj_zlk' : DISTRICT_ZLK,
|
||
|
}
|
||
|
|
||
|
STATUS_NEW = 0
|
||
|
STATUS_REG = 1
|
||
|
STATUS_CHOICES = (
|
||
|
(STATUS_NEW, 'nový'),
|
||
|
(STATUS_REG, 'registrovaný'),
|
||
|
)
|
||
|
|
||
|
KIND_NEWSLETTER = 0
|
||
|
KIND_WANT_SUPPORTER = 1
|
||
|
KIND_WANT_MEMBER = 2
|
||
|
KIND_HELP_VOLUNTEER = 3
|
||
|
KIND_HELP_EXPERT = 4
|
||
|
KIND_ALREADY_MEMBER = 5
|
||
|
KIND_ALREADY_OTHER = 6
|
||
|
KIND_CHOICES = (
|
||
|
(KIND_NEWSLETTER, _('dostávat novinky')),
|
||
|
(KIND_HELP_VOLUNTEER, _('pomáhat jako dobrovolník')),
|
||
|
(KIND_HELP_EXPERT, _('pomáhat jako expert')),
|
||
|
(KIND_WANT_SUPPORTER, _('se stát příznivcem')),
|
||
|
(KIND_WANT_MEMBER, _('se stát členem')),
|
||
|
(KIND_ALREADY_MEMBER, _('již jsem člen')),
|
||
|
(KIND_ALREADY_OTHER, _('již pirátím jinak')),
|
||
|
)
|
||
|
|
||
|
##
|
||
|
# User self-editable fields
|
||
|
##
|
||
|
postcode = IntegerField(_('PSČ'), blank=False, null=True)
|
||
|
city = CharField(_(u'Město'), max_length=120, default=None,
|
||
|
blank=True, null=True)
|
||
|
district = IntegerField(_('Kraj'), blank=True, null=True,
|
||
|
choices=DISTRICT_CHOICES, default=None)
|
||
|
|
||
|
kind = IntegerField(_('Chci'), blank=False, null=False,
|
||
|
default = KIND_NEWSLETTER, choices=KIND_CHOICES)
|
||
|
# Stav KIND_MEMBER se nastavuje dle skutečného členství, a bude
|
||
|
# pravidelně aktualizován synchronizací s DB členů.
|
||
|
|
||
|
interestedIn = CharField(_('Moje dovednosti (chci pomoci s)'), blank=True, null=True,
|
||
|
max_length=150)
|
||
|
# Stav KIND_MEMBER se nastavuje dle skutečného členství, a bude
|
||
|
# pravidelně aktualizován synchronizací s DB členů.
|
||
|
|
||
|
email_contact = EmailField(_('Kontaktní e-mail'),max_length=100,
|
||
|
default='', blank=True, null=True)
|
||
|
# Kontaktni email, ktery slouzi ke skryti emailu registrovaneho na SSO
|
||
|
# a pouziteho napr. v prihlasce o clenstvi.
|
||
|
# SSO bude dalsim aplikacim, krome nalodeni, predavat pouze kontaktni
|
||
|
# email, pokud bude nastaven.
|
||
|
|
||
|
email_contact_token = CharField(_('Ověřovací token pro kontaktní e-mail.'),
|
||
|
max_length=150, default=None, blank=True, null=True)
|
||
|
# Token pro ověření kontaktního emailu. Má tvat timestamp - token,
|
||
|
# kde timestamp se neposílá emailem, ale slouží pro vypršení platnosti.
|
||
|
|
||
|
email_contact_verified = BooleanField(_('Kontaktní e-mail byl ověřen.'),
|
||
|
default=False, blank=True)
|
||
|
# Kontaktni email je potřeba ověřit zaslání ověřovacího emailu.
|
||
|
|
||
|
email_contact_active = EmailField(_('Kontaktní e-mail aktivní'),max_length=100,
|
||
|
default='', blank=True, null=True)
|
||
|
# Kontaktni email, který je ověřen a zapsán v LDAP.
|
||
|
# pole je vyplňováno při přihlášení z SSO.
|
||
|
|
||
|
ts_for_ldap_sync = DateTimeField(_('Timestamp pro LDAP synchronizaci'),
|
||
|
default=datetime.datetime.now, blank=True, null=True)
|
||
|
# Datum a čas poslední změny údajů, které se mají propisovat do LDAP.
|
||
|
# Podle tohoto údaje se v synchronizačním skriptu pozná, jaké
|
||
|
# záznamy je potřeba aktualizovat v LDAP.
|
||
|
|
||
|
dc_stamp = DateTimeField(_('Data consent timestamp'),
|
||
|
default=None, blank=True, null=True)
|
||
|
|
||
|
|
||
|
# dotaznik pro uzivatele
|
||
|
userform = ForeignKey('UserForm', on_delete=CASCADE, verbose_name=_('dotazník'),
|
||
|
blank=True, null=True, default=None)
|
||
|
|
||
|
##
|
||
|
# Other fields
|
||
|
##
|
||
|
status = IntegerField(_('Stav'), blank=False, null=False,
|
||
|
default = STATUS_NEW, choices=STATUS_CHOICES)
|
||
|
|
||
|
|
||
|
##
|
||
|
# pro kompatibilitu kodu a modelu
|
||
|
@property
|
||
|
def firstName(self):
|
||
|
return self.first_name
|
||
|
|
||
|
@firstName.setter
|
||
|
def firstName(self,value):
|
||
|
self.first_name = value
|
||
|
|
||
|
@property
|
||
|
def lastName(self):
|
||
|
return self.last_name
|
||
|
|
||
|
@lastName.setter
|
||
|
def lastName(self, value):
|
||
|
self.last_name = value
|
||
|
#
|
||
|
##
|
||
|
|
||
|
##
|
||
|
# Ukladani a nacitani nastaveni
|
||
|
|
||
|
# consent_gdpr
|
||
|
@property
|
||
|
def consent_gdpr(self):
|
||
|
return readUserSetting(self, 'consent_gdpr', None, datetime.date)
|
||
|
@consent_gdpr.setter
|
||
|
def consent_gdpr(self, value):
|
||
|
writeUserSetting(self, 'consent_gdpr', value, datetime.date)
|
||
|
|
||
|
|
||
|
# consent_cookies
|
||
|
@property
|
||
|
def consent_cookies(self):
|
||
|
return readUserSetting(self, 'consent_cookies', None, datetime.date)
|
||
|
@consent_cookies.setter
|
||
|
def consent_cookies(self, value):
|
||
|
writeUserSetting(self, 'consent_cookies', value, datetime.date)
|
||
|
|
||
|
|
||
|
# consent_terms
|
||
|
@property
|
||
|
def consent_terms(self):
|
||
|
return readUserSetting(self, 'consent_terms', None, datetime.date)
|
||
|
@consent_terms.setter
|
||
|
def consent_terms(self, value):
|
||
|
writeUserSetting(self, 'consent_terms', value, datetime.date)
|
||
|
|
||
|
# Konec
|
||
|
##
|
||
|
|
||
|
|
||
|
##
|
||
|
# Meta information
|
||
|
##
|
||
|
def __str__(self):
|
||
|
return self.username
|
||
|
|
||
|
if self.ssoUid:
|
||
|
return "%s %s" % (self.first_name, self.last_name)
|
||
|
else:
|
||
|
return self.email
|
||
|
|
||
|
|
||
|
class Meta:
|
||
|
verbose_name = _('AppUser')
|
||
|
verbose_name_plural = _('AppUsers')
|
||
|
unique_together = (("email", ),("username",), ("emailToken",),)
|
||
|
ordering = ('username',)
|
||
|
|
||
|
##
|
||
|
# Permission functions
|
||
|
##
|
||
|
def do_site_perms_calc(self, site_perms):
|
||
|
'''Precalculate needed permissions:
|
||
|
- district IDs for the given roles
|
||
|
'''
|
||
|
spc = {}
|
||
|
|
||
|
rslt = []
|
||
|
for p in self.DISTRICT_ROLES:
|
||
|
if p in site_perms:
|
||
|
# append the ID of the choice
|
||
|
rslt.append(self.DISTRICT_ROLES[p])
|
||
|
|
||
|
spc['dist'] = rslt
|
||
|
|
||
|
return spc
|
||
|
|
||
|
"""
|
||
|
|
||
|
from django.db.models import Count
|
||
|
from django.db.models.functions import TruncMonth
|
||
|
from django.db.models.functions import TruncYear
|
||
|
|
||
|
import nalodeni as n
|
||
|
|
||
|
objs = n.models.USER_MODEL.objects.annotate( month=TruncMonth('createdStamp')).values('month', 'district').annotate(c=Count('id')).order_by('district','month')
|
||
|
|
||
|
district_choices = {
|
||
|
0 : 'PHA',
|
||
|
1 : 'JHC',
|
||
|
2 : 'JHM',
|
||
|
3 : 'KVK',
|
||
|
4 : 'VYS',
|
||
|
5 : 'KHK',
|
||
|
6 : 'LBK',
|
||
|
7 : 'MSK',
|
||
|
8 : 'OLK',
|
||
|
9 : 'PAK',
|
||
|
10 : 'PLK',
|
||
|
11 : 'STC',
|
||
|
12 : 'ULK',
|
||
|
13 : 'ZLK',
|
||
|
}
|
||
|
|
||
|
for o in objs:
|
||
|
print( "%s \t %s \t %s" % (district_choices[o['district']] if o['district'] is not None else "---", o['month'].strftime('%Y-%m'),o['c']))
|
||
|
|
||
|
"""
|
||
|
|
||
|
|
||
|
|
||
|
USER_MODEL = AppUser
|
||
|
#USER_MODEL = get_user_model()
|
||
|
|
||
|
class InterestRegion(Model):
|
||
|
"""
|
||
|
Dostupné emailové zdroje informací.
|
||
|
"""
|
||
|
name = CharField(_('Název'), blank=True, null=True,
|
||
|
max_length=100)
|
||
|
tag = CharField(_('Značka (tag) newsletteru'), blank=True, null=True,
|
||
|
max_length=20)
|
||
|
|
||
|
def __str__(self):
|
||
|
return self.name
|
||
|
|
||
|
class Meta:
|
||
|
ordering = ['name',]
|
||
|
unique_together = ( ('tag',),)
|
||
|
|
||
|
class UserSkill(Model):
|
||
|
"""
|
||
|
Dovednosti, které může mít daný uživatel.
|
||
|
"""
|
||
|
name = CharField(_('Název'), blank=True, null=True,
|
||
|
max_length=100)
|
||
|
tag = CharField(_('Značka (tag) dovednosti'), blank=True, null=True,
|
||
|
max_length=20)
|
||
|
|
||
|
def __str__(self):
|
||
|
return self.name
|
||
|
|
||
|
class Meta:
|
||
|
ordering = ['tag',]
|
||
|
unique_together = ( ('tag',),)
|
||
|
|
||
|
class UserTopic(Model):
|
||
|
"""
|
||
|
Zájmová témata daného uživatele.
|
||
|
"""
|
||
|
name = CharField(_('Název'), blank=True, null=True,
|
||
|
max_length=100)
|
||
|
tag = CharField(_('Značka (tag) tématu'), blank=True, null=True,
|
||
|
max_length=20)
|
||
|
|
||
|
def __str__(self):
|
||
|
return self.name
|
||
|
|
||
|
class Meta:
|
||
|
ordering = ['name',]
|
||
|
unique_together = ( ('tag',),)
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
class UserForm(Model):
|
||
|
"""
|
||
|
Dotaznik ohledne dovednosti a schopnosti uzivatele.
|
||
|
"""
|
||
|
skills = ManyToManyField(UserSkill, blank=True, verbose_name=_('Dovednosti'))
|
||
|
skills_note = CharField(_('Poznámka k dovednostem'),max_length=250,
|
||
|
default=None, blank=True, null=True)
|
||
|
|
||
|
topics = ManyToManyField(UserTopic, blank=True, verbose_name=_('Zájmová témata'))
|
||
|
regions = ManyToManyField(InterestRegion, blank=True, verbose_name=_('Zájmové regiony'))
|
||
|
|
||
|
|
||
|
class AppRegEmail(Model):
|
||
|
"""
|
||
|
Pozadavky na registraci emailu.
|
||
|
"""
|
||
|
createdStamp = DateTimeField(_('Uživatel vytvořen'),
|
||
|
default=datetime.datetime.now, blank=False, null=False, editable=False)
|
||
|
|
||
|
email = CharField(_('e-mail'),max_length=100, default='', blank=True, null=True)
|
||
|
postcode = IntegerField(_('PSČ (kvůli dělení do krajů)'), blank=False, null=True)
|
||
|
|
||
|
kind = IntegerField(_('Chci'), blank=True, null=True,
|
||
|
default = AppUser.KIND_NEWSLETTER, choices=AppUser.KIND_CHOICES)
|
||
|
# Stav KIND_MEMBER se nastavuje dle skutečného členství, a bude
|
||
|
# pravidelně aktualizován synchronizací s DB členů.
|
||
|
|
||
|
interestedIn = CharField(_('Poznámka (info pro koordinátora)'), blank=True, null=True,
|
||
|
max_length=100)
|
||
|
|
||
|
data_consent = BooleanField(_('Souhlasím se zpracováním osobních údajů.'),
|
||
|
default=False, blank=False, null=False)
|
||
|
# Souhlas se zpracováním os. údajů
|
||
|
dc_stamp = DateTimeField(_('Data consent timestamp'), editable=False,
|
||
|
default=None, blank=True, null=True)
|
||
|
|
||
|
|
||
|
# registration token
|
||
|
emailToken = CharField(_(u'Registration token'), max_length=120, default=None,
|
||
|
blank=True, null=True)
|
||
|
etStamp = DateTimeField(_('Registration token timestamp'), editable=False,
|
||
|
default=None, blank=True, null=True)
|
||
|
|
||
|
userform = ForeignKey(UserForm, on_delete=CASCADE, verbose_name=_('dotazník'),
|
||
|
blank=True, null=True, default=None)
|
||
|
|
||
|
class Meta:
|
||
|
verbose_name = _('AppRegEmail')
|
||
|
verbose_name_plural = _('AppRegEmails')
|
||
|
unique_together = (("emailToken",),("email",))
|
||
|
|
||
|
|
||
|
class Euro2019Interest(Model):
|
||
|
"""
|
||
|
Zajemci o pomoc ve volbach 2019.
|
||
|
"""
|
||
|
createdStamp = DateTimeField(_('Uživatel vytvořen'),
|
||
|
default=datetime.datetime.now, blank=False, null=False, editable=False)
|
||
|
|
||
|
email = CharField(_('e-mail'),max_length=100, blank=False, null=True)
|
||
|
district = IntegerField(_('Kraj'), blank=True, null=True,
|
||
|
choices=AppUser.DISTRICT_CHOICES, default=None)
|
||
|
|
||
|
postcode = IntegerField(_('PSČ (kvůli dělení do krajů)'), blank=False, null=True)
|
||
|
|
||
|
want_info = BooleanField(_('Chci dostávat informace e-mailem'),
|
||
|
default=True, blank=False, null=False)
|
||
|
|
||
|
want_campaign = BooleanField(_('Chci rozdávat Pirátské listy'),
|
||
|
default=False, blank=False, null=False)
|
||
|
|
||
|
want_be_active = BooleanField(_('Chci se podílet chodu kampaně'),
|
||
|
default=False, blank=False, null=False)
|
||
|
|
||
|
note = CharField(_('Poznámka (info pro koordinátora)'), blank=True, null=True,
|
||
|
max_length=100)
|
||
|
|
||
|
data_consent = BooleanField(_('Souhlasím se zpracováním osobních údajů'),
|
||
|
default=False, blank=False, null=False)
|
||
|
# Souhlas se zpracováním os. údajů
|
||
|
dc_stamp = DateTimeField(_('Data consent timestamp'), editable=False,
|
||
|
default=None, blank=True, null=True)
|
||
|
|
||
|
|
||
|
class Meta:
|
||
|
unique_together = (("email",))
|
||
|
|
||
|
|
||
|
|
||
|
def get_user_by_keycloak_email(id):
|
||
|
"""
|
||
|
Funkce pro dohledani uzivatele podle dat ze SSO serveru.
|
||
|
"aud" a "azp" jsou client-id dle SSO serveru.
|
||
|
|
||
|
Struktura id :
|
||
|
{
|
||
|
'jti': '0d5be61a-fccc-49ce-b82d-78fe53eec7ac'
|
||
|
'exp': 1505657690
|
||
|
'nbf': 0
|
||
|
'iat': 1505657390
|
||
|
'iss': 'http://localhost:8080/auth/realms/nga'
|
||
|
'aud': 'nalodeni'
|
||
|
'sub': 'db831083-7e1e-42ee-9682-2ff85021fcbc'
|
||
|
'typ': 'ID'
|
||
|
'azp': 'nalodeni'
|
||
|
'auth_time': 1505657362
|
||
|
'session_state': '73b44047-b86b-429b-b21f-800b2bc0d33f'
|
||
|
'acr': '0'
|
||
|
'name': 'Martin Rejman'
|
||
|
'preferred_username': 'mr'
|
||
|
'given_name': 'Martin'
|
||
|
'family_name': 'Rejman'
|
||
|
'email': 'martin.rejman@centrum.cz'
|
||
|
}
|
||
|
"""
|
||
|
# check presence of required attributes
|
||
|
if not 'sub' in id:
|
||
|
raise sso_exc.MissingSsoInfo(_('SSO server nepředal identifikaci uživatele.'))
|
||
|
if not 'preferred_username' in id:
|
||
|
raise sso_exc.MissingSsoInfo(_('SSO server nepředal uživatelské jméno.'))
|
||
|
if not 'email' in id:
|
||
|
raise sso_exc.MissingSsoInfo(_('SSO server nepředal email uživatele.'))
|
||
|
if not 'given_name' in id:
|
||
|
raise sso_exc.MissingSsoInfo(_('SSO server nepředal křestní jméno uživatele.'))
|
||
|
if not 'family_name' in id:
|
||
|
raise sso_exc.MissingSsoInfo(_('SSO server nepředal příjmení uživatele.'))
|
||
|
|
||
|
id_email = id['email'].lower().strip()
|
||
|
if 'email_contact' in id:
|
||
|
id_email_contact_active = id['email_contact'].lower().strip()
|
||
|
else:
|
||
|
id_email_contact_active = None
|
||
|
|
||
|
# get user by SSO id
|
||
|
sso_users = USER_MODEL.objects.filter(ssoUid=id['sub'])
|
||
|
|
||
|
if len(sso_users) == 0:
|
||
|
# user unknown in Django
|
||
|
email_users = USER_MODEL.objects.filter(email=id_email)
|
||
|
|
||
|
if len(email_users) == 0:
|
||
|
if len(id['preferred_username']) > 30: # Django User username is 30 character limited
|
||
|
raise sso_exc.MissingSsoInfo(_('Uživatelské jméno je příliš dlouhé.'))
|
||
|
|
||
|
usernames_found = USER_MODEL.objects.filter(username=id['preferred_username'])
|
||
|
if len(usernames_found) > 0:
|
||
|
raise sso_exc.UsernameAlreadyTaken( _('Uživatelské jméno %s je '
|
||
|
+ 'již obsazeno.') % id['preferred_username'])
|
||
|
|
||
|
user = USER_MODEL()
|
||
|
user.ssoUid = id['sub']
|
||
|
user.email = id_email
|
||
|
user.email_contact_active = id_email_contact_active
|
||
|
user.username = id['preferred_username']
|
||
|
user.first_name = id['given_name']
|
||
|
user.last_name = id['family_name']
|
||
|
user.save()
|
||
|
|
||
|
elif len(email_users) == 1:
|
||
|
user = email_users[0]
|
||
|
|
||
|
if user.ssoUid == None:
|
||
|
user.ssoUid = id['sub']
|
||
|
else:
|
||
|
# email souhlasí, ale ssoUid nikoliv
|
||
|
raise sso_exc.EmailVersusIdentityMismatch(
|
||
|
_('Účet nalodění s tímto emailem (%s, %s) ' +
|
||
|
'je již napárován na jinou identitu (%s) na Pirátské identitě.')
|
||
|
% ( user.email, user.ssoUid, id['sub'] )
|
||
|
)
|
||
|
|
||
|
#check duplicity of the new username
|
||
|
if len(id['preferred_username']) > 30: # Django User username is 30 character limited
|
||
|
raise sso_exc.MissingSsoInfo(_('Uživatelské jméno je příliš dlouhé.'))
|
||
|
rslt = USER_MODEL.objects.filter(username=id['preferred_username'])
|
||
|
if len(rslt) > 0:
|
||
|
raise sso_exc.UsernameAlreadyTaken( _('Uživatelské jméno %s je '
|
||
|
+ 'již obsazeno.') % id['preferred_username'])
|
||
|
|
||
|
user.username = id['preferred_username']
|
||
|
user.first_name = id['given_name']
|
||
|
user.last_name = id['family_name']
|
||
|
user.email_contact_active = id_email_contact_active
|
||
|
|
||
|
# reset email login attempts
|
||
|
user.emailToken = None
|
||
|
user.etStamp = None
|
||
|
|
||
|
user.save()
|
||
|
|
||
|
elif len(email_users) > 1:
|
||
|
raise Exception("More users with one email value.")
|
||
|
|
||
|
elif len(sso_users) == 1:
|
||
|
# user known
|
||
|
user = sso_users[0]
|
||
|
|
||
|
|
||
|
|
||
|
changed = False
|
||
|
if user.username != id['preferred_username']:
|
||
|
#check duplicity of the new values (username, email)
|
||
|
if len(id['preferred_username']) > 30: # Django User username is 30 character limited
|
||
|
raise sso_exc.MissingSsoInfo(_('Uživatelské jméno je příliš dlouhé.'))
|
||
|
rslt = USER_MODEL.objects.filter(username=id['preferred_username'])
|
||
|
if len(rslt) > 0:
|
||
|
raise sso_exc.UsernameAlreadyTaken( _('Uživatelské jméno %s je '
|
||
|
+ 'již obsazeno.') % id['preferred_username'])
|
||
|
user.username = id['preferred_username']
|
||
|
changed = True
|
||
|
if user.first_name != id['given_name']:
|
||
|
user.first_name = id['given_name']
|
||
|
changed = True
|
||
|
if user.last_name != id['family_name']:
|
||
|
user.last_name = id['family_name']
|
||
|
changed = True
|
||
|
if user.email != id_email:
|
||
|
# check duplicity
|
||
|
rslt = USER_MODEL.objects.filter(email=id_email)
|
||
|
if len(rslt) > 0:
|
||
|
raise sso_exc.EmailAlreadyTaken( _('Email %s je '
|
||
|
+ 'již obsazen jiným účtem.') % id_email)
|
||
|
user.email = id_email
|
||
|
changed = True
|
||
|
if user.email_contact_active != id_email_contact_active:
|
||
|
user.email_contact_active = id_email_contact_active
|
||
|
changed = True
|
||
|
if changed:
|
||
|
user.save()
|
||
|
|
||
|
else:
|
||
|
raise Exception("More users with one sso-uid value.")
|
||
|
|
||
|
return user
|
||
|
|
||
|
|
||
|
class Newsletter(Model):
|
||
|
"""
|
||
|
Záznam o newsletteru, který se chce zasílat.
|
||
|
"""
|
||
|
is_del = BooleanField(_('Smazaný'),
|
||
|
default=False, blank=False, null=False, editable=False)
|
||
|
|
||
|
name = CharField(_('Název'), blank=True, null=True,
|
||
|
max_length=150)
|
||
|
|
||
|
desc = CharField(_('Popis'), blank=True, null=True,
|
||
|
max_length=500)
|
||
|
PER_WEEK = 0
|
||
|
PER_MONTH = 1
|
||
|
PER_IRR = 2
|
||
|
PER_CHOICES = (
|
||
|
(PER_WEEK, _('Týdně')),
|
||
|
(PER_MONTH, _('Měsíčně')),
|
||
|
(PER_IRR, _('Nepravidelně')),
|
||
|
)
|
||
|
period = IntegerField(_('Perioda zasílání'),
|
||
|
blank=False, null=False, default=PER_MONTH, choices=PER_CHOICES)
|
||
|
|
||
|
|
||
|
# Kdo může newsletter nastavovat
|
||
|
managed_by = ForeignKey(USER_MODEL, models.CASCADE, verbose_name=_('Správce newsletteru'),
|
||
|
related_name="newsletter_managed")
|
||
|
|
||
|
# Kdo může pomocí tohoto newsletteru odesílat zprávy
|
||
|
sent_by = ManyToManyField(USER_MODEL, blank=True, verbose_name=_('Odesílatelé'),
|
||
|
related_name="newsletter_sent")
|
||
|
|
||
|
enabled = BooleanField(_('Aktivní'),
|
||
|
default=False, blank=False, null=False)
|
||
|
|
||
|
recipients = CharField(_('Příjemci (pomocí tagů)'), blank=True, null=True,
|
||
|
max_length=500)
|
||
|
|
||
|
replyToEmail = CharField(_('E-mailová adresa pro odpovědi'),
|
||
|
blank=True, null=True, max_length=150)
|
||
|
|
||
|
|
||
|
class Meta:
|
||
|
ordering = ("-enabled", "name",)
|
||
|
|
||
|
def __str__(self):
|
||
|
return self.name
|
||
|
|
||
|
def get_recip_users(self):
|
||
|
"""
|
||
|
Parse self.recipients and return a list of matching users.
|
||
|
"""
|
||
|
all_users = USER_MODEL.objects.none()
|
||
|
|
||
|
if self.recipients is None:
|
||
|
return all_users
|
||
|
|
||
|
for group in self.recipients.strip().replace('\n',' ').split('*'):
|
||
|
group = group.strip()
|
||
|
|
||
|
if group == "":
|
||
|
continue
|
||
|
|
||
|
topic_ids = []
|
||
|
region_ids = []
|
||
|
skill_ids = []
|
||
|
|
||
|
#print('* ')
|
||
|
for item in group.split(" "):
|
||
|
item = item.strip()
|
||
|
|
||
|
if item == "":
|
||
|
continue
|
||
|
|
||
|
#print(item)
|
||
|
if item[0] == "s":
|
||
|
rslt = UserSkill.objects.filter(tag=item[2:])
|
||
|
if len(rslt) == 1:
|
||
|
skill_ids.append(rslt[0].id)
|
||
|
elif item[0] == "r":
|
||
|
rslt = InterestRegion.objects.filter(tag=item[2:])
|
||
|
if len(rslt) == 1:
|
||
|
region_ids.append(rslt[0].id)
|
||
|
elif item[0] == "t":
|
||
|
rslt = UserTopic.objects.filter(tag=item[2:])
|
||
|
if len(rslt) == 1:
|
||
|
topic_ids.append(rslt[0].id)
|
||
|
|
||
|
#print(topic_ids, region_ids, skill_ids)
|
||
|
rslt_u = AppUser.objects.all()
|
||
|
if len(topic_ids) > 0:
|
||
|
rslt_u = rslt_u.filter(userform__topics__in = topic_ids)
|
||
|
if len(skill_ids) > 0:
|
||
|
rslt_u = rslt_u.filter(userform__skills__in = skill_ids)
|
||
|
if len(region_ids) > 0:
|
||
|
rslt_u = rslt_u.filter(userform__regions__in = region_ids)
|
||
|
|
||
|
#print(rslt_u)
|
||
|
all_users = all_users | rslt_u
|
||
|
|
||
|
# use distinct because ORM _will_ multiply the results
|
||
|
return all_users.distinct()
|
||
|
|
||
|
def get_recip_users_count(self):
|
||
|
return len(self.get_recip_users())
|
||
|
|
||
|
|
||
|
|
||
|
class NewsCond(Model):
|
||
|
"""
|
||
|
Podmínky pro filtrování uživatelů, kterým se má newsletter odesílat.
|
||
|
|
||
|
Podmínky v rámci skupiny se spojují operátorem AND, skupiny jako celek
|
||
|
jsou pak spojeny operátorem OR.
|
||
|
"""
|
||
|
|
||
|
news = ForeignKey(Newsletter, models.CASCADE, verbose_name=_('Newsletter'))
|
||
|
|
||
|
group = IntegerField(_('Skupina podmínek'),
|
||
|
default=False, blank=False, null=False)
|
||
|
neg = BooleanField(_('Negovat podmínku'),
|
||
|
default=False, blank=False, null=False)
|
||
|
|
||
|
KIND_SKILL = 0
|
||
|
KIND_TOPIC = 1
|
||
|
KIND_REGION = 2
|
||
|
KIND_CHOICES = (
|
||
|
(KIND_SKILL, _('Dovednost')),
|
||
|
(KIND_TOPIC, _('Téma')),
|
||
|
(KIND_REGION, _('Území')),
|
||
|
)
|
||
|
kind = IntegerField(_('Druh'),
|
||
|
blank=False, null=False, default=KIND_SKILL, choices=KIND_CHOICES)
|
||
|
opt_id = IntegerField(_('ID vybrané možnosti'),
|
||
|
blank=False, null=False)
|
||
|
|
||
|
|
||
|
class NewsMsg(Model):
|
||
|
"""
|
||
|
Jedna konkrétní zpráva k rozeslání, skládá se z bloků, které tvoří její obsah.
|
||
|
Uživatelé mohou reagovat na NewsMsg pomocí rozhraní nad NewsMsgReply.
|
||
|
"""
|
||
|
is_del = BooleanField(_('Smazaný'),
|
||
|
default=False, blank=False, null=False, editable=False)
|
||
|
|
||
|
news = ForeignKey(Newsletter, models.CASCADE, verbose_name=_('Newsletter'))
|
||
|
|
||
|
created_by = ForeignKey(USER_MODEL, models.CASCADE, verbose_name=_('Vytvořil'))
|
||
|
|
||
|
created_ts = DateTimeField(_('Datum vytvoření'),
|
||
|
default=datetime.datetime.now, blank=False, null=False, editable=False)
|
||
|
delivery_ts = DateTimeField(_('Datum plánovaného rozeslání'),
|
||
|
blank=True, null=True, editable=True)
|
||
|
sent_ts = DateTimeField(_('Datum skutečného rozeslání'),
|
||
|
blank=True, null=True, editable=True)
|
||
|
|
||
|
title = CharField(_('Název, předmět emailu'), blank=False, null=True,
|
||
|
max_length=150)
|
||
|
|
||
|
headerText = TextField(_('Hlavička'), blank=True, null=True)
|
||
|
footerText = TextField(_('Patička'), blank=True, null=True)
|
||
|
|
||
|
testMailRecipients = CharField(_('Přidaní adresáti testovacího emailu'),
|
||
|
blank=True, null=True, max_length=150)
|
||
|
|
||
|
|
||
|
def __str__(self):
|
||
|
return self.title
|
||
|
|
||
|
|
||
|
class NewsMsgBlock(Model):
|
||
|
"""
|
||
|
Jeden blok newsletteru, ze kterých se skládá celá zpráva.
|
||
|
"""
|
||
|
|
||
|
newsmsg = ForeignKey(NewsMsg, models.CASCADE, verbose_name=_('Zpráva'))
|
||
|
order = IntegerField(_('Pořadí'),
|
||
|
default=False, blank=False, null=False)
|
||
|
|
||
|
heading = CharField(_('Nadpis'), max_length=150,
|
||
|
blank=True, null=True)
|
||
|
content = TextField(_('Text (i validované HTML)'),
|
||
|
blank=True, null=True)
|
||
|
|
||
|
link_text = CharField(_('Text odkazu'), max_length=100,
|
||
|
blank=True, null=True)
|
||
|
link = CharField(_('Odkaz'), max_length=500,
|
||
|
blank=True, null=True)
|
||
|
|
||
|
# časem je asi možné obrázky nahrávat přímo do nalodění
|
||
|
img_thumb_url = CharField(_('Obrázek náhledu (URL)'), max_length=500,
|
||
|
blank=True, null=True)
|
||
|
img_url = CharField(_('Obrázek (URL)'), max_length=500,
|
||
|
blank=True, null=True)
|
||
|
img_label = CharField(_('Popis obrázku'), max_length=100,
|
||
|
blank=True, null=True)
|
||
|
|
||
|
class Meta:
|
||
|
ordering = ['order', 'heading']
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
class NewsMsgReply(Model):
|
||
|
"""
|
||
|
Reakce ctenaru na dany informacni blok.
|
||
|
"""
|
||
|
|
||
|
block = ForeignKey(NewsMsgBlock, models.CASCADE, verbose_name=_('Část zprávy'))
|
||
|
user = ForeignKey(USER_MODEL, models.CASCADE, verbose_name=_('Vytvořil'))
|
||
|
|
||
|
comment = CharField(_('Poznámka'), max_length=250,
|
||
|
blank=True, null=True)
|
||
|
|
||
|
rating_usefullness = IntegerField(_('Využiji to'),
|
||
|
default=0, blank=True, null=True)
|
||
|
|
||
|
rating_interest = IntegerField(_('Zajímá mě to'),
|
||
|
default=0, blank=True, null=True)
|
||
|
|
||
|
rating_action = IntegerField(_('Zapojím se / Pomůžu'),
|
||
|
default=0, blank=True, null=True)
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
## .......+++++++......... ##
|
||
|
# User settings #
|
||
|
# #
|
||
|
class UserSetting(Model):
|
||
|
"""
|
||
|
Different settings and values stored for global purpose.
|
||
|
"""
|
||
|
|
||
|
user = ForeignKey(USER_MODEL, models.CASCADE, verbose_name=_('user'))
|
||
|
key = CharField(_("Key"), max_length=64)
|
||
|
value = CharField(_("Value"), max_length=128, null=True)
|
||
|
|
||
|
def __str__(self):
|
||
|
return str(self.key)
|
||
|
|
||
|
class Meta:
|
||
|
unique_together = (( 'user','key', ),)
|
||
|
|
||
|
|
||
|
def readUserSetting(user, key, default, dataType):
|
||
|
"""
|
||
|
Reads user settings for the current SITE_ID.
|
||
|
|
||
|
Returns 'default' value if the setting is not present
|
||
|
or the value is None.
|
||
|
"""
|
||
|
obj = UserSetting.objects.filter(key=key,user=user)
|
||
|
if len(obj) == 0:
|
||
|
return default
|
||
|
else:
|
||
|
val = obj[0].value
|
||
|
|
||
|
if val is None:
|
||
|
return default
|
||
|
|
||
|
if dataType == datetime.date:
|
||
|
return datetime.datetime.strptime(val,'%Y-%m-%d').date()
|
||
|
elif dataType == bool:
|
||
|
return val == 'T'
|
||
|
else:
|
||
|
return dataType(val)
|
||
|
|
||
|
def writeUserSetting(user, key, value, dataType):
|
||
|
"""
|
||
|
Saves a user setting for the current SITE_ID.
|
||
|
"""
|
||
|
obj, created = UserSetting.objects.get_or_create(key=key,user=user)
|
||
|
|
||
|
# modify
|
||
|
if value is not None:
|
||
|
if dataType == bool:
|
||
|
value = 'T' if value else 'F'
|
||
|
elif dataType == datetime.date:
|
||
|
value = value.strftime('%Y-%m-%d')
|
||
|
|
||
|
# save modified value, or the original one,
|
||
|
# or None if value is None
|
||
|
obj.value = value
|
||
|
obj.save()
|
||
|
|
||
|
|
||
|
# #
|
||
|
# End user settings #
|
||
|
## .......+++++++......... ##
|
||
|
|
||
|
|
||
|
## .......+++++++......... ##
|
||
|
# GLOBAL APP SETTINGS #
|
||
|
# #
|
||
|
class AppSetting(Model):
|
||
|
"""
|
||
|
Different settings and values stored for global purpose.
|
||
|
"""
|
||
|
|
||
|
key = CharField(_("Key"), max_length=64)
|
||
|
# not null ... because we agree "" is null
|
||
|
value = CharField(_("Value"), max_length=128)
|
||
|
|
||
|
def __str__(self):
|
||
|
return self.key
|
||
|
|
||
|
class Meta:
|
||
|
unique_together = (( 'key', ),)
|
||
|
|
||
|
|
||
|
def readAppSetting(key, default=None):
|
||
|
try:
|
||
|
obj = AppSetting.objects.get(key=key)
|
||
|
if obj.value == "":
|
||
|
return default
|
||
|
else:
|
||
|
return obj.value
|
||
|
except:
|
||
|
return default
|
||
|
|
||
|
def writeAppSetting(key, value):
|
||
|
obj, created = AppSetting.objects.get_or_create(key=key)
|
||
|
|
||
|
if created:
|
||
|
pass
|
||
|
# means you have created a new person
|
||
|
else:
|
||
|
pass
|
||
|
# person just refers to the existing one
|
||
|
if value is not None:
|
||
|
obj.value = str(value)
|
||
|
else:
|
||
|
obj.value = ''
|
||
|
obj.save()
|
||
|
|
||
|
|
||
|
# #
|
||
|
# END GLOBAL SETTINGS #
|
||
|
## .......+++++++......... ##
|
||
|
|
||
|
|