Presun z predchoziho repo
This commit is contained in:
commit
c1093a54fe
|
@ -0,0 +1,18 @@
|
||||||
|
# git ls-files --others --exclude-from=.git/info/exclude
|
||||||
|
# Lines that start with '#' are comments.
|
||||||
|
# For a project mostly in C, the following would be a good set of
|
||||||
|
# exclude patterns (uncomment them if you want to use them):
|
||||||
|
# *.[oa]
|
||||||
|
# *~
|
||||||
|
|
||||||
|
local_python
|
||||||
|
lopy
|
||||||
|
static_files
|
||||||
|
log_files
|
||||||
|
venv
|
||||||
|
|
||||||
|
*.sw[op]
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
settings_local.py
|
||||||
|
docker-compose.yml
|
|
@ -0,0 +1,45 @@
|
||||||
|
virtualenv -p python3.5 venv
|
||||||
|
|
||||||
|
source ./venv/bin/activate
|
||||||
|
|
||||||
|
pip install Django==2.0.3 # 2.0.3
|
||||||
|
|
||||||
|
# s binarne distribuovanou libssl
|
||||||
|
pip install psycopg2
|
||||||
|
|
||||||
|
pip install django-statici18n # preklad Javascript souboru
|
||||||
|
|
||||||
|
# nahrada za SUDS, zalozeno na lxml
|
||||||
|
#pip install zeep
|
||||||
|
|
||||||
|
pip install bleach
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# Keycloak SSO
|
||||||
|
##
|
||||||
|
|
||||||
|
#pip install git+https://github.com/jhuapl-boss/django-oidc.git
|
||||||
|
#pip install git+https://github.com/jhuapl-boss/drf-oidc-auth.git
|
||||||
|
#pip install git+https://github.com/jhuapl-boss/boss-oidc.git
|
||||||
|
|
||||||
|
pip install -U django-auth-oidc pyjwkest
|
||||||
|
#pip install -U service_identity
|
||||||
|
#pip install -U openid_connect
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# Database setup
|
||||||
|
##
|
||||||
|
psql <<SQL
|
||||||
|
CREATE USER piratinalodeni;
|
||||||
|
CREATE DATABASE piratinalodeni OWNER=piratinalodeni ENCODING='UTF8' LC_COLLATE='cs_CZ.UTF-8' template template0;
|
||||||
|
SQL
|
||||||
|
|
||||||
|
##
|
||||||
|
# App Setup
|
||||||
|
##
|
||||||
|
./src/manage.py makemigrations nalodeni
|
||||||
|
./src/manage.py migrate
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
nalodeni.env
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
FROM debian:stretch
|
||||||
|
#FROM python:3
|
||||||
|
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
WORKDIR /
|
||||||
|
|
||||||
|
RUN apt-get update
|
||||||
|
|
||||||
|
RUN apt-get install -y python-virtualenv python-pip
|
||||||
|
|
||||||
|
RUN virtualenv -p python3 venv
|
||||||
|
|
||||||
|
# ponechano kvuli vyuziti cache
|
||||||
|
#RUN bash -c 'source /venv/bin/activate; pip install -r pip-requirements.txt'
|
||||||
|
RUN bash -c 'source /venv/bin/activate; pip install Django==2.0.3 psycopg2==2.7.4 django-statici18n==1.7.0 django-auth-oidc==0.4.5 pyjwkest==1.4.0 bleach==2.1.4 django-anymail==3.0 html2text==2018.1.9'
|
||||||
|
|
||||||
|
ADD . /nalodeni/src/
|
||||||
|
|
||||||
|
#RUN bash -c 'touch /nalodeni/src/main/settings_local.py' # this file needs to exist
|
||||||
|
#RUN adduser --disabled-login --quiet --gecos nalodeni nalodeni
|
||||||
|
#RUN chown -R nalodeni:nalodeni /nalodeni/
|
||||||
|
#RUN chmod u+x /nalodeni/src/docker-entrypoint.sh
|
||||||
|
RUN bash -c 'adduser --disabled-login --quiet --gecos nalodeni nalodeni && \
|
||||||
|
chmod -R o+r /nalodeni/ && \
|
||||||
|
chown -R nalodeni:nalodeni /nalodeni/src/static_files && \
|
||||||
|
chmod o+x /nalodeni/src/docker-entrypoint.sh && \
|
||||||
|
touch /nalodeni/src/main/settings_local.py '
|
||||||
|
|
||||||
|
USER nalodeni
|
||||||
|
|
||||||
|
ENTRYPOINT /nalodeni/src/docker-entrypoint.sh
|
|
@ -0,0 +1,22 @@
|
||||||
|
upstream nalodeni.pirati.io {
|
||||||
|
ip_hash;
|
||||||
|
server nalodeni:8000;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
server_name nalodeni.pirati.io;
|
||||||
|
listen 8001;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://nalodeni.pirati.io/;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Forwarded-Proto https;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /static/ {
|
||||||
|
alias /static_nalodeni/;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
nalodeni.conf
|
|
@ -0,0 +1,27 @@
|
||||||
|
version: '2'
|
||||||
|
services:
|
||||||
|
nginx:
|
||||||
|
image: nginx:latest
|
||||||
|
container_name: nalodeni-nginx
|
||||||
|
ports:
|
||||||
|
- "80:8001"
|
||||||
|
volumes:
|
||||||
|
- ./deploy/nginx:/etc/nginx/conf.d
|
||||||
|
- vol_static_nalodeni:/static_nalodeni
|
||||||
|
depends_on:
|
||||||
|
- nalodeni
|
||||||
|
nalodeni:
|
||||||
|
build: .
|
||||||
|
container_name: nalodeni
|
||||||
|
command: bash -c "/nalodeni/src/docker-entrypoint.sh"
|
||||||
|
volumes:
|
||||||
|
- vol_static_nalodeni:/nalodeni/src/static_files
|
||||||
|
ports:
|
||||||
|
- "8000"
|
||||||
|
environment:
|
||||||
|
- NALODENI_DEBUG=off
|
||||||
|
- NALODENI_DEBUG_LOCAL=off
|
||||||
|
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
vol_static_nalodeni:
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
trap "echo TRAPed signal" HUP INT QUIT KILL TERM
|
||||||
|
|
||||||
|
source /venv/bin/activate
|
||||||
|
|
||||||
|
cd /nalodeni/src
|
||||||
|
|
||||||
|
python manage.py migrate
|
||||||
|
|
||||||
|
python manage.py collectstatic --noinput
|
||||||
|
|
||||||
|
python manage.py loaddata nalodeni_newsletter nalodeni_topics nalodeni_skills
|
||||||
|
|
||||||
|
PYTHONIOENCODING=utf-8 python manage.py runserver 0.0.0.0:8000
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
import os
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
from openid_connect import connect, connect_url
|
||||||
|
from openid_connect._oidc import OpenIDClient, TokenResponse
|
||||||
|
|
||||||
|
class OpenIDClientCustom(OpenIDClient):
|
||||||
|
|
||||||
|
def refresh_session(self, refresh_token):
|
||||||
|
r = requests.post(self.token_endpoint, auth=self.auth, data=dict(
|
||||||
|
grant_type="refresh_token",
|
||||||
|
refresh_token=refresh_token
|
||||||
|
), headers={'Accept': 'application/json'})
|
||||||
|
if r.status_code != 200:
|
||||||
|
#r.raise_for_status()
|
||||||
|
return None
|
||||||
|
|
||||||
|
resp = TokenResponse(r.json(), self)
|
||||||
|
|
||||||
|
if "scope" in resp._data:
|
||||||
|
resp.scope = set(self.translate_scope_out(set(resp._data["scope"].split(" "))))
|
||||||
|
if not hasattr(resp, "scope") or "openid" in resp.scope:
|
||||||
|
resp.id = self.get_id(resp)
|
||||||
|
|
||||||
|
return resp
|
||||||
|
|
||||||
|
|
||||||
|
server = None
|
||||||
|
|
||||||
|
def get_server_orig():
|
||||||
|
global server
|
||||||
|
|
||||||
|
if server is not None:
|
||||||
|
return server
|
||||||
|
|
||||||
|
AUTH_URL = os.environ.get("AUTH_URL")
|
||||||
|
if AUTH_URL:
|
||||||
|
server = connect_url(AUTH_URL)
|
||||||
|
else:
|
||||||
|
server = connect(settings.AUTH_SERVER, settings.AUTH_CLIENT_ID,
|
||||||
|
settings.AUTH_CLIENT_SECRET,
|
||||||
|
getattr(settings, 'AUTH_PROTOCOL', None))
|
||||||
|
|
||||||
|
return server
|
||||||
|
|
||||||
|
def get_server():
|
||||||
|
global server
|
||||||
|
|
||||||
|
if server is not None:
|
||||||
|
return server
|
||||||
|
|
||||||
|
server = OpenIDClientCustom( settings.AUTH_SERVER, settings.AUTH_CLIENT_ID,
|
||||||
|
settings.AUTH_CLIENT_SECRET)
|
||||||
|
|
||||||
|
return server
|
|
@ -0,0 +1,13 @@
|
||||||
|
# encoding: utf8
|
||||||
|
|
||||||
|
class MissingSsoInfo(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class EmailVersusIdentityMismatch(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class UsernameAlreadyTaken(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class EmailAlreadyTaken(Exception):
|
||||||
|
pass
|
|
@ -0,0 +1,29 @@
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
import keycloak_oidc.views
|
||||||
|
|
||||||
|
class KeycloakSessionRefreshMiddleware(object):
|
||||||
|
def __init__(self, get_response):
|
||||||
|
self.get_response = get_response
|
||||||
|
# One-time configuration and initialization.
|
||||||
|
|
||||||
|
def __call__(self, request):
|
||||||
|
# Code to be executed for each request before
|
||||||
|
# the view (and later middleware) are called.
|
||||||
|
|
||||||
|
refresh_expires_at = request.session.get('refresh_expires_at',None)
|
||||||
|
#print("refresh_expires_at", refresh_expires_at, time.time())
|
||||||
|
|
||||||
|
if refresh_expires_at is not None:
|
||||||
|
if refresh_expires_at - time.time() < 5*60: # 5 minutes before expiration
|
||||||
|
rslt = keycloak_oidc.views.renew_session(request)
|
||||||
|
|
||||||
|
if rslt != 0:
|
||||||
|
# renew not successful
|
||||||
|
return keycloak_oidc.views.logout(request, multiSessionFound=False)
|
||||||
|
|
||||||
|
return self.get_response(request)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
from django.conf.urls import url
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
app_name = 'keycloak_oidc'
|
||||||
|
urlpatterns = [
|
||||||
|
url(r'^$', views.login, name='login-base'),
|
||||||
|
url(r'^login/$', views.login, name='login'),
|
||||||
|
url(r'^login/(?P<idp_hint>[a-z]+)/$', views.login, name='login_idp'),
|
||||||
|
url(r'^done/$', views.callback, name='login-done'),
|
||||||
|
url(r'^logout/$', views.logout, name='logout', kwargs={'ssoLogout':True}),
|
||||||
|
url(r'^logoutLocal/$', views.logout, name='logout'),
|
||||||
|
url(r'^account/$', views.account, name='account'),
|
||||||
|
url(r'^refresh_session/$', views.refresh_session, name='refresh-session'),
|
||||||
|
]
|
|
@ -0,0 +1,337 @@
|
||||||
|
# encoding: utf8
|
||||||
|
from importlib import import_module
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from jwkest.jwt import JWT
|
||||||
|
from requests.exceptions import HTTPError
|
||||||
|
|
||||||
|
from django.contrib import auth
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.contrib.auth.models import Permission
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.shortcuts import redirect, resolve_url
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils.http import is_safe_url
|
||||||
|
|
||||||
|
from django.conf import settings as appSettings
|
||||||
|
|
||||||
|
from . import auth as _auth
|
||||||
|
from . import exceptions as sso_exc
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
try:
|
||||||
|
LOGIN_REDIRECT_URL = appSettings.LOGIN_REDIRECT_URL
|
||||||
|
except AttributeError:
|
||||||
|
LOGIN_REDIRECT_URL = '/'
|
||||||
|
|
||||||
|
try:
|
||||||
|
LOGOUT_REDIRECT_URL = appSettings.LOGOUT_REDIRECT_URL
|
||||||
|
except AttributeError:
|
||||||
|
LOGOUT_REDIRECT_URL = '/sso/logout/'
|
||||||
|
|
||||||
|
if LOGOUT_REDIRECT_URL is None:
|
||||||
|
LOGOUT_REDIRECT_URL = '/sso/logout/'
|
||||||
|
|
||||||
|
try:
|
||||||
|
AUTH_SCOPE = appSettings.AUTH_SCOPE
|
||||||
|
except AttributeError:
|
||||||
|
AUTH_SCOPE = ('openid',)
|
||||||
|
|
||||||
|
try:
|
||||||
|
AUTH_AVAIL_IDP = appSettings.AUTH_AVAIL_IDP
|
||||||
|
except AttributeError:
|
||||||
|
AUTH_AVAIL_IDP = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
GET_USER_FUNCTION = appSettings.AUTH_GET_USER_FUNCTION
|
||||||
|
except AttributeError:
|
||||||
|
GET_USER_FUNCTION = 'keycloak_oidc:get_user_by_username'
|
||||||
|
|
||||||
|
try:
|
||||||
|
LOGOUT_REDIRECT_VIEW = appSettings.AUTH_LOGOUT_REDIRECT_VIEW
|
||||||
|
except AttributeError:
|
||||||
|
LOGOUT_REDIRECT_VIEW = 'nalodeni:about'
|
||||||
|
|
||||||
|
try:
|
||||||
|
LOGIN_ROLE_REQUIRED = appSettings.AUTH_LOGIN_ROLE_REQUIRED
|
||||||
|
except AttributeError:
|
||||||
|
LOGIN_ROLE_REQUIRED = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
LOGIN_ROLE_REQUIRED_ANY = appSettings.AUTH_LOGIN_ROLE_REQUIRED_ANY
|
||||||
|
except AttributeError:
|
||||||
|
LOGIN_ROLE_REQUIRED_ANY = False
|
||||||
|
|
||||||
|
|
||||||
|
jwt_singleton = JWT()
|
||||||
|
|
||||||
|
|
||||||
|
def _import_object(path, def_name):
|
||||||
|
try:
|
||||||
|
mod, cls = path.split(':', 1)
|
||||||
|
except ValueError:
|
||||||
|
mod = path
|
||||||
|
cls = def_name
|
||||||
|
|
||||||
|
return getattr(import_module(mod), cls)
|
||||||
|
|
||||||
|
get_user = _import_object(GET_USER_FUNCTION, 'get_user')
|
||||||
|
|
||||||
|
|
||||||
|
def login(request, idp_hint=None):
|
||||||
|
return_path = request.GET.get(auth.REDIRECT_FIELD_NAME, "/")
|
||||||
|
|
||||||
|
if idp_hint and idp_hint not in AUTH_AVAIL_IDP:
|
||||||
|
idp_hint = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
srv = _auth.get_server()
|
||||||
|
except Exception as e:
|
||||||
|
messages.error(request, "Bohužel nastaly potíže s centrálním přihlášením uživatele.")
|
||||||
|
logger.error('Chyba SSO login: %s' % e)
|
||||||
|
return redirect(request.build_absolute_uri("/"))
|
||||||
|
|
||||||
|
return redirect(_auth.get_server().authorize(
|
||||||
|
redirect_uri = request.build_absolute_uri(reverse("keycloak_oidc:login-done")),
|
||||||
|
state = return_path,
|
||||||
|
scope = AUTH_SCOPE,
|
||||||
|
) + "&kc_locale=" + appSettings.AUTH_SSO_LOCALE + (
|
||||||
|
('&kc_idp_hint=' + idp_hint) if idp_hint is not None else ""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def renew_session(request, returnHttp=False):
|
||||||
|
"""
|
||||||
|
Refresh the session using a refresh_token.
|
||||||
|
"""
|
||||||
|
|
||||||
|
reftoken = request.session.get('refresh_token', None)
|
||||||
|
|
||||||
|
if reftoken is None:
|
||||||
|
return -3
|
||||||
|
|
||||||
|
#print("Refresh token IAT:", jwt_singleton.unpack(reftoken).payload()['iat'])
|
||||||
|
#print(jwt_singleton.unpack(reftoken).payload())
|
||||||
|
|
||||||
|
try:
|
||||||
|
srv = _auth.get_server()
|
||||||
|
except Exception as e:
|
||||||
|
messages.error(request,"Bohužel nastaly potíže s centrálním SSO serverem.")
|
||||||
|
logger.error('Chyba SSO renew: %s' % e)
|
||||||
|
if returnHttp:
|
||||||
|
return redirect(request.build_absolute_uri("/"))
|
||||||
|
else:
|
||||||
|
return -1
|
||||||
|
|
||||||
|
new_token = srv.refresh_session(reftoken)
|
||||||
|
if new_token is None:
|
||||||
|
if returnHttp:
|
||||||
|
return redirect(request.build_absolute_uri("/"))
|
||||||
|
else:
|
||||||
|
return -2
|
||||||
|
|
||||||
|
rt = new_token._data['refresh_token']
|
||||||
|
#print("Refresh token NEW IAT:", jwt_singleton.unpack(rt).payload()['iat'])
|
||||||
|
rt_payload = jwt_singleton.unpack(rt).payload()
|
||||||
|
|
||||||
|
# save the new refresh token
|
||||||
|
request.session['refresh_token'] = rt
|
||||||
|
request.session['refresh_expires_at'] = rt_payload['exp']
|
||||||
|
|
||||||
|
if returnHttp:
|
||||||
|
return redirect(request.build_absolute_uri("/"))
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def callback(request):
|
||||||
|
return_path = request.GET.get("state",'/')
|
||||||
|
sso_code = request.GET.get('code','')
|
||||||
|
|
||||||
|
if sso_code == "" and return_path == "":
|
||||||
|
messages.error(request, "Chyba při přihlášení na centrálním serveru SSO.")
|
||||||
|
return redirect(request.build_absolute_uri("/"))
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
res = _auth.get_server().request_token(
|
||||||
|
redirect_uri = request.build_absolute_uri(reverse("keycloak_oidc:login-done")),
|
||||||
|
code = sso_code,
|
||||||
|
)
|
||||||
|
except HTTPError as e:
|
||||||
|
messages.error(request, "Přihlášení uživatele se nezdařilo. Chyba komunikace s centrálním serverem přihlášení.")
|
||||||
|
logger.error('Chyba SSO callback: %s' % e)
|
||||||
|
return redirect(request.build_absolute_uri("/"))
|
||||||
|
|
||||||
|
#print(res)
|
||||||
|
#print(dir(res))
|
||||||
|
#print(res.id_token)
|
||||||
|
#print(res.access_token)
|
||||||
|
|
||||||
|
id_token_data = jwt_singleton.unpack(res.id_token).payload()
|
||||||
|
|
||||||
|
reftoken = jwt_singleton.unpack(res._data['refresh_token']).payload()
|
||||||
|
|
||||||
|
# Get the user from local DB
|
||||||
|
#
|
||||||
|
try:
|
||||||
|
user = get_user(id_token_data)
|
||||||
|
user.backend = 'django.contrib.auth.backends.ModelBackend'
|
||||||
|
except sso_exc.MissingSsoInfo as e:
|
||||||
|
messages.error(request, "Účet na Pirátské identitě není řádně vyplněn. %s" % e )
|
||||||
|
messages.info(request,"Zkontrolujte údaje na <a href='%s' target='_blank'>auth.pirati.cz</a>." % (appSettings.AUTH_SERVER + "account/"), extra_tags="safe")
|
||||||
|
logger.error('SSO account incomplete 1: %s' % e)
|
||||||
|
return redirect(request.build_absolute_uri("/"))
|
||||||
|
except sso_exc.EmailVersusIdentityMismatch as e:
|
||||||
|
messages.error(request, "Účet v nalodění a na Pirátské identitě nesouhlasí.")
|
||||||
|
messages.info(request,"Obraťte se na technický odbor Pirátů.")
|
||||||
|
logger.error('SSO account email vs. identity mismatch: %s' % e)
|
||||||
|
return redirect(request.build_absolute_uri("/"))
|
||||||
|
except sso_exc.UsernameAlreadyTaken as e:
|
||||||
|
messages.error(request, "%s" % e)
|
||||||
|
messages.info(request,'Pokud se jedná o vaše uživatelské jméno,'
|
||||||
|
+ 'obraťte se na technický odbor Pirátů.')
|
||||||
|
logger.error('Username already taken in Nalodeni: %s' % e)
|
||||||
|
return redirect(request.build_absolute_uri("/"))
|
||||||
|
except sso_exc.EmailAlreadyTaken as e:
|
||||||
|
messages.error(request, "%s" % e)
|
||||||
|
messages.info(request,'Pokud se jedná o váš email,'
|
||||||
|
+ 'obraťte se na technický odbor Pirátů.')
|
||||||
|
logger.error('Email already taken in Nalodeni: %s' % e)
|
||||||
|
return redirect(request.build_absolute_uri("/"))
|
||||||
|
|
||||||
|
|
||||||
|
# Process the user roles, if any ( taken from bossoidc.backend )
|
||||||
|
#
|
||||||
|
jwt = jwt_singleton.unpack(res.access_token).payload()
|
||||||
|
#print("access_token:", jwt_singleton.unpack(res.access_token).payload())
|
||||||
|
#print("id_token:", jwt_singleton.unpack(res.id_token).payload())
|
||||||
|
#### if 'realm_access' in jwt: # Session logins and Bearer tokens from password Grant Types
|
||||||
|
#### roles = jwt['realm_access']['roles']
|
||||||
|
#### else:
|
||||||
|
#### roles = []
|
||||||
|
resource_roles = []
|
||||||
|
if 'resource_access' in jwt:
|
||||||
|
if jwt['azp'] in jwt['resource_access']:
|
||||||
|
if 'roles' in jwt['resource_access'][jwt['azp']]:
|
||||||
|
resource_roles = jwt['resource_access'][jwt['azp']]['roles']
|
||||||
|
if (LOGIN_ROLE_REQUIRED_ANY and len(resource_roles) == 0) or (LOGIN_ROLE_REQUIRED is not None and LOGIN_ROLE_REQUIRED not in resource_roles):
|
||||||
|
messages.error(request, "Váš pirátský účet nemá přidělené potřebné přístupové role. Přístup odmítnut.")
|
||||||
|
logger.error('SSO account %s: no roles assigned' % (user.ssoUid,))
|
||||||
|
return redirect(request.build_absolute_uri("/"))
|
||||||
|
|
||||||
|
# Update user information
|
||||||
|
#
|
||||||
|
if 'given_name' in jwt and 'family_name' in jwt:
|
||||||
|
user.first_name = jwt['given_name']
|
||||||
|
user.last_name = jwt['family_name']
|
||||||
|
else:
|
||||||
|
messages.error(request, "Účet na Pirátské identitě není řádně vyplněn.")
|
||||||
|
messages.info(request,"Zkontrolujte údaje na <a href='%s' target='_blank'>auth.pirati.cz</a>." % (appSettings.AUTH_SERVER + "account/"), extra_tags="safe")
|
||||||
|
logger.error('SSO account incomplete 2: %s' % e)
|
||||||
|
return redirect(request.build_absolute_uri("/"))
|
||||||
|
|
||||||
|
user.loginSession = jwt['session_state']
|
||||||
|
user.save()
|
||||||
|
|
||||||
|
# Authenticate the user
|
||||||
|
#
|
||||||
|
auth.login(request, user)
|
||||||
|
|
||||||
|
request.session['openid_token'] = res.id_token
|
||||||
|
request.session['openid'] = id_token_data
|
||||||
|
request.session['refresh_token'] = res._data['refresh_token']
|
||||||
|
request.session['refresh_expires_at'] = reftoken['exp']
|
||||||
|
request.session['loginSession'] = user.loginSession
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# Set client-system roles based on SSO roles
|
||||||
|
#
|
||||||
|
# After setting permissions, the User object should be reloaded,
|
||||||
|
# but we do a redirect, so it is fine.
|
||||||
|
##
|
||||||
|
#print(roles)
|
||||||
|
#print(resource_roles)
|
||||||
|
user.user_permissions.clear()
|
||||||
|
|
||||||
|
request.session['site_perms'] = []
|
||||||
|
|
||||||
|
# prepend "sso_" not to mix with other perms
|
||||||
|
for rr in resource_roles:
|
||||||
|
perm = 'sso_'+rr
|
||||||
|
request.session['site_perms'].append(perm)
|
||||||
|
|
||||||
|
# tell the User to calculate needed permissions
|
||||||
|
request.session['spc'] = user.do_site_perms_calc(request.session['site_perms'])
|
||||||
|
|
||||||
|
# tell us, what roles do we have
|
||||||
|
if appSettings.DEBUG:
|
||||||
|
print("Roles assigned:", request.session['site_perms'])
|
||||||
|
print("Roles calculated:", request.session['spc'])
|
||||||
|
|
||||||
|
url_is_safe = is_safe_url(
|
||||||
|
url = return_path,
|
||||||
|
host = request.get_host(),
|
||||||
|
allowed_hosts = set(request.get_host()),
|
||||||
|
require_https = request.is_secure(),
|
||||||
|
)
|
||||||
|
|
||||||
|
if not url_is_safe:
|
||||||
|
return redirect(resolve_url(LOGIN_REDIRECT_URL))
|
||||||
|
return redirect(return_path)
|
||||||
|
|
||||||
|
|
||||||
|
def logout(request, multiSessionFound=False, ssoLogout=False):
|
||||||
|
id_token = request.session.get('openid_token', None)
|
||||||
|
|
||||||
|
if request.user.is_authenticated:
|
||||||
|
if not multiSessionFound:
|
||||||
|
# reset the DB session, because this is
|
||||||
|
# a user requested logout
|
||||||
|
request.user.loginSession = None
|
||||||
|
request.user.save()
|
||||||
|
|
||||||
|
request.session['loginSession'] = None
|
||||||
|
|
||||||
|
auth.logout(request)
|
||||||
|
|
||||||
|
if ssoLogout:
|
||||||
|
try:
|
||||||
|
srv = _auth.get_server()
|
||||||
|
except Exception as e:
|
||||||
|
messages.error(request,"Bohužel nastaly potíže s centrálním odhlášením uživatele.")
|
||||||
|
logger.error('Chyba SSO logout: %s' % e)
|
||||||
|
return redirect(request.build_absolute_uri("/"))
|
||||||
|
|
||||||
|
if _auth.get_server().end_session_endpoint and id_token is not None:
|
||||||
|
messages.info(request,"Byli jste odhlášení z aplikace i Centrální identity.")
|
||||||
|
return redirect(_auth.get_server().end_session(
|
||||||
|
post_logout_redirect_uri = request.build_absolute_uri(LOGOUT_REDIRECT_URL),
|
||||||
|
state = '',
|
||||||
|
id_token_hint = id_token,
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
if id_token:
|
||||||
|
messages.info(request,"Byli jste odhlášení z aplikace. Vaše centrální identita na <a href='%s' target='_blank'>auth.pirati.cz</a> zůstává přihlášena." % (appSettings.AUTH_SERVER + "account/"), extra_tags="safe")
|
||||||
|
else:
|
||||||
|
messages.info(request,"Byli jste odhlášení z aplikace.")
|
||||||
|
|
||||||
|
return redirect(LOGOUT_REDIRECT_VIEW)
|
||||||
|
|
||||||
|
def account(request):
|
||||||
|
return redirect(
|
||||||
|
request.build_absolute_uri(appSettings.AUTH_SERVER+"account/")
|
||||||
|
+ "?kc_locale=" + appSettings.AUTH_SSO_LOCALE
|
||||||
|
)
|
||||||
|
|
||||||
|
def refresh_session(request):
|
||||||
|
"""
|
||||||
|
Does a keycloak session refresh via middleware.
|
||||||
|
"""
|
||||||
|
resp = { 'exp' : request.session.get('refresh_expires_at', None) }
|
||||||
|
|
||||||
|
return HttpResponse(json.dumps(resp), content_type="application/json")
|
||||||
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
# -*- encoding:utf-8 -*-
|
||||||
|
"""
|
||||||
|
Main settings file for one domain.
|
||||||
|
|
||||||
|
Domain: .CZ
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from .settings_global import *
|
||||||
|
from .settings_local import *
|
||||||
|
|
||||||
|
##
|
||||||
|
# Load ENV variables with prefix NALODENI_ to UPDATE_CONFIGS
|
||||||
|
#
|
||||||
|
# Docker deployment needs setting to be supplied externally.
|
||||||
|
#
|
||||||
|
# ENV_APPROVED_UPDATES contains known variables we can set,
|
||||||
|
# the tuple contains "path" to the setting.
|
||||||
|
ENV_APPROVED_UPDATES = {
|
||||||
|
'AUTH_SERVER' : ('AUTH_SERVER',),
|
||||||
|
'AUTH_CLIENT_ID' : ('AUTH_CLIENT_ID',),
|
||||||
|
'AUTH_CLIENT_SECRET' : ('AUTH_CLIENT_SECRET',),
|
||||||
|
'AUTH_AVAIL_IDP' : ('AUTH_AVAIL_IDP',),
|
||||||
|
|
||||||
|
'HTTP_PROTOCOL' : ('HTTP_PROTOCOL',),
|
||||||
|
'BASE_DOMAIN' : ('BASE_DOMAIN',),
|
||||||
|
'BASE_SUBDOMAIN' : ('BASE_SUBDOMAIN',),
|
||||||
|
'BASE_PORT' : ('BASE_PORT',),
|
||||||
|
|
||||||
|
'PSQL_USER' : ('DATABASES','default','USER'),
|
||||||
|
'PSQL_PASSWORD' : ('DATABASES','default','PASSWORD'),
|
||||||
|
'PSQL_DBNAME' : ('DATABASES','default','NAME'),
|
||||||
|
'PSQL_HOST' : ('DATABASES','default','HOST'),
|
||||||
|
'PSQL_PORT' : ('DATABASES','default','PORT'),
|
||||||
|
|
||||||
|
'APP_REG_LIMIT_HARD' : ('APP_REG_LIMIT_HARD', ),
|
||||||
|
'APP_REG_LIMIT_SOFT' : ('APP_REG_LIMIT_SOFT', ),
|
||||||
|
'TOKEN_VALID_SEC' : ('TOKEN_VALID_SEC', ),
|
||||||
|
|
||||||
|
'EMAIL_BACKEND' : ('EMAIL_BACKEND',),
|
||||||
|
'EMAIL_HOST' : ('EMAIL_HOST',),
|
||||||
|
'EMAIL_PORT' : ('EMAIL_PORT',),
|
||||||
|
'EMAIL_HOST_USER' : ('EMAIL_HOST_USER',),
|
||||||
|
'EMAIL_HOST_PASSWORD' : ('EMAIL_HOST_PASSWORD',),
|
||||||
|
'EMAIL_USE_TLS' : ('EMAIL_USE_TLS',),
|
||||||
|
'EMAIL_USE_SSL' : ('EMAIL_USE_SSL',),
|
||||||
|
'EMAIL_SSL_CERTFILE' : ('EMAIL_SSL_CERTFILE',),
|
||||||
|
'EMAIL_SSL_KEYFILE' : ('EMAIL_SSL_KEYFILE',),
|
||||||
|
|
||||||
|
'DEBUG' : ('DEBUG',),
|
||||||
|
'DEBUG_LOCAL' : ('DEBUG_LOCAL',),
|
||||||
|
}
|
||||||
|
for evk in os.environ:
|
||||||
|
if evk[0:9] == "NALODENI_":
|
||||||
|
var_key = evk[9:]
|
||||||
|
if var_key in ENV_APPROVED_UPDATES:
|
||||||
|
val_type = os.environ[evk][0:2]
|
||||||
|
val = str(os.environ[evk][2:])
|
||||||
|
|
||||||
|
if val_type == "s-":
|
||||||
|
val = (val, ) # tuple
|
||||||
|
elif val_type == "b-":
|
||||||
|
val = (val == 'on', ) # tuple
|
||||||
|
elif val_type == "a-":
|
||||||
|
# nested tuple, one gets eaten by + operator
|
||||||
|
val = ( tuple(val.split(',')) ,)
|
||||||
|
else:
|
||||||
|
print("Wrong ENV value for '%s', skipping" % evk)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if val == "":
|
||||||
|
val = (None, )
|
||||||
|
|
||||||
|
UPDATE_CONFIGS.append( ENV_APPROVED_UPDATES[var_key] + val )
|
||||||
|
|
||||||
|
##
|
||||||
|
# Update configs from settings_local.UPDATE_CONFIGS
|
||||||
|
#
|
||||||
|
# Each item is a list-like path to the setting to be
|
||||||
|
# updated, the last value of the list is the value of
|
||||||
|
# the setting updated. E.g.:
|
||||||
|
# ('CACHES', 'default', 'KEY_PREFIX', 'cz:'),
|
||||||
|
# is the same as
|
||||||
|
# CACHES['default']['KEY_PREFIX'] = 'cz:'
|
||||||
|
# written directly in the settings file.
|
||||||
|
#
|
||||||
|
# Use array references to update the existing array.
|
||||||
|
# [:-2] used to preserve the last array-like link, so
|
||||||
|
# we can update the value there, and not the local variable.
|
||||||
|
for item in UPDATE_CONFIGS:
|
||||||
|
p = vars()
|
||||||
|
for i in item[:-2]:
|
||||||
|
if not i in p:
|
||||||
|
p[i] = {}
|
||||||
|
p = p[i]
|
||||||
|
p[item[-2]]=item[-1]
|
||||||
|
|
||||||
|
#
|
||||||
|
##
|
||||||
|
|
||||||
|
|
||||||
|
## ##
|
||||||
|
# DO NOT EDIT BELOW !!! #
|
||||||
|
## ##
|
||||||
|
|
||||||
|
##
|
||||||
|
# SECURITY - do not change.
|
||||||
|
##
|
||||||
|
#CSRF_USE_SESSIONS = False
|
||||||
|
CSRF_COOKIE_HTTPONLY = True
|
||||||
|
CSRF_COOKIE_SECURE = (HTTP_PROTOCOL == 'https') # fix to HTTPS when available
|
||||||
|
SESSION_COOKIE_HTTPONLY = True
|
||||||
|
SESSION_COOKIE_SECURE = (HTTP_PROTOCOL == 'https') # fix to HTTPS when available
|
||||||
|
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', HTTP_PROTOCOL)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Domain setup
|
||||||
|
##
|
||||||
|
BASE_URL = HTTP_PROTOCOL + '://' + BASE_SUBDOMAIN + BASE_DOMAIN + ((':' + BASE_PORT) if BASE_PORT != "" else "")
|
||||||
|
SESSION_COOKIE_DOMAIN= BASE_SUBDOMAIN + BASE_DOMAIN
|
||||||
|
CSRF_COOKIE_DOMAIN=BASE_SUBDOMAIN + BASE_DOMAIN
|
||||||
|
CSRF_TRUSTED_ORIGINS = [ SESSION_COOKIE_DOMAIN , ]
|
||||||
|
ALLOWED_HOSTS = [ BASE_SUBDOMAIN + BASE_DOMAIN, ]
|
||||||
|
|
||||||
|
##
|
||||||
|
# Internationalization, https://docs.djangoproject.com/en/1.11/topics/i18n/
|
||||||
|
##
|
||||||
|
LANGUAGE_CODE = 'cs'
|
||||||
|
TIME_ZONE = 'Europe/Prague'
|
||||||
|
|
||||||
|
##
|
||||||
|
# User model
|
||||||
|
##
|
||||||
|
AUTH_USER_MODEL = "nalodeni.AppUser"
|
||||||
|
|
||||||
|
##
|
||||||
|
# Logger config
|
||||||
|
##
|
||||||
|
LOG_FILES = os.path.join(BASE_DIR, 'log_files')
|
||||||
|
LOG_INCOMING_REQUESTS_FILE = os.path.join(
|
||||||
|
LOG_FILES, 'incoming_requests_%s.log' % BASE_DOMAIN
|
||||||
|
)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Media files path setup
|
||||||
|
##
|
||||||
|
MEDIA_ROOT = os.path.join(BASE_DIR,'media_files')
|
||||||
|
|
||||||
|
##
|
||||||
|
# Debugging settings
|
||||||
|
##
|
||||||
|
if DEBUG_LOCAL:
|
||||||
|
ALLOWED_HOSTS += [
|
||||||
|
'localhost',
|
||||||
|
]
|
||||||
|
|
||||||
|
CSRF_COOKIE_DOMAIN = "localhost"
|
||||||
|
SESSION_COOKIE_DOMAIN= CSRF_COOKIE_DOMAIN
|
||||||
|
|
|
@ -0,0 +1,208 @@
|
||||||
|
"""
|
||||||
|
Django settings for main project.
|
||||||
|
|
||||||
|
Generated by 'django-admin startproject' using Django 2.0.2.
|
||||||
|
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/2.0/topics/settings/
|
||||||
|
|
||||||
|
For the full list of settings and their values, see
|
||||||
|
https://docs.djangoproject.com/en/2.0/ref/settings/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Used to update config values _after_ importing settings_local
|
||||||
|
UPDATE_CONFIGS = []
|
||||||
|
|
||||||
|
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||||
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
|
||||||
|
# Quick-start development settings - unsuitable for production
|
||||||
|
# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/
|
||||||
|
|
||||||
|
# SECURITY WARNING: keep the secret key used in production secret!
|
||||||
|
SECRET_KEY = '-replace-this-in-install-settings-3243v432'
|
||||||
|
|
||||||
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
|
DEBUG = False
|
||||||
|
DEBUG_LOCAL=False
|
||||||
|
DEBUG_PROPAGATE_EXCEPTIONS = True
|
||||||
|
LOG_INCOMING_REQUESTS = False
|
||||||
|
|
||||||
|
ALLOWED_HOSTS = []
|
||||||
|
HTTP_PROTOCOL = 'https'
|
||||||
|
BASE_DOMAIN = "localhost"
|
||||||
|
BASE_SUBDOMAIN = ""
|
||||||
|
BASE_PORT = ""
|
||||||
|
|
||||||
|
|
||||||
|
# Application definition
|
||||||
|
|
||||||
|
INSTALLED_APPS = [
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.messages',
|
||||||
|
'django.contrib.staticfiles',
|
||||||
|
|
||||||
|
#'statici18n', # e.g. for javascript translation
|
||||||
|
'keycloak_oidc',
|
||||||
|
|
||||||
|
'anymail',
|
||||||
|
|
||||||
|
'nalodeni',
|
||||||
|
]
|
||||||
|
|
||||||
|
MIDDLEWARE = [
|
||||||
|
'django.middleware.security.SecurityMiddleware',
|
||||||
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
|
|
||||||
|
'keycloak_oidc.middleware.KeycloakSessionRefreshMiddleware', # added
|
||||||
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
|
|
||||||
|
'django.middleware.gzip.GZipMiddleware',
|
||||||
|
]
|
||||||
|
|
||||||
|
ROOT_URLCONF = 'main.urls'
|
||||||
|
|
||||||
|
TEMPLATES = [
|
||||||
|
{
|
||||||
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
|
'DIRS': [],
|
||||||
|
'APP_DIRS': True,
|
||||||
|
'OPTIONS': {
|
||||||
|
'context_processors': [
|
||||||
|
#'django.template.context_processors.debug',
|
||||||
|
'django.template.context_processors.request',
|
||||||
|
'django.contrib.auth.context_processors.auth',
|
||||||
|
'django.contrib.messages.context_processors.messages',
|
||||||
|
'django.template.context_processors.i18n', # for statici18n
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
WSGI_APPLICATION = 'main.wsgi.application'
|
||||||
|
|
||||||
|
|
||||||
|
# Database
|
||||||
|
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases
|
||||||
|
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.postgresql',
|
||||||
|
'NAME': '',
|
||||||
|
'USER': '',
|
||||||
|
'PASSWORD': '',
|
||||||
|
'HOST': '', # Set to empty string for localhost.
|
||||||
|
'PORT': '', # Set to empty string for default.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Specify in settings
|
||||||
|
AUTH_USER_MODEL = None
|
||||||
|
|
||||||
|
# Other auth backends
|
||||||
|
AUTHENTICATION_BACKENDS = [
|
||||||
|
'django.contrib.auth.backends.ModelBackend',
|
||||||
|
'nalodeni.auth.EmailTokenAuthBackend',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Password validation
|
||||||
|
# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators
|
||||||
|
|
||||||
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||||
|
},
|
||||||
|
# {
|
||||||
|
# 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||||
|
# },
|
||||||
|
# {
|
||||||
|
# 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||||
|
# },
|
||||||
|
# {
|
||||||
|
# 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||||
|
# },
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# Internationalization
|
||||||
|
# https://docs.djangoproject.com/en/2.0/topics/i18n/
|
||||||
|
|
||||||
|
LANGUAGE_CODE = 'cs'
|
||||||
|
|
||||||
|
TIME_ZONE = 'Europe/Prague'
|
||||||
|
|
||||||
|
USE_I18N = True
|
||||||
|
|
||||||
|
USE_L10N = True
|
||||||
|
|
||||||
|
USE_TZ = False
|
||||||
|
|
||||||
|
|
||||||
|
# Static files (CSS, JavaScript, Images)
|
||||||
|
# https://docs.djangoproject.com/en/2.0/howto/static-files/
|
||||||
|
|
||||||
|
STATIC_URL = '/static/'
|
||||||
|
STATIC_ROOT = os.path.join(BASE_DIR,'static_files')
|
||||||
|
|
||||||
|
##
|
||||||
|
# Locale, translation files
|
||||||
|
##
|
||||||
|
LOCALE_PATHS = [
|
||||||
|
BASE_DIR + "/locale",
|
||||||
|
]
|
||||||
|
|
||||||
|
##
|
||||||
|
# Email setup
|
||||||
|
##
|
||||||
|
EMAIL_HOST="localhost"
|
||||||
|
EMAIL_PORT=25
|
||||||
|
#DEFAULT_FROM_EMAIL = 'nalodeni@pirati.cz'
|
||||||
|
ANYMAIL = {
|
||||||
|
"MAILGUN_API_KEY": "",
|
||||||
|
"MAILGUN_SENDER_DOMAIN": '',
|
||||||
|
"MAILGUN_API_URL": 'https://api.eu.mailgun.net/v3',
|
||||||
|
}
|
||||||
|
#EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend"
|
||||||
|
|
||||||
|
##
|
||||||
|
# SingleSignOn
|
||||||
|
#
|
||||||
|
# AUTH_SCOPE - list of scopes to request from the auth server
|
||||||
|
# AUTH_GET_USER_FUNCTION - name of a function that takes the user info dict,
|
||||||
|
# and returns an user object representing that user
|
||||||
|
##
|
||||||
|
AUTH_SERVER = "https://pttest1.kouzelnakrabicka.cz/auth/realms/pirati/"
|
||||||
|
AUTH_CLIENT_ID = "" # view setting_local.py
|
||||||
|
AUTH_CLIENT_SECRET = "" # view setting_local.py
|
||||||
|
AUTH_SCOPE = ['openid',]
|
||||||
|
AUTH_GET_USER_FUNCTION = 'nalodeni.models:get_user_by_keycloak_email'
|
||||||
|
AUTH_SSO_LOCALE = 'cs'
|
||||||
|
AUTH_AVAIL_IDP = []
|
||||||
|
|
||||||
|
#LOGIN_REDIRECT_URL = '/sso/login'
|
||||||
|
#LOGOUT_REDIRECT_URL = '/sso/logout'
|
||||||
|
|
||||||
|
##
|
||||||
|
# Email registration
|
||||||
|
##
|
||||||
|
APP_REG_LIMIT_HARD = 50
|
||||||
|
APP_REG_LIMIT_SOFT = 0
|
||||||
|
TOKEN_VALID_SEC = 30*60
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# Defaults for settings_local variables
|
||||||
|
##
|
|
@ -0,0 +1,57 @@
|
||||||
|
# -*- encoding:utf-8 -*-
|
||||||
|
"""
|
||||||
|
Application local settings:
|
||||||
|
- database
|
||||||
|
- server connections
|
||||||
|
"""
|
||||||
|
|
||||||
|
# SECURITY WARNING: keep the secret key used in production secret!
|
||||||
|
SECRET_KEY = '5k@dbpq_3+t7g+ylt5h1*ox79lnwp-qijry3y60^1r_9q*m5b('
|
||||||
|
|
||||||
|
##
|
||||||
|
# Database setup
|
||||||
|
##
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.postgresql', # Add 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
|
||||||
|
'NAME': 'mainApp', # Or path to database file if using sqlite3.
|
||||||
|
'USER': 'user', # Not used with sqlite3.
|
||||||
|
'PASSWORD': '', # Not used with sqlite3.
|
||||||
|
'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
|
||||||
|
'PORT': '', # Set to empty string for default. Not used with sqlite3.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
##
|
||||||
|
# Email setup
|
||||||
|
##
|
||||||
|
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||||
|
ANYMAIL = {
|
||||||
|
"MAILGUN_API_KEY": "",
|
||||||
|
"MAILGUN_SENDER_DOMAIN": '',
|
||||||
|
"MAILGUN_API_URL": 'https://api.eu.mailgun.net/v3',
|
||||||
|
|
||||||
|
"FROM_NAME" : "",
|
||||||
|
"FROM_EMAIL" : "",
|
||||||
|
"PUBLIC_TO_EMAIL" : "",
|
||||||
|
}
|
||||||
|
|
||||||
|
##
|
||||||
|
# Update config array with these values
|
||||||
|
##
|
||||||
|
UPDATE_CONFIGS = [
|
||||||
|
]
|
||||||
|
|
||||||
|
UPDATE_CONFIGS = [
|
||||||
|
#('CACHES', 'default', 'LOCATION', '/var/run/memcached/memcached.sock'),
|
||||||
|
('CACHES', 'default', 'KEY_PREFIX', 'mainApp:'),
|
||||||
|
|
||||||
|
('AUTH_CLIENT_ID','mainApp'),
|
||||||
|
('AUTH_CLIENT_SECRET',''),
|
||||||
|
('AUTH_SSO_LOCALE', 'cs'),
|
||||||
|
('AUTH_AVAIL_IDP',('facebook',)), # tuple
|
||||||
|
]
|
||||||
|
|
||||||
|
LOG_INCOMING_REQUESTS = False
|
||||||
|
|
||||||
|
DEBUG_LOCAL=False
|
|
@ -0,0 +1,96 @@
|
||||||
|
<!doctype html>
|
||||||
|
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7" lang=""> <![endif]-->
|
||||||
|
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8" lang=""> <![endif]-->
|
||||||
|
<!--[if IE 8]> <html class="no-js lt-ie9" lang=""> <![endif]-->
|
||||||
|
<!--[if gt IE 8]><!--> <html class="no-js" lang=""> <!--<![endif]-->
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
|
<title></title>
|
||||||
|
<meta name="description" content="">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="apple-touch-icon" href="apple-touch-icon.png">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="static/css/bootstrap.min.css">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
padding-top: 50px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<link rel="stylesheet" href="static/css/bootstrap-theme.min.css">
|
||||||
|
<link rel="stylesheet" href="static/css/main.css">
|
||||||
|
|
||||||
|
<script src="static/js/vendor/modernizr-2.8.3-respond-1.4.2.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!--[if lt IE 8]>
|
||||||
|
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
|
||||||
|
<![endif]-->
|
||||||
|
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-header">
|
||||||
|
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||||
|
<span class="sr-only">Toggle navigation</span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
</button>
|
||||||
|
<a class="navbar-brand" href="#">Project name</a>
|
||||||
|
</div>
|
||||||
|
<div id="navbar" class="navbar-collapse collapse">
|
||||||
|
<form class="navbar-form navbar-right" role="form">
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" placeholder="Email" class="form-control">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="password" placeholder="Password" class="form-control">
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-success">Sign in</button>
|
||||||
|
</form>
|
||||||
|
</div><!--/.navbar-collapse -->
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Main jumbotron for a primary marketing message or call to action -->
|
||||||
|
<div class="jumbotron">
|
||||||
|
<div class="container">
|
||||||
|
<h1>Hello, world!</h1>
|
||||||
|
<p>This is a template for a simple marketing or informational website. It includes a large callout called a jumbotron and three supporting pieces of content. Use it as a starting point to create something more unique.</p>
|
||||||
|
<p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more »</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<!-- Example row of columns -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<h2>Heading</h2>
|
||||||
|
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui. </p>
|
||||||
|
<p><a class="btn btn-default" href="#" role="button">View details »</a></p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<h2>Heading</h2>
|
||||||
|
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui. </p>
|
||||||
|
<p><a class="btn btn-default" href="#" role="button">View details »</a></p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<h2>Heading</h2>
|
||||||
|
<p>Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p>
|
||||||
|
<p><a class="btn btn-default" href="#" role="button">View details »</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>© Company 2015</p>
|
||||||
|
</footer>
|
||||||
|
</div> <!-- /container -->
|
||||||
|
<script>window.jQuery || document.write('<script src="static/js/vendor/jquery-1.11.2.min.js"><\/script>')</script>
|
||||||
|
|
||||||
|
<script src="static/js/vendor/bootstrap.min.js"></script>
|
||||||
|
|
||||||
|
<script src="static/js/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,38 @@
|
||||||
|
"""main URL Configuration
|
||||||
|
|
||||||
|
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||||
|
https://docs.djangoproject.com/en/2.0/topics/http/urls/
|
||||||
|
Examples:
|
||||||
|
Function views
|
||||||
|
1. Add an import: from my_app import views
|
||||||
|
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||||
|
Class-based views
|
||||||
|
1. Add an import: from other_app.views import Home
|
||||||
|
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||||
|
Including another URLconf
|
||||||
|
1. Import the include() function: from django.urls import include, path
|
||||||
|
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||||
|
"""
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.urls import include, path, re_path
|
||||||
|
|
||||||
|
import nalodeni.urls
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
#path('admin/', admin.site.urls),
|
||||||
|
path('', include(nalodeni.urls)),
|
||||||
|
|
||||||
|
# SSO
|
||||||
|
path('sso/', include('keycloak_oidc.urls')),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# Javascript translations
|
||||||
|
from django.views.i18n import JavaScriptCatalog
|
||||||
|
urlpatterns += [
|
||||||
|
path('jsi18n/nalodeni/',
|
||||||
|
JavaScriptCatalog.as_view(packages=['nalodeni']),
|
||||||
|
name='js-cat-website'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
"""
|
||||||
|
WSGI config for main project.
|
||||||
|
|
||||||
|
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||||
|
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
|
||||||
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "main.settings")
|
||||||
|
|
||||||
|
application = get_wsgi_application()
|
|
@ -0,0 +1,15 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "main.settings")
|
||||||
|
try:
|
||||||
|
from django.core.management import execute_from_command_line
|
||||||
|
except ImportError as exc:
|
||||||
|
raise ImportError(
|
||||||
|
"Couldn't import Django. Are you sure it's installed and "
|
||||||
|
"available on your PYTHONPATH environment variable? Did you "
|
||||||
|
"forget to activate a virtual environment?"
|
||||||
|
) from exc
|
||||||
|
execute_from_command_line(sys.argv)
|
|
@ -0,0 +1,262 @@
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
import hashlib
|
||||||
|
from datetime import datetime
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.utils.crypto import get_random_string
|
||||||
|
from django.core.mail import send_mail
|
||||||
|
|
||||||
|
from django.core.validators import validate_email
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
|
from django.conf import settings as appSettings
|
||||||
|
|
||||||
|
from . import models
|
||||||
|
|
||||||
|
# Get an instance of a logger
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Our user model
|
||||||
|
USER_MODEL = get_user_model()
|
||||||
|
|
||||||
|
class EmailTokenAuthBackend:
|
||||||
|
"""
|
||||||
|
Provides authorization via email workflow.
|
||||||
|
|
||||||
|
The user is verified by a token previously sent by email.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_user(self, user_id):
|
||||||
|
try:
|
||||||
|
return USER_MODEL.objects.get(pk=user_id)
|
||||||
|
except USER_MODEL.DoesNotExist:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def authenticate(self, request, emailToken=None):
|
||||||
|
if emailToken is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# do we have a user with this valid token ?
|
||||||
|
try:
|
||||||
|
u = USER_MODEL.objects.get(emailToken=emailToken)
|
||||||
|
|
||||||
|
if u.etStamp and (datetime.now() - u.etStamp).total_seconds() < int(appSettings.TOKEN_VALID_SEC):
|
||||||
|
u.emailToken = None
|
||||||
|
u.etStamp = None
|
||||||
|
u.save()
|
||||||
|
return u
|
||||||
|
else:
|
||||||
|
u.emailToken = None
|
||||||
|
u.etStamp = None
|
||||||
|
u.save()
|
||||||
|
raise ValidationError("Přihlašovací odkaz vypršel, nechte si poslat nový.")
|
||||||
|
|
||||||
|
except USER_MODEL.DoesNotExist:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# is this a first-time login, so the user has to be created ?
|
||||||
|
rslt = models.AppRegEmail.objects.filter(emailToken=emailToken)
|
||||||
|
if len(rslt) == 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
elif len(rslt) == 1:
|
||||||
|
reg = rslt[0]
|
||||||
|
if reg.etStamp and (datetime.now() - reg.etStamp).total_seconds() < int(appSettings.TOKEN_VALID_SEC):
|
||||||
|
rsltUsers = models.AppUser.objects.filter(username=reg.email)
|
||||||
|
if len(rsltUsers) != 0 :
|
||||||
|
raise ValidationError("Uživatelské jméno je již obsazeno.")
|
||||||
|
|
||||||
|
rsltUsers = models.AppUser.objects.filter(email=reg.email)
|
||||||
|
if len(rsltUsers) != 0 :
|
||||||
|
raise ValidationError("E-mailová adresa je již obsazena.")
|
||||||
|
|
||||||
|
# token valid, create the user
|
||||||
|
u = USER_MODEL()
|
||||||
|
u.username = reg.email
|
||||||
|
u.email = reg.email
|
||||||
|
u.postcode = reg.postcode
|
||||||
|
u.kind = reg.kind
|
||||||
|
u.interestedIn = reg.interestedIn
|
||||||
|
u.userform = reg.userform
|
||||||
|
u.dc_stamp = datetime.now()
|
||||||
|
u.save()
|
||||||
|
|
||||||
|
# remove the approved email from registration
|
||||||
|
reg.userform = None
|
||||||
|
reg.save()
|
||||||
|
reg.delete()
|
||||||
|
|
||||||
|
return u
|
||||||
|
else:
|
||||||
|
raise ValidationError("Registrační odkaz vypršel, nechte si poslat nový.")
|
||||||
|
|
||||||
|
else:
|
||||||
|
# multiple tokens, which should not happen
|
||||||
|
raise Exception("Multiple records with the same token.")
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def sendLoginToken(user):
|
||||||
|
"""
|
||||||
|
Generate and send a token to the user.
|
||||||
|
"""
|
||||||
|
|
||||||
|
emailToken = get_random_string(120)
|
||||||
|
|
||||||
|
user.emailToken = emailToken
|
||||||
|
user.etStamp = datetime.now()
|
||||||
|
user.save()
|
||||||
|
|
||||||
|
emailSubj = 'Piráti - nalodění - přihlašovací odkaz'
|
||||||
|
emailBody = """\
|
||||||
|
Dobrý den,
|
||||||
|
|
||||||
|
níže zasíláme přihlašovací odkaz do aplikace Pirátů "Nalodění":
|
||||||
|
|
||||||
|
{baseUrl}/prihlaseni/?t={emailToken}
|
||||||
|
|
||||||
|
Přihlásíte se kliknutím na odkaz, nebo jeho překopírováním do prohlížeče internetových stránek.
|
||||||
|
Odkaz je možné použít pouze jednou, v případě potřeby si nechte zaslat nový odkaz.
|
||||||
|
|
||||||
|
S pozdravem
|
||||||
|
Piráti
|
||||||
|
"""
|
||||||
|
|
||||||
|
send_mail(
|
||||||
|
emailSubj,
|
||||||
|
emailBody.format(
|
||||||
|
emailToken=emailToken,
|
||||||
|
baseUrl=appSettings.BASE_URL),
|
||||||
|
"nalodeni@pirati.cz",
|
||||||
|
[user.email], # email to ...
|
||||||
|
fail_silently=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def sendRegisterToken(f_email):
|
||||||
|
"""
|
||||||
|
Generate and send a token to the registered email.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Check for user non-existence with this email
|
||||||
|
f_email = f_email.strip().lower()
|
||||||
|
rslt = models.AppUser.objects.filter(email__iexact=f_email)
|
||||||
|
if len(rslt) == 0:
|
||||||
|
pass
|
||||||
|
elif len(rslt) == 1:
|
||||||
|
# send login token instead
|
||||||
|
return sendLoginToken(rslt[0])
|
||||||
|
else:
|
||||||
|
logger.error("More AppUser objects with the same email.")
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
# check registrations
|
||||||
|
rslt = models.AppRegEmail.objects.filter(email__iexact=f_email)
|
||||||
|
if len(rslt) == 1:
|
||||||
|
rt = rslt[0]
|
||||||
|
elif len(rslt) == 0:
|
||||||
|
rt = models.AppRegEmail()
|
||||||
|
rt.email = f_email
|
||||||
|
else:
|
||||||
|
logger.error("More AppRegEmail objects with the same email.")
|
||||||
|
return
|
||||||
|
|
||||||
|
sendRegisterTokenReg(rt)
|
||||||
|
|
||||||
|
|
||||||
|
def sendRegisterTokenReg(rt):
|
||||||
|
""" Send registration email to an already existing AppRegEmail instance 'rt'. """
|
||||||
|
# create token
|
||||||
|
emailToken = get_random_string(120)
|
||||||
|
|
||||||
|
rt.emailToken = emailToken
|
||||||
|
rt.etStamp = datetime.now()
|
||||||
|
rt.save()
|
||||||
|
|
||||||
|
emailSubj = 'Piráti - nalodění - registrační odkaz'
|
||||||
|
emailBody = """\
|
||||||
|
Dobrý den,
|
||||||
|
|
||||||
|
níže Vám zasíláme registrační odkaz do aplikace Pirátů "Nalodění":
|
||||||
|
|
||||||
|
{baseUrl}/prihlaseni/?t={emailToken}
|
||||||
|
|
||||||
|
V registraci pokračujte kliknutím na odkaz, nebo jeho překopírováním do prohlížeče internetových stránek.
|
||||||
|
Odkaz je možné použít pouze jednou, v případě potřeby si nechte zaslat nový odkaz.
|
||||||
|
|
||||||
|
S pozdravem
|
||||||
|
Piráti
|
||||||
|
"""
|
||||||
|
|
||||||
|
send_mail(
|
||||||
|
emailSubj,
|
||||||
|
emailBody.format(
|
||||||
|
emailToken=emailToken,
|
||||||
|
baseUrl=appSettings.BASE_URL),
|
||||||
|
"nalodeni@pirati.cz",
|
||||||
|
[rt.email], # email to ...
|
||||||
|
fail_silently=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def sendEmailContactVerificationToken(user):
|
||||||
|
""" Send validation email to user.email_contact. """
|
||||||
|
# The wanted new value, save it here so we do not accidentally change it,
|
||||||
|
# or someone else through the user-object.
|
||||||
|
ecw = user.email_contact
|
||||||
|
|
||||||
|
if ecw is None or ecw == "":
|
||||||
|
raise ValidationError("Chybí kontaktní email.")
|
||||||
|
|
||||||
|
# ValidationError se odchytava vyse
|
||||||
|
validate_email(ecw)
|
||||||
|
|
||||||
|
# create token
|
||||||
|
rand_str = get_random_string(120)
|
||||||
|
|
||||||
|
user.email_contact_token = "%s-%s" % ( int(datetime.now().timestamp()), rand_str )
|
||||||
|
user.email_contact_verified = False
|
||||||
|
user.save()
|
||||||
|
|
||||||
|
sha256hash = hashlib.sha256()
|
||||||
|
sha256hash.update(rand_str.encode('utf-8'))
|
||||||
|
sha256hash.update(ecw.encode('utf-8'))
|
||||||
|
|
||||||
|
emailToken = sha256hash.hexdigest()
|
||||||
|
emailSubj = 'Piráti - nalodění - ověření kontaktního emailu'
|
||||||
|
emailBody = """\
|
||||||
|
Dobrý den,
|
||||||
|
|
||||||
|
níže Vám zasíláme odkaz pro ověření platnosti kontaktní e-mailové adresy.
|
||||||
|
Změna adresy na {email_contact} byla vyžádána v aplikace Pirátů "Nalodění".
|
||||||
|
|
||||||
|
Adresu ověříte přihlášením se do aplikace Nalodění
|
||||||
|
(https://nalodeni.pirati.cz/prihlaseni/) a následným kliknutím na odkaz:
|
||||||
|
|
||||||
|
{baseUrl}/ja-pirat/profil/?t={emailToken}
|
||||||
|
|
||||||
|
Pokud jste tuto změnu nežádali, můžete tuto zprávu smazat.
|
||||||
|
|
||||||
|
S pozdravem
|
||||||
|
Piráti
|
||||||
|
"""
|
||||||
|
|
||||||
|
send_mail(
|
||||||
|
emailSubj,
|
||||||
|
emailBody.format(
|
||||||
|
email_contact = ecw,
|
||||||
|
emailToken=emailToken,
|
||||||
|
baseUrl=appSettings.BASE_URL),
|
||||||
|
"nalodeni@pirati.cz",
|
||||||
|
[ecw], # email to ...
|
||||||
|
fail_silently=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#TODO:: osetreni transakci (pro pripad utoku typu DDoS)
|
|
@ -0,0 +1,122 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"model": "nalodeni.interestregion",
|
||||||
|
"pk": 1,
|
||||||
|
"fields": {
|
||||||
|
"name": "Celostátní",
|
||||||
|
"tag": "news-republic"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.interestregion",
|
||||||
|
"pk": 3,
|
||||||
|
"fields": {
|
||||||
|
"name": "Hlavní město Praha",
|
||||||
|
"tag": "news-region-pha"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.interestregion",
|
||||||
|
"pk": 4,
|
||||||
|
"fields": {
|
||||||
|
"name": "Jihočeský kraj",
|
||||||
|
"tag": "news-region-jhc"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.interestregion",
|
||||||
|
"pk": 5,
|
||||||
|
"fields": {
|
||||||
|
"name": "Jihomoravský kraj",
|
||||||
|
"tag": "news-region-jhm"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.interestregion",
|
||||||
|
"pk": 6,
|
||||||
|
"fields": {
|
||||||
|
"name": "Karlovarský kraj",
|
||||||
|
"tag": "news-region-kvk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.interestregion",
|
||||||
|
"pk": 7,
|
||||||
|
"fields": {
|
||||||
|
"name": "Kraj Vysočina",
|
||||||
|
"tag": "news-region-vys"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.interestregion",
|
||||||
|
"pk": 8,
|
||||||
|
"fields": {
|
||||||
|
"name": "Královéhradecký kraj",
|
||||||
|
"tag": "news-region-khk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.interestregion",
|
||||||
|
"pk": 9,
|
||||||
|
"fields": {
|
||||||
|
"name": "Liberecký kraj",
|
||||||
|
"tag": "news-region-lbk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.interestregion",
|
||||||
|
"pk": 10,
|
||||||
|
"fields": {
|
||||||
|
"name": "Moravskoslezský kraj",
|
||||||
|
"tag": "news-region-msk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.interestregion",
|
||||||
|
"pk": 11,
|
||||||
|
"fields": {
|
||||||
|
"name": "Olomoucký kraj",
|
||||||
|
"tag": "news-region-olk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.interestregion",
|
||||||
|
"pk": 12,
|
||||||
|
"fields": {
|
||||||
|
"name": "Pardubický kraj",
|
||||||
|
"tag": "news-region-pak"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.interestregion",
|
||||||
|
"pk": 13,
|
||||||
|
"fields": {
|
||||||
|
"name": "Plzeňský kraj",
|
||||||
|
"tag": "news-region-plk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.interestregion",
|
||||||
|
"pk": 14,
|
||||||
|
"fields": {
|
||||||
|
"name": "Středočeský kraj",
|
||||||
|
"tag": "news-region-stc"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.interestregion",
|
||||||
|
"pk": 15,
|
||||||
|
"fields": {
|
||||||
|
"name": "Ústecký kraj",
|
||||||
|
"tag": "news-region-ulk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.interestregion",
|
||||||
|
"pk": 16,
|
||||||
|
"fields": {
|
||||||
|
"name": "Zlínský kraj",
|
||||||
|
"tag": "news-region-zlk"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,75 @@
|
||||||
|
- model: nalodeni.interestregion
|
||||||
|
pk: 1
|
||||||
|
fields:
|
||||||
|
name: Celostátní
|
||||||
|
tag: news-republic
|
||||||
|
- model: nalodeni.interestregion
|
||||||
|
pk: 3
|
||||||
|
fields:
|
||||||
|
name: Hlavní město Praha
|
||||||
|
tag: news-region-pha
|
||||||
|
- model: nalodeni.interestregion
|
||||||
|
pk: 4
|
||||||
|
fields:
|
||||||
|
name: Jihočeský kraj
|
||||||
|
tag: news-region-jhc
|
||||||
|
- model: nalodeni.interestregion
|
||||||
|
pk: 5
|
||||||
|
fields:
|
||||||
|
name: Jihomoravský kraj
|
||||||
|
tag: news-region-jhm
|
||||||
|
- model: nalodeni.interestregion
|
||||||
|
pk: 6
|
||||||
|
fields:
|
||||||
|
name: Karlovarský kraj
|
||||||
|
tag: news-region-kvk
|
||||||
|
- model: nalodeni.interestregion
|
||||||
|
pk: 7
|
||||||
|
fields:
|
||||||
|
name: Kraj Vysočina
|
||||||
|
tag: news-region-vys
|
||||||
|
- model: nalodeni.interestregion
|
||||||
|
pk: 8
|
||||||
|
fields:
|
||||||
|
name: Královéhradecký kraj
|
||||||
|
tag: news-region-khk
|
||||||
|
- model: nalodeni.interestregion
|
||||||
|
pk: 9
|
||||||
|
fields:
|
||||||
|
name: Liberecký kraj
|
||||||
|
tag: news-region-lbk
|
||||||
|
- model: nalodeni.interestregion
|
||||||
|
pk: 10
|
||||||
|
fields:
|
||||||
|
name: Moravskoslezský kraj
|
||||||
|
tag: news-region-msk
|
||||||
|
- model: nalodeni.interestregion
|
||||||
|
pk: 11
|
||||||
|
fields:
|
||||||
|
name: Olomoucký kraj
|
||||||
|
tag: news-region-olk
|
||||||
|
- model: nalodeni.interestregion
|
||||||
|
pk: 12
|
||||||
|
fields:
|
||||||
|
name: Pardubický kraj
|
||||||
|
tag: news-region-pak
|
||||||
|
- model: nalodeni.interestregion
|
||||||
|
pk: 13
|
||||||
|
fields:
|
||||||
|
name: Plzeňský kraj
|
||||||
|
tag: news-region-plk
|
||||||
|
- model: nalodeni.interestregion
|
||||||
|
pk: 14
|
||||||
|
fields:
|
||||||
|
name: Středočeský kraj
|
||||||
|
tag: news-region-stc
|
||||||
|
- model: nalodeni.interestregion
|
||||||
|
pk: 15
|
||||||
|
fields:
|
||||||
|
name: Ústecký kraj
|
||||||
|
tag: news-region-ulk
|
||||||
|
- model: nalodeni.interestregion
|
||||||
|
pk: 16
|
||||||
|
fields:
|
||||||
|
name: Zlínský kraj
|
||||||
|
tag: news-region-zlk
|
|
@ -0,0 +1,114 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"model": "nalodeni.userskill",
|
||||||
|
"pk": 1,
|
||||||
|
"fields": {
|
||||||
|
"name": "Roznášení Pirátských listů",
|
||||||
|
"tag": "akt_roznos-listu"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.userskill",
|
||||||
|
"pk": 2,
|
||||||
|
"fields": {
|
||||||
|
"name": "Hlídání dětí",
|
||||||
|
"tag": "akt_hlidani-deti"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.userskill",
|
||||||
|
"pk": 3,
|
||||||
|
"fields": {
|
||||||
|
"name": "Zajištění občerstvení",
|
||||||
|
"tag": "akt_catering"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.userskill",
|
||||||
|
"pk": 4,
|
||||||
|
"fields": {
|
||||||
|
"name": "Řidič (možnost převozu něčeho někam)",
|
||||||
|
"tag": "akt_ridic"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.userskill",
|
||||||
|
"pk": 5,
|
||||||
|
"fields": {
|
||||||
|
"name": "Psaní článků a tiskových zpráv",
|
||||||
|
"tag": "odb_psavec"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.userskill",
|
||||||
|
"pk": 6,
|
||||||
|
"fields": {
|
||||||
|
"name": "Fotografické práce",
|
||||||
|
"tag": "odb_foto"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.userskill",
|
||||||
|
"pk": 7,
|
||||||
|
"fields": {
|
||||||
|
"name": "Grafické práce",
|
||||||
|
"tag": "odb_grafik"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.userskill",
|
||||||
|
"pk": 8,
|
||||||
|
"fields": {
|
||||||
|
"name": "Připomínkování legislativních návrhů",
|
||||||
|
"tag": "odb_pripominkovani"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.userskill",
|
||||||
|
"pk": 9,
|
||||||
|
"fields": {
|
||||||
|
"name": "Hudební produkce (mám kapelu, jsem DJ, ... )",
|
||||||
|
"tag": "akt_hudebni"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.userskill",
|
||||||
|
"pk": 10,
|
||||||
|
"fields": {
|
||||||
|
"name": "Programování, IT skills",
|
||||||
|
"tag": "odb_it"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.userskill",
|
||||||
|
"pk": 11,
|
||||||
|
"fields": {
|
||||||
|
"name": "Mám prostor pro banner",
|
||||||
|
"tag": "akt_banner"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.userskill",
|
||||||
|
"pk": 12,
|
||||||
|
"fields": {
|
||||||
|
"name": "Mám prostor pro vlajku",
|
||||||
|
"tag": "akt_vlajka"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.userskill",
|
||||||
|
"pk": 13,
|
||||||
|
"fields": {
|
||||||
|
"name": "Mám možnost umístit reklamu na auto",
|
||||||
|
"tag": "akt_autopolep"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.userskill",
|
||||||
|
"pk": 14,
|
||||||
|
"fields": {
|
||||||
|
"name": "Pomoc s administrativou",
|
||||||
|
"tag": "odb_administrativa"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,70 @@
|
||||||
|
- model: nalodeni.userskill
|
||||||
|
pk: 1
|
||||||
|
fields:
|
||||||
|
name: Roznášení Pirátských listů
|
||||||
|
tag: akt_roznos-listu
|
||||||
|
- model: nalodeni.userskill
|
||||||
|
pk: 2
|
||||||
|
fields:
|
||||||
|
name: Hlídání dětí
|
||||||
|
tag: akt_hlidani-deti
|
||||||
|
- model: nalodeni.userskill
|
||||||
|
pk: 3
|
||||||
|
fields:
|
||||||
|
name: Zajištění občerstvení
|
||||||
|
tag: akt_catering
|
||||||
|
- model: nalodeni.userskill
|
||||||
|
pk: 4
|
||||||
|
fields:
|
||||||
|
name: Řidič (možnost převozu něčeho někam)
|
||||||
|
tag: akt_ridic
|
||||||
|
- model: nalodeni.userskill
|
||||||
|
pk: 5
|
||||||
|
fields:
|
||||||
|
name: Psaní článků a tiskových zpráv
|
||||||
|
tag: odb_psavec
|
||||||
|
- model: nalodeni.userskill
|
||||||
|
pk: 6
|
||||||
|
fields:
|
||||||
|
name: Fotografické práce
|
||||||
|
tag: odb_foto
|
||||||
|
- model: nalodeni.userskill
|
||||||
|
pk: 7
|
||||||
|
fields:
|
||||||
|
name: Grafické práce
|
||||||
|
tag: odb_grafik
|
||||||
|
- model: nalodeni.userskill
|
||||||
|
pk: 8
|
||||||
|
fields:
|
||||||
|
name: Připomínkování legislativních návrhů
|
||||||
|
tag: odb_pripominkovani
|
||||||
|
- model: nalodeni.userskill
|
||||||
|
pk: 9
|
||||||
|
fields:
|
||||||
|
name: Hudební produkce (mám kapelu, jsem DJ, ... )
|
||||||
|
tag: akt_hudebni
|
||||||
|
- model: nalodeni.userskill
|
||||||
|
pk: 10
|
||||||
|
fields:
|
||||||
|
name: Programování, IT skills
|
||||||
|
tag: odb_it
|
||||||
|
- model: nalodeni.userskill
|
||||||
|
pk: 11
|
||||||
|
fields:
|
||||||
|
name: Mám prostor pro banner
|
||||||
|
tag: akt_banner
|
||||||
|
- model: nalodeni.userskill
|
||||||
|
pk: 12
|
||||||
|
fields:
|
||||||
|
name: Mám prostor pro vlajku
|
||||||
|
tag: akt_vlajka
|
||||||
|
- model: nalodeni.userskill
|
||||||
|
pk: 13
|
||||||
|
fields:
|
||||||
|
name: Mám možnost umístit reklamu na auto
|
||||||
|
tag: akt_autopolep
|
||||||
|
- model: nalodeni.userskill
|
||||||
|
pk: 14
|
||||||
|
fields:
|
||||||
|
name: Pomoc s administrativou
|
||||||
|
tag: odb_administrativa
|
|
@ -0,0 +1,170 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"model": "nalodeni.usertopic",
|
||||||
|
"pk": 1,
|
||||||
|
"fields": {
|
||||||
|
"name": "Evropská unie",
|
||||||
|
"tag": "eu"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.usertopic",
|
||||||
|
"pk": 2,
|
||||||
|
"fields": {
|
||||||
|
"name": "Obrana",
|
||||||
|
"tag": "obrana"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.usertopic",
|
||||||
|
"pk": 3,
|
||||||
|
"fields": {
|
||||||
|
"name": "Informatika",
|
||||||
|
"tag": "informatika"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.usertopic",
|
||||||
|
"pk": 4,
|
||||||
|
"fields": {
|
||||||
|
"name": "Místní rozvoj",
|
||||||
|
"tag": "rozvoj"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.usertopic",
|
||||||
|
"pk": 5,
|
||||||
|
"fields": {
|
||||||
|
"name": "Práce a sociální věci",
|
||||||
|
"tag": "pracesoc"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.usertopic",
|
||||||
|
"pk": 6,
|
||||||
|
"fields": {
|
||||||
|
"name": "Doprava a logistika",
|
||||||
|
"tag": "doprava"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.usertopic",
|
||||||
|
"pk": 7,
|
||||||
|
"fields": {
|
||||||
|
"name": "Finance",
|
||||||
|
"tag": "finance"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.usertopic",
|
||||||
|
"pk": 8,
|
||||||
|
"fields": {
|
||||||
|
"name": "Kultura",
|
||||||
|
"tag": "kultura"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.usertopic",
|
||||||
|
"pk": 9,
|
||||||
|
"fields": {
|
||||||
|
"name": "Průmysl",
|
||||||
|
"tag": "prumysl"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.usertopic",
|
||||||
|
"pk": 10,
|
||||||
|
"fields": {
|
||||||
|
"name": "Obchod",
|
||||||
|
"tag": "obchod"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.usertopic",
|
||||||
|
"pk": 11,
|
||||||
|
"fields": {
|
||||||
|
"name": "Vnitro",
|
||||||
|
"tag": "vnitro"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.usertopic",
|
||||||
|
"pk": 12,
|
||||||
|
"fields": {
|
||||||
|
"name": "Otevřený stát",
|
||||||
|
"tag": "otevrstat"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.usertopic",
|
||||||
|
"pk": 13,
|
||||||
|
"fields": {
|
||||||
|
"name": "Zdravotnictví",
|
||||||
|
"tag": "zdravotnictvi"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.usertopic",
|
||||||
|
"pk": 14,
|
||||||
|
"fields": {
|
||||||
|
"name": "Zemědělství",
|
||||||
|
"tag": "zemedelstvi"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.usertopic",
|
||||||
|
"pk": 15,
|
||||||
|
"fields": {
|
||||||
|
"name": "Životní prostředí",
|
||||||
|
"tag": "zivprostredi"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.usertopic",
|
||||||
|
"pk": 16,
|
||||||
|
"fields": {
|
||||||
|
"name": "Školství",
|
||||||
|
"tag": "skolstvi"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.usertopic",
|
||||||
|
"pk": 17,
|
||||||
|
"fields": {
|
||||||
|
"name": "Spravedlnost a justice",
|
||||||
|
"tag": "spravedlnost"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.usertopic",
|
||||||
|
"pk": 18,
|
||||||
|
"fields": {
|
||||||
|
"name": "Legalizace",
|
||||||
|
"tag": "legalizace"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.usertopic",
|
||||||
|
"pk": 19,
|
||||||
|
"fields": {
|
||||||
|
"name": "Kontrola moci a mocných",
|
||||||
|
"tag": "kontrola"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.usertopic",
|
||||||
|
"pk": 20,
|
||||||
|
"fields": {
|
||||||
|
"name": "Exekuce",
|
||||||
|
"tag": "exekuce"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "nalodeni.usertopic",
|
||||||
|
"pk": 21,
|
||||||
|
"fields": {
|
||||||
|
"name": "Demokracie",
|
||||||
|
"tag": "demokracie"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,108 @@
|
||||||
|
#
|
||||||
|
# Vim skript pro generování položky z formátu "tag[mezera]name":
|
||||||
|
#
|
||||||
|
- model: nalodeni.usertopic
|
||||||
|
pk: 1
|
||||||
|
fields:
|
||||||
|
name: Evropská unie
|
||||||
|
tag: eu
|
||||||
|
- model: nalodeni.usertopic
|
||||||
|
pk: 2
|
||||||
|
fields:
|
||||||
|
name: Obrana
|
||||||
|
tag: obrana
|
||||||
|
- model: nalodeni.usertopic
|
||||||
|
pk: 3
|
||||||
|
fields:
|
||||||
|
name: Informatika
|
||||||
|
tag: informatika
|
||||||
|
- model: nalodeni.usertopic
|
||||||
|
pk: 4
|
||||||
|
fields:
|
||||||
|
name: Místní rozvoj
|
||||||
|
tag: rozvoj
|
||||||
|
- model: nalodeni.usertopic
|
||||||
|
pk: 5
|
||||||
|
fields:
|
||||||
|
name: Práce a sociální věci
|
||||||
|
tag: pracesoc
|
||||||
|
- model: nalodeni.usertopic
|
||||||
|
pk: 6
|
||||||
|
fields:
|
||||||
|
name: Doprava a logistika
|
||||||
|
tag: doprava
|
||||||
|
- model: nalodeni.usertopic
|
||||||
|
pk: 7
|
||||||
|
fields:
|
||||||
|
name: Finance
|
||||||
|
tag: finance
|
||||||
|
- model: nalodeni.usertopic
|
||||||
|
pk: 8
|
||||||
|
fields:
|
||||||
|
name: Kultura
|
||||||
|
tag: kultura
|
||||||
|
- model: nalodeni.usertopic
|
||||||
|
pk: 9
|
||||||
|
fields:
|
||||||
|
name: Průmysl
|
||||||
|
tag: prumysl
|
||||||
|
- model: nalodeni.usertopic
|
||||||
|
pk: 10
|
||||||
|
fields:
|
||||||
|
name: Obchod
|
||||||
|
tag: obchod
|
||||||
|
- model: nalodeni.usertopic
|
||||||
|
pk: 11
|
||||||
|
fields:
|
||||||
|
name: Vnitro
|
||||||
|
tag: vnitro
|
||||||
|
- model: nalodeni.usertopic
|
||||||
|
pk: 12
|
||||||
|
fields:
|
||||||
|
name: Otevřený stát
|
||||||
|
tag: otevrstat
|
||||||
|
- model: nalodeni.usertopic
|
||||||
|
pk: 13
|
||||||
|
fields:
|
||||||
|
name: Zdravotnictví
|
||||||
|
tag: zdravotnictvi
|
||||||
|
- model: nalodeni.usertopic
|
||||||
|
pk: 14
|
||||||
|
fields:
|
||||||
|
name: Zemědělství
|
||||||
|
tag: zemedelstvi
|
||||||
|
- model: nalodeni.usertopic
|
||||||
|
pk: 15
|
||||||
|
fields:
|
||||||
|
name: Životní prostředí
|
||||||
|
tag: zivprostredi
|
||||||
|
- model: nalodeni.usertopic
|
||||||
|
pk: 16
|
||||||
|
fields:
|
||||||
|
name: Školství
|
||||||
|
tag: skolstvi
|
||||||
|
- model: nalodeni.usertopic
|
||||||
|
pk: 17
|
||||||
|
fields:
|
||||||
|
name: Spravedlnost a justice
|
||||||
|
tag: spravedlnost
|
||||||
|
- model: nalodeni.usertopic
|
||||||
|
pk: 18
|
||||||
|
fields:
|
||||||
|
name: Legalizace
|
||||||
|
tag: legalizace
|
||||||
|
- model: nalodeni.usertopic
|
||||||
|
pk: 19
|
||||||
|
fields:
|
||||||
|
name: Kontrola moci a mocných
|
||||||
|
tag: kontrola
|
||||||
|
- model: nalodeni.usertopic
|
||||||
|
pk: 20
|
||||||
|
fields:
|
||||||
|
name: Exekuce
|
||||||
|
tag: exekuce
|
||||||
|
- model: nalodeni.usertopic
|
||||||
|
pk: 21
|
||||||
|
fields:
|
||||||
|
name: Demokracie
|
||||||
|
tag: demokracie
|
|
@ -0,0 +1,203 @@
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
from django.forms import ModelForm
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
|
from . import models
|
||||||
|
|
||||||
|
class AppUserForm(ModelForm):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(AppUserForm, self).__init__(*args, **kwargs)
|
||||||
|
self.fields['email'].disabled = True
|
||||||
|
self.fields['email_contact_active'].disabled = True
|
||||||
|
self.fields['dc_stamp'].disabled = True
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.AppUser
|
||||||
|
fields = ['first_name','last_name', 'city', 'postcode', 'district', 'kind',
|
||||||
|
'email', 'email_contact', 'email_contact_active', 'dc_stamp']
|
||||||
|
|
||||||
|
def clean_postcode(self):
|
||||||
|
data = self.cleaned_data['postcode']
|
||||||
|
if data is not None:
|
||||||
|
if data < 10000 or data > 99999:
|
||||||
|
raise ValidationError(_('PSČ musí být číslo mezi 10000 a 99999.'), code='invalid')
|
||||||
|
return data
|
||||||
|
|
||||||
|
def clean_first_name(self):
|
||||||
|
data = self.cleaned_data['first_name']
|
||||||
|
if data is not None:
|
||||||
|
if len(data) > 30:
|
||||||
|
raise ValidationError(_('Jméno může mít maximálně 30 znaků.'), code='invalid')
|
||||||
|
return data
|
||||||
|
|
||||||
|
def clean_last_name(self):
|
||||||
|
data = self.cleaned_data['last_name']
|
||||||
|
if data is not None:
|
||||||
|
if len(data) > 30:
|
||||||
|
raise ValidationError(_('Příjmení může mít maximálně 30 znaků.'), code='invalid')
|
||||||
|
return data
|
||||||
|
|
||||||
|
def clean_city(self):
|
||||||
|
data = self.cleaned_data['city']
|
||||||
|
if data is not None:
|
||||||
|
if len(data) > 50:
|
||||||
|
raise ValidationError(_('Město může mít maximálně 50 znaků.'), code='invalid')
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class AppUserSsoForm(ModelForm):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(AppUserSsoForm, self).__init__(*args, **kwargs)
|
||||||
|
self.fields['email'].disabled = True
|
||||||
|
self.fields['email_contact_active'].disabled = True
|
||||||
|
self.fields['dc_stamp'].disabled = True
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.AppUser
|
||||||
|
fields = ['city', 'postcode', 'district', 'kind',
|
||||||
|
'email', 'email_contact', 'email_contact_active', 'dc_stamp']
|
||||||
|
|
||||||
|
|
||||||
|
class AppRegEmailForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = models.AppRegEmail
|
||||||
|
fields = ['email', 'postcode', 'kind', 'interestedIn', 'data_consent']
|
||||||
|
|
||||||
|
def clean_postcode(self):
|
||||||
|
data = self.cleaned_data['postcode']
|
||||||
|
if data is not None:
|
||||||
|
if data < 10000 or data > 99999:
|
||||||
|
raise ValidationError(_('PSČ musí být číslo mezi 10000 a 99999.'), code='invalid')
|
||||||
|
return data
|
||||||
|
|
||||||
|
def clean_data_consent(self):
|
||||||
|
data = self.cleaned_data['data_consent']
|
||||||
|
if data is None or data == False:
|
||||||
|
raise ValidationError(_('Prosím, potvrďte souhlas se zpracováním osobních údajů.'), code='invalid')
|
||||||
|
return data
|
||||||
|
|
||||||
|
def clean_kind(self):
|
||||||
|
data = self.cleaned_data['kind']
|
||||||
|
if data is None:
|
||||||
|
raise ValidationError(_('Prosím vyberte, o co máte zájem.'), code='invalid')
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class AppRegFollowEmailForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = models.AppRegEmail
|
||||||
|
fields = ['email', 'data_consent']
|
||||||
|
|
||||||
|
def clean_data_consent(self):
|
||||||
|
data = self.cleaned_data['data_consent']
|
||||||
|
if data is None or data == False:
|
||||||
|
raise ValidationError(_('Prosím, potvrďte souhlas se zpracováním osobních údajů.'), code='invalid')
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class UserFormForm(ModelForm):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(UserFormForm, self).__init__(*args, **kwargs)
|
||||||
|
self.fields['skills'] = forms.ModelMultipleChoiceField(
|
||||||
|
label=_("Moje dovednosti (dokážu pomoci s)"),
|
||||||
|
widget=forms.CheckboxSelectMultiple,
|
||||||
|
queryset=models.UserSkill.objects.all(),
|
||||||
|
required=False)
|
||||||
|
self.fields['topics'] = forms.ModelMultipleChoiceField(
|
||||||
|
label=_("Zájmová témata"),
|
||||||
|
widget=forms.CheckboxSelectMultiple,
|
||||||
|
queryset=models.UserTopic.objects.all(),
|
||||||
|
required=False)
|
||||||
|
self.fields['regions'] = forms.ModelMultipleChoiceField(
|
||||||
|
label=_("Zájmové regiony"),
|
||||||
|
widget=forms.CheckboxSelectMultiple,
|
||||||
|
queryset=models.InterestRegion.objects.all(),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.UserForm
|
||||||
|
fields = ['skills','skills_note', 'topics','regions']
|
||||||
|
|
||||||
|
class UserFollowFormForm(ModelForm):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(UserFollowFormForm, self).__init__(*args, **kwargs)
|
||||||
|
self.fields['topics'] = forms.ModelMultipleChoiceField(
|
||||||
|
label=_("Zájmová témata"),
|
||||||
|
widget=forms.CheckboxSelectMultiple,
|
||||||
|
queryset=models.UserTopic.objects.all(),
|
||||||
|
required=False)
|
||||||
|
self.fields['regions'] = forms.ModelMultipleChoiceField(
|
||||||
|
label=_("Novinky zasílané e-mailem"),
|
||||||
|
widget=forms.CheckboxSelectMultiple,
|
||||||
|
queryset=models.InterestRegion.objects.all(),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.UserForm
|
||||||
|
fields = ['topics','regions']
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class EmailVizitkaForm(forms.Form):
|
||||||
|
name = forms.CharField(label=_('Jméno'), max_length=50)
|
||||||
|
email = forms.CharField(label=_('E-mail'), max_length=50, required=False)
|
||||||
|
nazev_funkce = forms.CharField(label=_('Název funkce'), max_length=50, required=False)
|
||||||
|
soc_fb = forms.CharField(label=_('Facebook odkaz'), max_length=50, required=False)
|
||||||
|
soc_in = forms.CharField(label=_('LinkedIn odkaz'), max_length=50, required=False)
|
||||||
|
phone = forms.CharField(label=_('Telefon'), max_length=50, required=False)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# Newsletters
|
||||||
|
##
|
||||||
|
class NewsletterForm(ModelForm):
|
||||||
|
#def __init__(self, *args, **kwargs):
|
||||||
|
# super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.Newsletter
|
||||||
|
exclude = []
|
||||||
|
|
||||||
|
class NewsMsgForm(ModelForm):
|
||||||
|
#def __init__(self, *args, **kwargs):
|
||||||
|
# super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.NewsMsg
|
||||||
|
fields = ['title', 'headerText', 'footerText',
|
||||||
|
'delivery_ts', 'sent_ts',
|
||||||
|
'testMailRecipients']
|
||||||
|
|
||||||
|
class NewsMsgBlockForm(ModelForm):
|
||||||
|
#def __init__(self, *args, **kwargs):
|
||||||
|
# super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.NewsMsgBlock
|
||||||
|
exclude = ['newsmsg']
|
||||||
|
|
||||||
|
|
||||||
|
class Euro2019InterestForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = models.Euro2019Interest
|
||||||
|
exclude = ['createdStamp', 'dc_stamp']
|
||||||
|
|
||||||
|
def clean_postcode(self):
|
||||||
|
data = self.cleaned_data['postcode']
|
||||||
|
if data is not None:
|
||||||
|
if data < 10000 or data > 99999:
|
||||||
|
raise ValidationError(_('PSČ musí být číslo mezi 10000 a 99999.'), code='invalid')
|
||||||
|
return data
|
||||||
|
|
||||||
|
def clean_data_consent(self):
|
||||||
|
data = self.cleaned_data['data_consent']
|
||||||
|
if data is None or data == False:
|
||||||
|
raise ValidationError(_('Prosím, potvrďte souhlas se zpracováním osobních údajů.'), code='invalid')
|
||||||
|
return data
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
# Generated by Django 2.0.2 on 2018-02-28 12:12
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
import django.contrib.auth.models
|
||||||
|
import django.contrib.auth.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.utils.timezone
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('auth', '0009_alter_user_last_name_max_length'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='AppUser',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||||
|
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
||||||
|
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
||||||
|
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
|
||||||
|
('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')),
|
||||||
|
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
|
||||||
|
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
|
||||||
|
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
||||||
|
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||||
|
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
||||||
|
('ssoUid', models.CharField(blank=True, default=None, max_length=40, null=True, verbose_name='Keycloak SSO user ID')),
|
||||||
|
('postcode', models.IntegerField(blank=True, default='', null=True, verbose_name='PSČ')),
|
||||||
|
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
|
||||||
|
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name_plural': 'AppUsers',
|
||||||
|
'verbose_name': 'AppUser',
|
||||||
|
},
|
||||||
|
managers=[
|
||||||
|
('objects', django.contrib.auth.models.UserManager()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='AppSetting',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('key', models.CharField(max_length=64, verbose_name='Key')),
|
||||||
|
('value', models.CharField(max_length=128, verbose_name='Value')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='UserSetting',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('key', models.CharField(max_length=64, verbose_name='Key')),
|
||||||
|
('value', models.CharField(max_length=128, null=True, verbose_name='Value')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='user')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='appsetting',
|
||||||
|
unique_together={('key',)},
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='usersetting',
|
||||||
|
unique_together={('user', 'key')},
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='appuser',
|
||||||
|
unique_together={('email',)},
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Generated by Django 2.0.2 on 2018-02-28 16:27
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='postcode',
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.0.2 on 2018-02-28 16:28
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0002_remove_appuser_postcode'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='district',
|
||||||
|
field=models.IntegerField(blank=True, choices=[(0, 'Hlavní město Praha'), (1, 'Jihočeský kraj'), (2, 'Jihomoravský kraj'), (3, 'Karlovarský kraj'), (4, 'Kraj Vysočina'), (5, 'Královéhradecký kraj'), (6, 'Liberecký kraj'), (7, 'Moravskoslezský kraj'), (8, 'Olomoucký kraj'), (9, 'Pardubický kraj'), (10, 'Plzeňský kraj'), (11, 'Středočeský kraj'), (12, 'Ústecký kraj'), (13, 'Zlínský kraj')], null=True, verbose_name='Kraj'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.0.2 on 2018-02-28 16:28
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0003_appuser_district'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='postcode',
|
||||||
|
field=models.IntegerField(blank=True, null=True, verbose_name='PSČ'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-03-12 01:03
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0004_appuser_postcode'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='emailToken',
|
||||||
|
field=models.CharField(blank=True, default=None, max_length=120, null=True, verbose_name='Email login token'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-03-12 01:12
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0005_appuser_emailtoken'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='appuser',
|
||||||
|
unique_together={('emailToken',), ('email',)},
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,35 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-03-12 02:13
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0006_auto_20180312_0112'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='AppRegEmail',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('email', models.CharField(blank=True, default='', max_length=100, null=True, verbose_name='email')),
|
||||||
|
('token', models.CharField(blank=True, default=None, max_length=120, null=True, verbose_name='Registration token')),
|
||||||
|
('rtts', models.DateTimeField(blank=True, default=None, editable=False, null=True, verbose_name='Registration token timestamp')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name_plural': 'AppRegEmails',
|
||||||
|
'verbose_name': 'AppRegEmail',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='etDate',
|
||||||
|
field=models.DateTimeField(blank=True, default=None, editable=False, null=True, verbose_name='Email token timestamp'),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='appregemail',
|
||||||
|
unique_together={('token',)},
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-03-12 02:32
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0007_auto_20180312_0213'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='city',
|
||||||
|
field=models.CharField(blank=True, default=None, max_length=120, null=True, verbose_name='Město'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-03-23 11:45
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0008_appuser_city'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='appregemail',
|
||||||
|
old_name='rtts',
|
||||||
|
new_name='timestamp',
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='appregemail',
|
||||||
|
unique_together={('token',), ('email',)},
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-03-23 12:14
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0009_auto_20180323_1145'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='appuser',
|
||||||
|
old_name='etDate',
|
||||||
|
new_name='etStamp',
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,27 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-03-23 13:12
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0010_auto_20180323_1214'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='appregemail',
|
||||||
|
old_name='token',
|
||||||
|
new_name='emailToken',
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='appregemail',
|
||||||
|
old_name='timestamp',
|
||||||
|
new_name='etStamp',
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='appregemail',
|
||||||
|
unique_together={('email',), ('emailToken',)},
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-03-25 18:51
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0011_auto_20180323_1312'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='kind',
|
||||||
|
field=models.IntegerField(choices=[(0, 'zájemce o novinky'), (1, 'příznivec'), (2, 'zájemce o členství'), (3, 'člen')], default=0, verbose_name='Stav'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='status',
|
||||||
|
field=models.IntegerField(choices=[(0, 'nový'), (1, 'ok')], default=0, verbose_name='Stav'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-03-25 21:09
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0012_auto_20180325_1851'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='district',
|
||||||
|
field=models.IntegerField(choices=[(0, 'Hlavní město Praha'), (1, 'Jihočeský kraj'), (2, 'Jihomoravský kraj'), (3, 'Karlovarský kraj'), (4, 'Kraj Vysočina'), (5, 'Královéhradecký kraj'), (6, 'Liberecký kraj'), (7, 'Moravskoslezský kraj'), (8, 'Olomoucký kraj'), (9, 'Pardubický kraj'), (10, 'Plzeňský kraj'), (11, 'Středočeský kraj'), (12, 'Ústecký kraj'), (13, 'Zlínský kraj')], default=0, verbose_name='Kraj'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='kind',
|
||||||
|
field=models.IntegerField(choices=[(0, 'dostávat novinky'), (1, 'stát se příznivcem'), (2, 'stát se členem'), (3, 'již jsem člen')], default=0, verbose_name='Chci'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='status',
|
||||||
|
field=models.IntegerField(choices=[(0, 'nový'), (1, 'registrovaný')], default=1, verbose_name='Stav'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-03-25 21:10
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0013_auto_20180325_2109'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='postcode',
|
||||||
|
field=models.IntegerField(default=-1, verbose_name='PSČ'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-03-25 23:26
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0014_auto_20180325_2110'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='postcode',
|
||||||
|
field=models.IntegerField(null=True, verbose_name='PSČ'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='status',
|
||||||
|
field=models.IntegerField(choices=[(0, 'nový'), (1, 'registrovaný')], default=0, verbose_name='Stav'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-03-26 09:26
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0015_auto_20180325_2326'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='interestedIn',
|
||||||
|
field=models.CharField(blank=True, max_length=150, null=True, verbose_name='Dovednosti (chci pomoci s)'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-04-10 11:44
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0016_appuser_interestedin'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appregemail',
|
||||||
|
name='interestedIn',
|
||||||
|
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Poznámka (info pro koordinátora)'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appregemail',
|
||||||
|
name='kind',
|
||||||
|
field=models.IntegerField(choices=[(0, 'dostávat novinky'), (1, 'stát se příznivcem'), (2, 'stát se členem'), (3, 'již jsem člen'), (3, 'již pirátím jinak')], default=0, verbose_name='Chci'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='kind',
|
||||||
|
field=models.IntegerField(choices=[(0, 'dostávat novinky'), (1, 'stát se příznivcem'), (2, 'stát se členem'), (3, 'již jsem člen'), (3, 'již pirátím jinak')], default=0, verbose_name='Chci'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-04-10 15:47
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0017_auto_20180410_1144'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appregemail',
|
||||||
|
name='postcode',
|
||||||
|
field=models.IntegerField(null=True, verbose_name='PSČ'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appregemail',
|
||||||
|
name='kind',
|
||||||
|
field=models.IntegerField(blank=True, choices=[(0, 'dostávat novinky'), (1, 'stát se příznivcem'), (2, 'stát se členem'), (3, 'již jsem člen'), (3, 'již pirátím jinak')], default=0, null=True, verbose_name='Chci'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-06-17 11:04
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0018_auto_20180410_1547'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='email_contact',
|
||||||
|
field=models.CharField(blank=True, default='', max_length=100, null=True, verbose_name='Kontaktní email'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appregemail',
|
||||||
|
name='postcode',
|
||||||
|
field=models.IntegerField(null=True, verbose_name='PSČ (kvůli dělení do krajů)'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='interestedIn',
|
||||||
|
field=models.CharField(blank=True, max_length=150, null=True, verbose_name='Moje dovednosti (chci pomoci s)'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,33 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-06-17 11:46
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0019_auto_20180617_1104'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='email_contact_active',
|
||||||
|
field=models.CharField(blank=True, default='', max_length=100, null=True, verbose_name='Kontaktní e-mail aktivní'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appregemail',
|
||||||
|
name='kind',
|
||||||
|
field=models.IntegerField(blank=True, choices=[(0, 'dostávat novinky'), (1, 'se stát příznivcem'), (2, 'se stát členem'), (3, 'již jsem člen'), (3, 'již pirátím jinak')], default=0, null=True, verbose_name='Chci'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='email_contact',
|
||||||
|
field=models.CharField(blank=True, default='', max_length=100, null=True, verbose_name='Kontaktní e-mail'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='kind',
|
||||||
|
field=models.IntegerField(choices=[(0, 'dostávat novinky'), (1, 'se stát příznivcem'), (2, 'se stát členem'), (3, 'již jsem člen'), (3, 'již pirátím jinak')], default=0, verbose_name='Chci'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-06-17 13:10
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0020_auto_20180617_1146'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='email_contact_verified',
|
||||||
|
field=models.BooleanField(default=False, verbose_name='Kontaktní e-mail byl ověřen.'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='email_contact',
|
||||||
|
field=models.EmailField(blank=True, default='', max_length=100, null=True, verbose_name='Kontaktní e-mail'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='email_contact_active',
|
||||||
|
field=models.EmailField(blank=True, default='', max_length=100, null=True, verbose_name='Kontaktní e-mail aktivní'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-06-17 13:44
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0021_auto_20180617_1310'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='email_contact_token',
|
||||||
|
field=models.CharField(blank=True, default=None, max_length=150, null=True, verbose_name='Ověřovací token pro kontaktní email.'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-06-18 07:49
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0022_appuser_email_contact_token'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='ts_for_ldap_sync',
|
||||||
|
field=models.DateTimeField(blank=True, default=datetime.datetime.now, null=True, verbose_name='Timestamp pro LDAP synchronizaci'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-06-25 01:15
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0023_appuser_ts_for_ldap_sync'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appregemail',
|
||||||
|
name='data_consent',
|
||||||
|
field=models.BooleanField(default=False, verbose_name='Souhlasím se zpracováním os. údajů.'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-06-25 01:17
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0024_appregemail_data_consent'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appregemail',
|
||||||
|
name='dc_stamp',
|
||||||
|
field=models.DateTimeField(blank=True, default=None, editable=False, null=True, verbose_name='Data consent timestamp'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-06-25 01:25
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0025_appregemail_dc_stamp'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='dc_stamp',
|
||||||
|
field=models.DateTimeField(blank=True, default=None, editable=False, null=True, verbose_name='Data consent timestamp'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,75 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-06-25 03:14
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0026_appuser_dc_stamp'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Newsletter',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(blank=True, max_length=100, null=True, verbose_name='Název')),
|
||||||
|
('tag', models.CharField(blank=True, max_length=20, null=True, verbose_name='Značka (tag) newsletteru')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='UserForm',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('newsletters', models.ManyToManyField(blank=True, to='nalodeni.Newsletter')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='UserSkill',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(blank=True, max_length=100, null=True, verbose_name='Název')),
|
||||||
|
('tag', models.CharField(blank=True, max_length=20, null=True, verbose_name='Značka (tag) dovednosti')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='UserTopic',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(blank=True, max_length=100, null=True, verbose_name='Název')),
|
||||||
|
('tag', models.CharField(blank=True, max_length=20, null=True, verbose_name='Značka (tag) tématu')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appregemail',
|
||||||
|
name='data_consent',
|
||||||
|
field=models.BooleanField(default=False, verbose_name='Souhlasím se zpracováním osobních údajů.'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='dc_stamp',
|
||||||
|
field=models.DateTimeField(blank=True, default=None, null=True, verbose_name='Data consent timestamp'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='userform',
|
||||||
|
name='skills',
|
||||||
|
field=models.ManyToManyField(blank=True, to='nalodeni.UserSkill'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='userform',
|
||||||
|
name='topics',
|
||||||
|
field=models.ManyToManyField(blank=True, to='nalodeni.UserTopic'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appregemail',
|
||||||
|
name='userform',
|
||||||
|
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='nalodeni.UserForm', verbose_name='dotazník'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='userform',
|
||||||
|
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='nalodeni.UserForm', verbose_name='dotazník'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-07-07 15:08
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0027_auto_20180625_0314'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='userform',
|
||||||
|
name='newsletters',
|
||||||
|
field=models.ManyToManyField(blank=True, to='nalodeni.Newsletter', verbose_name='Zasílané novinky emailem'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='userform',
|
||||||
|
name='skills',
|
||||||
|
field=models.ManyToManyField(blank=True, to='nalodeni.UserSkill', verbose_name='Dovednosti'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='userform',
|
||||||
|
name='topics',
|
||||||
|
field=models.ManyToManyField(blank=True, to='nalodeni.UserTopic', verbose_name='Zájmová témata'),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='appuser',
|
||||||
|
unique_together={('email',), ('emailToken',), ('username',)},
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-07-07 15:36
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0028_auto_20180707_1508'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='createdStamp',
|
||||||
|
field=models.DateTimeField(default=datetime.datetime.now, editable=False, verbose_name='Uživatel vytvořen'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-07-07 15:41
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0029_appuser_createdstamp'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appregemail',
|
||||||
|
name='createdStamp',
|
||||||
|
field=models.DateTimeField(default=datetime.datetime.now, editable=False, verbose_name='Uživatel vytvořen'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,60 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-08-15 06:39
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0030_appregemail_createdstamp'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='newsletter',
|
||||||
|
options={'ordering': ['name']},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='userskill',
|
||||||
|
options={'ordering': ['tag']},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='usertopic',
|
||||||
|
options={'ordering': ['name']},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='userform',
|
||||||
|
name='skills_note',
|
||||||
|
field=models.CharField(blank=True, default=None, max_length=250, null=True, verbose_name='Poznámka k dovednostem'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appregemail',
|
||||||
|
name='email',
|
||||||
|
field=models.CharField(blank=True, default='', max_length=100, null=True, verbose_name='e-mail'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appregemail',
|
||||||
|
name='kind',
|
||||||
|
field=models.IntegerField(blank=True, choices=[(0, 'dostávat novinky'), (2, 'pomáhat jako dobrovolník'), (2, 'pomáhat jako expert'), (1, 'se stát příznivcem'), (2, 'se stát členem'), (3, 'již jsem člen'), (3, 'již pirátím jinak')], default=0, null=True, verbose_name='Chci'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='emailToken',
|
||||||
|
field=models.CharField(blank=True, default=None, max_length=120, null=True, verbose_name='E-mail login token'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='email_contact_token',
|
||||||
|
field=models.CharField(blank=True, default=None, max_length=150, null=True, verbose_name='Ověřovací token pro kontaktní e-mail.'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='etStamp',
|
||||||
|
field=models.DateTimeField(blank=True, default=None, editable=False, null=True, verbose_name='E-mail token timestamp'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='kind',
|
||||||
|
field=models.IntegerField(choices=[(0, 'dostávat novinky'), (2, 'pomáhat jako dobrovolník'), (2, 'pomáhat jako expert'), (1, 'se stát příznivcem'), (2, 'se stát členem'), (3, 'již jsem člen'), (3, 'již pirátím jinak')], default=0, verbose_name='Chci'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-08-18 17:28
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0031_auto_20180815_0639'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameModel(
|
||||||
|
old_name='Newsletter',
|
||||||
|
new_name='InterestRegion',
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='userform',
|
||||||
|
name='newsletters',
|
||||||
|
field=models.ManyToManyField(blank=True, to='nalodeni.InterestRegion', verbose_name='Zájmové regiony'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-08-18 17:29
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0032_auto_20180818_1728'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='userform',
|
||||||
|
old_name='newsletters',
|
||||||
|
new_name='regions',
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,81 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-08-18 17:30
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0033_auto_20180818_1729'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='NewsCond',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('group', models.IntegerField(default=False, verbose_name='Skupina podmínek')),
|
||||||
|
('neg', models.BooleanField(default=False, verbose_name='Negovat podmínku')),
|
||||||
|
('kind', models.IntegerField(choices=[(0, 'Dovednost'), (1, 'Téma'), (2, 'Území')], default=0, verbose_name='Druh')),
|
||||||
|
('opt_id', models.IntegerField(verbose_name='ID vybrané možnosti')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Newsletter',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(blank=True, max_length=150, null=True, verbose_name='Název')),
|
||||||
|
('desc', models.CharField(blank=True, max_length=500, null=True, verbose_name='Popis')),
|
||||||
|
('enabled', models.BooleanField(default=False, verbose_name='Aktivní')),
|
||||||
|
('managed_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='newsletter_managed', to=settings.AUTH_USER_MODEL, verbose_name='Správce newsletteru')),
|
||||||
|
('sent_by', models.ManyToManyField(blank=True, related_name='newsletter_sent', to=settings.AUTH_USER_MODEL, verbose_name='Odesílatelé')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='NewsMsg',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created_ts', models.DateTimeField(default=datetime.datetime.now, editable=False, verbose_name='Datum vytvoření')),
|
||||||
|
('delivery_ts', models.DateTimeField(blank=True, null=True, verbose_name='Datum plánovaného rozeslání')),
|
||||||
|
('sent_ts', models.DateTimeField(blank=True, null=True, verbose_name='Datum skutečného rozeslání')),
|
||||||
|
('title', models.CharField(max_length=150, null=True, verbose_name='Název, předmět emailu')),
|
||||||
|
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Vytvořil')),
|
||||||
|
('news', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='nalodeni.Newsletter', verbose_name='Newsletter')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='NewsMsgBlock',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('order', models.IntegerField(default=False, verbose_name='Pořadí')),
|
||||||
|
('heading', models.CharField(max_length=150, null=True, verbose_name='Nadpis')),
|
||||||
|
('content', models.TextField(null=True, verbose_name='Text (i validované HTML)')),
|
||||||
|
('link', models.CharField(max_length=500, null=True, verbose_name='Odkaz')),
|
||||||
|
('link_text', models.CharField(max_length=100, null=True, verbose_name='Text odkazu')),
|
||||||
|
('img_url', models.CharField(max_length=500, null=True, verbose_name='Obrázek (URL)')),
|
||||||
|
('img_thumb_url', models.CharField(max_length=500, null=True, verbose_name='Obrázek náhledu (URL)')),
|
||||||
|
('img_label', models.CharField(max_length=100, null=True, verbose_name='Popis obrázku')),
|
||||||
|
('nmsg', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='nalodeni.NewsMsg', verbose_name='Zpráva')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='NewsMsgReply',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('comment', models.CharField(blank=True, max_length=250, null=True, verbose_name='Poznámka')),
|
||||||
|
('rating_usefullness', models.IntegerField(blank=True, default=0, null=True, verbose_name='Využiji to')),
|
||||||
|
('rating_interest', models.IntegerField(blank=True, default=0, null=True, verbose_name='Zajímá mě to')),
|
||||||
|
('rating_action', models.IntegerField(blank=True, default=0, null=True, verbose_name='Zapojím se / Pomůžu')),
|
||||||
|
('block', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='nalodeni.NewsMsgBlock', verbose_name='Část zprávy')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Vytvořil')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='newscond',
|
||||||
|
name='news',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='nalodeni.Newsletter', verbose_name='Newsletter'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-08-18 19:19
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0034_auto_20180818_1730'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='newsletter',
|
||||||
|
name='is_del',
|
||||||
|
field=models.BooleanField(default=False, editable=False, verbose_name='Smazaný'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-08-18 19:33
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0035_newsletter_is_del'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='newsletter',
|
||||||
|
name='kind',
|
||||||
|
field=models.IntegerField(choices=[(0, 'Týdně'), (1, 'Měsíčně'), (2, 'Nepravidelně')], default=1, verbose_name='Perioda zasílání'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-08-18 19:34
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0036_newsletter_kind'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='newsletter',
|
||||||
|
old_name='kind',
|
||||||
|
new_name='period',
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-08-18 22:23
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0037_auto_20180818_1934'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='newsmsg',
|
||||||
|
name='is_del',
|
||||||
|
field=models.BooleanField(default=False, editable=False, verbose_name='Smazaný'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-08-19 02:21
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0038_newsmsg_is_del'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='newsmsgblock',
|
||||||
|
old_name='nmsg',
|
||||||
|
new_name='newsmsg',
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,43 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-08-19 02:22
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0039_auto_20180819_0221'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='newsmsgblock',
|
||||||
|
name='content',
|
||||||
|
field=models.TextField(blank=True, null=True, verbose_name='Text (i validované HTML)'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='newsmsgblock',
|
||||||
|
name='img_label',
|
||||||
|
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Popis obrázku'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='newsmsgblock',
|
||||||
|
name='img_thumb_url',
|
||||||
|
field=models.CharField(blank=True, max_length=500, null=True, verbose_name='Obrázek náhledu (URL)'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='newsmsgblock',
|
||||||
|
name='img_url',
|
||||||
|
field=models.CharField(blank=True, max_length=500, null=True, verbose_name='Obrázek (URL)'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='newsmsgblock',
|
||||||
|
name='link',
|
||||||
|
field=models.CharField(blank=True, max_length=500, null=True, verbose_name='Odkaz'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='newsmsgblock',
|
||||||
|
name='link_text',
|
||||||
|
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Text odkazu'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-08-19 02:54
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0040_auto_20180819_0222'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='newsmsgblock',
|
||||||
|
options={'ordering': ['order', 'heading']},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='newsmsg',
|
||||||
|
name='introText',
|
||||||
|
field=models.CharField(max_length=150, null=True, verbose_name='Úvodník'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-08-19 02:55
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0041_auto_20180819_0254'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='newsmsg',
|
||||||
|
name='introText',
|
||||||
|
field=models.TextField(null=True, verbose_name='Úvodník'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-08-19 02:57
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0042_auto_20180819_0255'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='newsmsg',
|
||||||
|
name='introText',
|
||||||
|
field=models.TextField(blank=True, null=True, verbose_name='Úvodník'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-08-19 08:47
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0043_auto_20180819_0257'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='newsmsg',
|
||||||
|
old_name='introText',
|
||||||
|
new_name='headerText',
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-08-19 08:48
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0044_auto_20180819_0847'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='newsmsg',
|
||||||
|
name='footerText',
|
||||||
|
field=models.TextField(blank=True, null=True, verbose_name='Patička'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='newsmsg',
|
||||||
|
name='headerText',
|
||||||
|
field=models.TextField(blank=True, null=True, verbose_name='Hlavička'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-08-19 09:00
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0045_auto_20180819_0848'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='newsmsgblock',
|
||||||
|
name='heading',
|
||||||
|
field=models.CharField(blank=True, max_length=150, null=True, verbose_name='Nadpis'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-08-19 10:01
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0046_auto_20180819_0900'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='newsletter',
|
||||||
|
name='recipients',
|
||||||
|
field=models.CharField(blank=True, max_length=500, null=True, verbose_name='Příjemci (pomocí tagů)'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-08-19 10:25
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0047_newsletter_recipients'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='interestregion',
|
||||||
|
unique_together={('tag',)},
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='userskill',
|
||||||
|
unique_together={('tag',)},
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='usertopic',
|
||||||
|
unique_together={('tag',)},
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Generated by Django 2.0.3 on 2018-11-23 16:07
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0048_auto_20180819_1025'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appregemail',
|
||||||
|
name='kind',
|
||||||
|
field=models.IntegerField(blank=True, choices=[(0, 'dostávat novinky'), (3, 'pomáhat jako dobrovolník'), (4, 'pomáhat jako expert'), (1, 'se stát příznivcem'), (2, 'se stát členem'), (5, 'již jsem člen'), (6, 'již pirátím jinak')], default=0, null=True, verbose_name='Chci'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='district',
|
||||||
|
field=models.IntegerField(blank=True, choices=[(0, 'Hlavní město Praha'), (1, 'Jihočeský kraj'), (2, 'Jihomoravský kraj'), (3, 'Karlovarský kraj'), (4, 'Kraj Vysočina'), (5, 'Královéhradecký kraj'), (6, 'Liberecký kraj'), (7, 'Moravskoslezský kraj'), (8, 'Olomoucký kraj'), (9, 'Pardubický kraj'), (10, 'Plzeňský kraj'), (11, 'Středočeský kraj'), (12, 'Ústecký kraj'), (13, 'Zlínský kraj')], default=None, null=True, verbose_name='Kraj'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='kind',
|
||||||
|
field=models.IntegerField(choices=[(0, 'dostávat novinky'), (3, 'pomáhat jako dobrovolník'), (4, 'pomáhat jako expert'), (1, 'se stát příznivcem'), (2, 'se stát členem'), (5, 'již jsem člen'), (6, 'již pirátím jinak')], default=0, verbose_name='Chci'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Generated by Django 2.0.3 on 2019-02-20 23:58
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0049_auto_20181123_1607'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='appuser',
|
||||||
|
options={'ordering': ('username',), 'verbose_name': 'AppUser', 'verbose_name_plural': 'AppUsers'},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='newsmsg',
|
||||||
|
name='testMailRecipients',
|
||||||
|
field=models.CharField(blank=True, max_length=150, null=True, verbose_name='Cc adresáti testovacího emailu'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Generated by Django 2.0.3 on 2019-02-21 00:35
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0050_auto_20190220_2358'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='newsletter',
|
||||||
|
name='replyToEmail',
|
||||||
|
field=models.CharField(blank=True, max_length=150, null=True, verbose_name='E-mailová adresa pro odpovědi'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='newsmsg',
|
||||||
|
name='testMailRecipients',
|
||||||
|
field=models.CharField(blank=True, max_length=150, null=True, verbose_name='Přidaní adresáti testovacího emailu'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Generated by Django 2.0.3 on 2019-03-03 11:51
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0051_auto_20190221_0035'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Euro2019Interest',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('createdStamp', models.DateTimeField(default=datetime.datetime.now, editable=False, verbose_name='Uživatel vytvořen')),
|
||||||
|
('email', models.CharField(max_length=100, null=True, verbose_name='e-mail')),
|
||||||
|
('district', models.IntegerField(blank=True, choices=[(0, 'Hlavní město Praha'), (1, 'Jihočeský kraj'), (2, 'Jihomoravský kraj'), (3, 'Karlovarský kraj'), (4, 'Kraj Vysočina'), (5, 'Královéhradecký kraj'), (6, 'Liberecký kraj'), (7, 'Moravskoslezský kraj'), (8, 'Olomoucký kraj'), (9, 'Pardubický kraj'), (10, 'Plzeňský kraj'), (11, 'Středočeský kraj'), (12, 'Ústecký kraj'), (13, 'Zlínský kraj')], default=None, null=True, verbose_name='Kraj')),
|
||||||
|
('postcode', models.IntegerField(null=True, verbose_name='PSČ (kvůli dělení do krajů)')),
|
||||||
|
('want_info', models.BooleanField(default=True, verbose_name='Chci dostávat newslettery')),
|
||||||
|
('want_campaign', models.BooleanField(default=True, verbose_name='Chci rozdávat Pirátské listy')),
|
||||||
|
('want_be_active', models.BooleanField(default=True, verbose_name='Chci se podílet chodu kampaně')),
|
||||||
|
('note', models.CharField(blank=True, max_length=100, null=True, verbose_name='Poznámka (info pro koordinátora)')),
|
||||||
|
('data_consent', models.BooleanField(default=False, verbose_name='Souhlasím se zpracováním osobních údajů')),
|
||||||
|
('dc_stamp', models.DateTimeField(blank=True, default=None, editable=False, null=True, verbose_name='Data consent timestamp')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='euro2019interest',
|
||||||
|
unique_together={('email',)},
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Generated by Django 2.0.3 on 2019-03-03 12:07
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0052_auto_20190303_1151'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='euro2019interest',
|
||||||
|
name='want_be_active',
|
||||||
|
field=models.BooleanField(default=False, verbose_name='Chci se podílet chodu kampaně'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='euro2019interest',
|
||||||
|
name='want_campaign',
|
||||||
|
field=models.BooleanField(default=False, verbose_name='Chci rozdávat Pirátské listy'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Generated by Django 2.0.2 on 2018-02-28 16:31
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='district',
|
||||||
|
field=models.IntegerField(blank=True, choices=[(0, 'Hlavní město Praha'), (1, 'Jihočeský kraj'), (2, 'Jihomoravský kraj'), (3, 'Karlovarský kraj'), (4, 'Kraj Vysočina'), (5, 'Královéhradecký kraj'), (6, 'Liberecký kraj'), (7, 'Moravskoslezský kraj'), (8, 'Olomoucký kraj'), (9, 'Pardubický kraj'), (10, 'Plzeňský kraj'), (11, 'Středočeský kraj'), (12, 'Ústecký kraj'), (13, 'Zlínský kraj')], null=True, verbose_name='Kraj'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='postcode',
|
||||||
|
field=models.IntegerField(blank=True, null=True, verbose_name='PSČ'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Generated by Django 2.0.2 on 2018-02-28 16:27
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='postcode',
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.0.2 on 2018-02-28 16:28
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0002_remove_appuser_postcode'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='district',
|
||||||
|
field=models.IntegerField(blank=True, choices=[(0, 'Hlavní město Praha'), (1, 'Jihočeský kraj'), (2, 'Jihomoravský kraj'), (3, 'Karlovarský kraj'), (4, 'Kraj Vysočina'), (5, 'Královéhradecký kraj'), (6, 'Liberecký kraj'), (7, 'Moravskoslezský kraj'), (8, 'Olomoucký kraj'), (9, 'Pardubický kraj'), (10, 'Plzeňský kraj'), (11, 'Středočeský kraj'), (12, 'Ústecký kraj'), (13, 'Zlínský kraj')], null=True, verbose_name='Kraj'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.0.2 on 2018-02-28 16:28
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('nalodeni', '0003_appuser_district'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='appuser',
|
||||||
|
name='postcode',
|
||||||
|
field=models.IntegerField(blank=True, null=True, verbose_name='PSČ'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,936 @@
|
||||||
|
# -*- 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 #
|
||||||
|
## .......+++++++......... ##
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,569 @@
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
from datetime import date, datetime, timedelta # timeSlices
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
import django
|
||||||
|
from django.http import HttpResponse, HttpResponseRedirect
|
||||||
|
from django.shortcuts import redirect, resolve_url
|
||||||
|
from django.template import Template, RequestContext, loader
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||||
|
from django.forms import ModelForm
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.utils.translation import pgettext, pgettext_lazy
|
||||||
|
|
||||||
|
from django.db import transaction
|
||||||
|
from django.db.models import F
|
||||||
|
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.core.validators import validate_email
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import bleach
|
||||||
|
import html2text
|
||||||
|
|
||||||
|
from django.conf import settings as appSettings
|
||||||
|
|
||||||
|
from . import models
|
||||||
|
from . import forms
|
||||||
|
from . import auth as nalodeni_auth
|
||||||
|
|
||||||
|
# Logger instance
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def role_required(roles=[]):
|
||||||
|
def decorate(func):
|
||||||
|
def call(request, *args, **kwargs):
|
||||||
|
for r in roles:
|
||||||
|
if not r in request.session['site_perms']:
|
||||||
|
messages.error(request, "Nedostatečné oprávnění pro přístup. Detaily byly zaznamenány.")
|
||||||
|
return HttpResponseRedirect('/')
|
||||||
|
result = func(request, *args, **kwargs)
|
||||||
|
return result
|
||||||
|
return call
|
||||||
|
return decorate
|
||||||
|
|
||||||
|
|
||||||
|
@ensure_csrf_cookie
|
||||||
|
#@login_required(login_url="/prihlaseni")
|
||||||
|
#@role_required(['sso_news_list'])
|
||||||
|
def news_all(request):
|
||||||
|
newsletters = models.Newsletter.objects.filter(is_del=False)
|
||||||
|
|
||||||
|
site_perms = request.session.get('site_perms',[])
|
||||||
|
actions = set()
|
||||||
|
|
||||||
|
if 'sso_news_manage' in site_perms:
|
||||||
|
actions.add('add_news_list')
|
||||||
|
actions.add('news_list_actions')
|
||||||
|
actions.add('news_list_details')
|
||||||
|
if 'sso_kodo' in site_perms:
|
||||||
|
actions.add('news_list_details')
|
||||||
|
|
||||||
|
if len(actions) == 0:
|
||||||
|
newsletters = newsletters.filter(enabled=True)
|
||||||
|
|
||||||
|
template = 'news/list.html'
|
||||||
|
context = {
|
||||||
|
'newsletters' : newsletters,
|
||||||
|
'actions' : actions,
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
@ensure_csrf_cookie
|
||||||
|
@login_required(login_url="/prihlaseni")
|
||||||
|
@role_required(['sso_news_list'])
|
||||||
|
def list_edit(request, lid=None):
|
||||||
|
if lid is not None:
|
||||||
|
obj = models.Newsletter.objects.filter(pk=lid, is_del=False)
|
||||||
|
if len(obj) == 1:
|
||||||
|
obj = obj[0]
|
||||||
|
elif len(obj) == 0:
|
||||||
|
obj = None
|
||||||
|
else:
|
||||||
|
1/0
|
||||||
|
else:
|
||||||
|
obj = None
|
||||||
|
|
||||||
|
site_perms = request.session.get('site_perms',[])
|
||||||
|
passed = False
|
||||||
|
if (obj is not None and request.user == obj.managed_by) or 'sso_news_manage' in site_perms:
|
||||||
|
passed = True
|
||||||
|
|
||||||
|
if not passed:
|
||||||
|
messages.error(request, _('Nedostatečné oprávnění.'))
|
||||||
|
return redirect('nalodeni:news_all')
|
||||||
|
|
||||||
|
if request.method == 'GET':
|
||||||
|
form = forms.NewsletterForm(instance=obj)
|
||||||
|
elif request.method == 'POST':
|
||||||
|
form = forms.NewsletterForm(request.POST, instance=obj)
|
||||||
|
|
||||||
|
if form.is_valid():
|
||||||
|
if request.POST.get('del_item', 'ne') == "ano":
|
||||||
|
form.instance.is_del = True
|
||||||
|
form.instance.save()
|
||||||
|
|
||||||
|
messages.info(request, "Záznam byl smazán.")
|
||||||
|
return redirect('nalodeni:news_all')
|
||||||
|
|
||||||
|
form.save()
|
||||||
|
|
||||||
|
messages.info(request, "Údaje byly uloženy.")
|
||||||
|
return redirect('nalodeni:news_all')
|
||||||
|
|
||||||
|
template = 'news/list_edit.html'
|
||||||
|
context = {
|
||||||
|
'form' : form,
|
||||||
|
'regions' : models.InterestRegion.objects.all(),
|
||||||
|
'topics' : models.UserTopic.objects.all(),
|
||||||
|
'skills' : models.UserSkill.objects.all(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
@ensure_csrf_cookie
|
||||||
|
def list_show(request, lid):
|
||||||
|
"""
|
||||||
|
Seznam zpráv pro daný newsletter.
|
||||||
|
"""
|
||||||
|
obj = models.Newsletter.objects.filter(pk=lid, is_del=False)
|
||||||
|
if len(obj) == 1:
|
||||||
|
obj=obj[0]
|
||||||
|
else:
|
||||||
|
return redirect('nalodeni:news_all')
|
||||||
|
|
||||||
|
site_perms = request.session.get('site_perms',[])
|
||||||
|
actions = set()
|
||||||
|
|
||||||
|
if (request.user == obj.managed_by or 'sso_news_manage' in site_perms):
|
||||||
|
actions.add('nwsl_edit')
|
||||||
|
if (request.user in obj.sent_by.all() or request.user == obj.managed_by
|
||||||
|
or 'sso_news_manage' in site_perms):
|
||||||
|
actions.add('nwsl_details')
|
||||||
|
actions.add('nwsl_actions')
|
||||||
|
if 'sso_news_list_recipients' in site_perms:
|
||||||
|
actions.add('show_newsletter_recipients')
|
||||||
|
|
||||||
|
msgs = obj.newsmsg_set.filter(is_del=False)
|
||||||
|
if 'nwsl_actions' not in actions:
|
||||||
|
msgs = msgs.exclude(sent_ts=None)
|
||||||
|
|
||||||
|
template = 'news/list_show.html'
|
||||||
|
context = {
|
||||||
|
'obj' : obj,
|
||||||
|
'msgs' : msgs,
|
||||||
|
'actions' : actions,
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
@ensure_csrf_cookie
|
||||||
|
@login_required(login_url="/prihlaseni")
|
||||||
|
@role_required(['sso_news_list_recipients'])
|
||||||
|
def list_show_recipients(request, lid):
|
||||||
|
"""
|
||||||
|
Seznam příjemců pro daný newsletter.
|
||||||
|
"""
|
||||||
|
obj = models.Newsletter.objects.filter(pk=lid, is_del=False)
|
||||||
|
if len(obj) == 1:
|
||||||
|
obj=obj[0]
|
||||||
|
else:
|
||||||
|
return redirect('nalodeni:news_all')
|
||||||
|
|
||||||
|
site_perms = request.session.get('site_perms',[])
|
||||||
|
actions = set()
|
||||||
|
|
||||||
|
if not (request.user in obj.sent_by.all() or request.user == obj.managed_by
|
||||||
|
or 'sso_news_manage' in site_perms):
|
||||||
|
messages.error(request, _('Nedostatečné oprávnění.'))
|
||||||
|
return redirect('nalodeni:news_all')
|
||||||
|
|
||||||
|
recipients = obj.get_recip_users()
|
||||||
|
recipients_cnt = len(recipients)
|
||||||
|
template = 'news/list_show_recipients.html'
|
||||||
|
context = {
|
||||||
|
'obj' : obj,
|
||||||
|
'msgs' : obj.newsmsg_set.filter(is_del=False),
|
||||||
|
'recipients' : recipients,
|
||||||
|
'recipients_cnt' : recipients_cnt,
|
||||||
|
'actions' : actions,
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ensure_csrf_cookie
|
||||||
|
def msg_show(request, mid):
|
||||||
|
"""
|
||||||
|
Detail zpravy newsletteru.
|
||||||
|
"""
|
||||||
|
obj = models.NewsMsg.objects.filter(pk=mid,is_del=False)
|
||||||
|
if len(obj) == 1:
|
||||||
|
obj = obj[0]
|
||||||
|
elif len(obj) == 0:
|
||||||
|
obj = None
|
||||||
|
messages.error("Objekt nenalezen.")
|
||||||
|
return redirect('nalodeni:news_all')
|
||||||
|
else:
|
||||||
|
1/0
|
||||||
|
|
||||||
|
site_perms = request.session.get('site_perms',[])
|
||||||
|
actions = set()
|
||||||
|
passed = False
|
||||||
|
|
||||||
|
if (request.user in obj.news.sent_by.all()
|
||||||
|
or request.user == obj.news.managed_by
|
||||||
|
or 'sso_news_manage' in site_perms):
|
||||||
|
actions.add('nwsl_actions')
|
||||||
|
passed = True
|
||||||
|
|
||||||
|
# zobrazit jiz odeslanou zpravu
|
||||||
|
if obj.sent_ts is not None:
|
||||||
|
passed = True
|
||||||
|
|
||||||
|
if not passed:
|
||||||
|
messages.error(request, _('Zpráva neexistuje, nebo nemáte dostatečné oprávnění k jejímu zobrazení.'))
|
||||||
|
return redirect('nalodeni:news_all')
|
||||||
|
|
||||||
|
template = 'news/msg_show.html'
|
||||||
|
context = {
|
||||||
|
'msg' : obj,
|
||||||
|
'blocks' : obj.newsmsgblock_set.all(),
|
||||||
|
'actions' : actions,
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
@ensure_csrf_cookie
|
||||||
|
@login_required(login_url="/prihlaseni")
|
||||||
|
def msg_edit(request, lid, mid=None):
|
||||||
|
"""
|
||||||
|
Seznam zpráv pro daný newsletter.
|
||||||
|
"""
|
||||||
|
if mid is not None:
|
||||||
|
obj = models.NewsMsg.objects.filter(pk=mid, is_del=False)
|
||||||
|
if len(obj) == 1:
|
||||||
|
obj = obj[0]
|
||||||
|
elif len(obj) == 0:
|
||||||
|
obj = None
|
||||||
|
return redirect('nalodeni:news_msg_create', lid=lid)
|
||||||
|
else:
|
||||||
|
1/0
|
||||||
|
else:
|
||||||
|
obj = None
|
||||||
|
|
||||||
|
if obj is None:
|
||||||
|
obj = models.NewsMsg(news_id=lid)
|
||||||
|
|
||||||
|
site_perms = request.session.get('site_perms',[])
|
||||||
|
actions = set()
|
||||||
|
|
||||||
|
if not (request.user in obj.news.sent_by.all()
|
||||||
|
or request.user == obj.news.managed_by
|
||||||
|
or 'sso_news_manage' in site_perms):
|
||||||
|
messages.error(request, _('Nedostatečné oprávnění pro vytvoření zprávy.'))
|
||||||
|
return redirect('nalodeni:news_all')
|
||||||
|
|
||||||
|
|
||||||
|
if request.method == 'GET':
|
||||||
|
form = forms.NewsMsgForm(instance=obj)
|
||||||
|
elif request.method == 'POST':
|
||||||
|
form = forms.NewsMsgForm(request.POST, instance=obj)
|
||||||
|
|
||||||
|
if form.is_valid():
|
||||||
|
form.instance.created_by = request.user
|
||||||
|
form.instance.created_ts = datetime.now()
|
||||||
|
|
||||||
|
if request.POST.get('del_item', 'ne') == "ano":
|
||||||
|
form.instance.is_del = True
|
||||||
|
form.instance.save()
|
||||||
|
|
||||||
|
messages.info(request, "Záznam byl smazán.")
|
||||||
|
return redirect('nalodeni:news_list_show', lid=lid)
|
||||||
|
|
||||||
|
form.instance.headerText = bleach.clean(form.instance.headerText,
|
||||||
|
tags=['b','ul','ol','li','a','i','hr','br'], strip=True)
|
||||||
|
form.instance.footerText = bleach.clean(form.instance.footerText,
|
||||||
|
tags=['b','ul','ol','li','a','i','hr','br'], strip=True)
|
||||||
|
|
||||||
|
form.instance.headerText = bleach.linkify(form.instance.headerText)
|
||||||
|
form.instance.footerText = bleach.linkify(form.instance.footerText)
|
||||||
|
form.save()
|
||||||
|
|
||||||
|
messages.info(request, "Údaje byly uloženy.")
|
||||||
|
return redirect('nalodeni:news_msg_show', mid=form.instance.id)
|
||||||
|
|
||||||
|
template = 'news/msg_edit.html'
|
||||||
|
context = {
|
||||||
|
'form' : form,
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
@ensure_csrf_cookie
|
||||||
|
@login_required(login_url="/prihlaseni")
|
||||||
|
def msg_send(request, id, realSend=False):
|
||||||
|
"""
|
||||||
|
Odeslani newsletteru.
|
||||||
|
"""
|
||||||
|
obj = models.NewsMsg.objects.filter(pk=id,is_del=False)
|
||||||
|
if len(obj) == 1:
|
||||||
|
obj = obj[0]
|
||||||
|
elif len(obj) == 0:
|
||||||
|
obj = None
|
||||||
|
messages.error("Objekt nenalezen.")
|
||||||
|
return redirect('nalodeni:news_all')
|
||||||
|
else:
|
||||||
|
1/0
|
||||||
|
|
||||||
|
site_perms = request.session.get('site_perms',[])
|
||||||
|
actions = set()
|
||||||
|
|
||||||
|
if not (request.user in obj.news.sent_by.all()
|
||||||
|
or request.user == obj.news.managed_by
|
||||||
|
or 'sso_news_manage' in site_perms):
|
||||||
|
messages.error(request, _('Nedostatečné oprávnění.'))
|
||||||
|
return redirect('nalodeni:news_all')
|
||||||
|
|
||||||
|
if 'sso_news_manage' in site_perms:
|
||||||
|
recipients_wanted = request.GET.get('recipients','test')
|
||||||
|
else:
|
||||||
|
recipients_wanted = "test"
|
||||||
|
|
||||||
|
update_sent_ts = False
|
||||||
|
use_public_to_email = False
|
||||||
|
recipients_email = []
|
||||||
|
recip_users = []
|
||||||
|
if obj.sent_ts is None: # jeste nerozeslano
|
||||||
|
if recipients_wanted == 'all':
|
||||||
|
update_sent_ts = True
|
||||||
|
use_public_to_email = True
|
||||||
|
recip_users = obj.news.get_recip_users()
|
||||||
|
else:
|
||||||
|
recip_users = [ request.user ]
|
||||||
|
else:
|
||||||
|
if recipients_wanted == "test":
|
||||||
|
recip_users = [ request.user ]
|
||||||
|
else:
|
||||||
|
messages.error(request, "Zpráva již byla rozeslána.")
|
||||||
|
return redirect('nalodeni:news_list_show', lid=obj.news_id)
|
||||||
|
|
||||||
|
if obj.testMailRecipients is not None and recipients_wanted == "test":
|
||||||
|
for eml in obj.testMailRecipients.split(","):
|
||||||
|
try:
|
||||||
|
eml = eml.strip()
|
||||||
|
validate_email(eml)
|
||||||
|
recipients_email.append(eml)
|
||||||
|
except ValidationError:
|
||||||
|
messages.error(request, "Testovací email %s není ve tvaru emailu." % (eml,) )
|
||||||
|
|
||||||
|
for rcp in recip_users:
|
||||||
|
# pouzivat KONTAKTNI email misto REGISTRACNIHO
|
||||||
|
eml = rcp.email_contact_active
|
||||||
|
if eml is None or len(eml.strip()) == 0:
|
||||||
|
eml = rcp.email
|
||||||
|
try:
|
||||||
|
validate_email(eml)
|
||||||
|
recipients_email.append(eml)
|
||||||
|
except ValidationError:
|
||||||
|
messages.error(request, "Email '%s' uživatele %s není ve správném tvaru." % (eml, rcp.username))
|
||||||
|
|
||||||
|
template = 'news/msg_to_email.html'
|
||||||
|
context = {
|
||||||
|
'msg' : obj,
|
||||||
|
'abs_host_url' : '%s://%s' % ( request.scheme, request.get_host()),
|
||||||
|
'blocks' : obj.newsmsgblock_set.all(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if realSend:
|
||||||
|
from django.core.mail import get_connection, EmailMultiAlternatives
|
||||||
|
import quopri
|
||||||
|
|
||||||
|
mailgun_backend = get_connection('anymail.backends.mailgun.EmailBackend')
|
||||||
|
|
||||||
|
header_from = '%s <%s>' % (
|
||||||
|
appSettings.ANYMAIL['FROM_NAME'],
|
||||||
|
appSettings.ANYMAIL['FROM_EMAIL'],
|
||||||
|
)
|
||||||
|
html_message = render_to_string(template, context)
|
||||||
|
plain_message = html2text.html2text(html_message)
|
||||||
|
|
||||||
|
ema = EmailMultiAlternatives(
|
||||||
|
"%s - %s" % (obj.news.name, obj.title),
|
||||||
|
plain_message, header_from )
|
||||||
|
ema.connection = mailgun_backend
|
||||||
|
|
||||||
|
if use_public_to_email:
|
||||||
|
ema.to = [ appSettings.ANYMAIL['PUBLIC_TO_EMAIL'] ]
|
||||||
|
else:
|
||||||
|
# pouzit kontaktni nebo registracni email uzivatele
|
||||||
|
eml = rcp.email_contact_active
|
||||||
|
if eml is None or len(eml.strip()) == 0:
|
||||||
|
eml = rcp.email
|
||||||
|
ema.to = [ eml ]
|
||||||
|
|
||||||
|
ema.bcc = recipients_email
|
||||||
|
if obj.news.replyToEmail is not None:
|
||||||
|
try:
|
||||||
|
eml = obj.news.replyToEmail.strip()
|
||||||
|
validate_email(eml)
|
||||||
|
ema.extra_headers = {
|
||||||
|
'Reply-To' : eml,
|
||||||
|
}
|
||||||
|
except ValidationError:
|
||||||
|
messages.error(request, "Odpovědní email '%s' není ve tvaru emailu. Odesílání zrušeno." % (eml,) )
|
||||||
|
messages.info(request, "Chybu může opravit správce tohoto newsletteru." )
|
||||||
|
return redirect('nalodeni:news_list_show', lid=obj.news_id)
|
||||||
|
|
||||||
|
ema.attach_alternative(html_message, "text/html")
|
||||||
|
ema.send()
|
||||||
|
|
||||||
|
if update_sent_ts:
|
||||||
|
obj.sent_ts = datetime.now()
|
||||||
|
obj.save()
|
||||||
|
|
||||||
|
messages.info(request, "Zpráva odeslána pomocí MailGun celkem %s uživatelům." % len(recipients_email) )
|
||||||
|
else:
|
||||||
|
messages.error(request, "Message delivery disabled.")
|
||||||
|
|
||||||
|
return redirect('nalodeni:news_list_show', lid=obj.news_id)
|
||||||
|
|
||||||
|
|
||||||
|
def msg_preview(request, id):
|
||||||
|
"""
|
||||||
|
Verejny nahled newsletteru.
|
||||||
|
"""
|
||||||
|
obj = models.NewsMsg.objects.filter(pk=id,is_del=False)
|
||||||
|
if len(obj) == 1:
|
||||||
|
obj = obj[0]
|
||||||
|
elif len(obj) == 0:
|
||||||
|
obj = None
|
||||||
|
messages.error("Objekt nenalezen.")
|
||||||
|
return redirect('nalodeni:news_all')
|
||||||
|
else:
|
||||||
|
1/0
|
||||||
|
|
||||||
|
site_perms = request.session.get('site_perms',[])
|
||||||
|
actions = set()
|
||||||
|
|
||||||
|
passed = False
|
||||||
|
if (request.user in obj.news.sent_by.all()
|
||||||
|
or request.user == obj.news.managed_by
|
||||||
|
or 'sso_news_manage' in site_perms):
|
||||||
|
passed = True
|
||||||
|
if obj.sent_ts is not None:
|
||||||
|
passed = True
|
||||||
|
|
||||||
|
if not passed:
|
||||||
|
return redirect('nalodeni:news_all')
|
||||||
|
|
||||||
|
|
||||||
|
template = 'news/msg_to_email.html'
|
||||||
|
context = {
|
||||||
|
'msg' : obj,
|
||||||
|
'abs_host_url' : '%s://%s' % ( request.scheme, request.get_host()),
|
||||||
|
'blocks' : obj.newsmsgblock_set.all(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ensure_csrf_cookie
|
||||||
|
@login_required(login_url="/prihlaseni")
|
||||||
|
def block_edit(request, mid, bid=None):
|
||||||
|
"""
|
||||||
|
Seznam zpráv pro daný newsletter.
|
||||||
|
"""
|
||||||
|
if bid is not None:
|
||||||
|
obj = models.NewsMsgBlock.objects.filter(pk=bid, newsmsg_id=mid)
|
||||||
|
if len(obj) == 1:
|
||||||
|
obj = obj[0]
|
||||||
|
elif len(obj) == 0:
|
||||||
|
obj = None
|
||||||
|
return redirect('nalodeni:news_block_create', mid=mid)
|
||||||
|
else:
|
||||||
|
1/0
|
||||||
|
else:
|
||||||
|
obj = None
|
||||||
|
|
||||||
|
if obj is None:
|
||||||
|
obj = models.NewsMsgBlock(newsmsg_id=mid)
|
||||||
|
|
||||||
|
site_perms = request.session.get('site_perms',[])
|
||||||
|
actions = set()
|
||||||
|
|
||||||
|
if not (request.user in obj.newsmsg.news.sent_by.all()
|
||||||
|
or request.user == obj.newsmsg.news.managed_by
|
||||||
|
or 'sso_news_manage' in site_perms):
|
||||||
|
messages.error(request, _('Nedostatečné oprávnění pro editaci bloku zprávy.'))
|
||||||
|
return redirect('nalodeni:news_all')
|
||||||
|
|
||||||
|
|
||||||
|
if request.method == 'GET':
|
||||||
|
form = forms.NewsMsgBlockForm(instance=obj)
|
||||||
|
elif request.method == 'POST':
|
||||||
|
form = forms.NewsMsgBlockForm(request.POST, instance=obj)
|
||||||
|
|
||||||
|
if form.is_valid():
|
||||||
|
form.instance.content = bleach.clean(form.instance.content,
|
||||||
|
tags=['b','ul','ol','li','a','i','hr'], strip=True)
|
||||||
|
form.instance.content = bleach.linkify(form.instance.content)
|
||||||
|
form.save()
|
||||||
|
|
||||||
|
messages.info(request, "Údaje byly uloženy.")
|
||||||
|
return redirect('nalodeni:news_msg_show', mid=mid)
|
||||||
|
|
||||||
|
template = 'news/block_edit.html'
|
||||||
|
context = {
|
||||||
|
'form' : form,
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
@ensure_csrf_cookie
|
||||||
|
@login_required(login_url="/prihlaseni")
|
||||||
|
def block_delete(request, bid):
|
||||||
|
"""
|
||||||
|
Seznam zpráv pro daný newsletter.
|
||||||
|
"""
|
||||||
|
obj = models.NewsMsgBlock.objects.filter(pk=bid)
|
||||||
|
if len(obj) == 1:
|
||||||
|
obj = obj[0]
|
||||||
|
elif len(obj) == 0:
|
||||||
|
obj = None
|
||||||
|
return redirect('nalodeni:news_all')
|
||||||
|
else:
|
||||||
|
1/0
|
||||||
|
|
||||||
|
site_perms = request.session.get('site_perms',[])
|
||||||
|
actions = set()
|
||||||
|
|
||||||
|
if not (request.user in obj.newsmsg.news.sent_by.all()
|
||||||
|
or request.user == obj.newsmsg.news.managed_by
|
||||||
|
or 'sso_news_manage' in site_perms):
|
||||||
|
messages.error(request, _('Nedostatečné oprávnění pro smazání bloku zprávy.'))
|
||||||
|
return redirect('nalodeni:news_all')
|
||||||
|
|
||||||
|
mid = obj.newsmsg_id
|
||||||
|
obj.delete()
|
||||||
|
|
||||||
|
messages.info(request, "Záznam byl smazán.")
|
||||||
|
return redirect('nalodeni:news_msg_show', mid=mid)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,197 @@
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
from datetime import date, datetime, timedelta # timeSlices
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
import django
|
||||||
|
from django.http import HttpResponse, HttpResponseRedirect
|
||||||
|
from django.template import Template, RequestContext, loader
|
||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||||
|
from django import forms
|
||||||
|
from django.forms import ModelForm
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.utils.translation import pgettext, pgettext_lazy
|
||||||
|
|
||||||
|
from django.db import transaction
|
||||||
|
from django.db.models import F
|
||||||
|
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from django.conf import settings as appSettings
|
||||||
|
|
||||||
|
from . import models
|
||||||
|
from . import forms
|
||||||
|
from . import auth as nalodeni_auth
|
||||||
|
|
||||||
|
# Logger instance
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def role_required(roles=[]):
|
||||||
|
def decorate(func):
|
||||||
|
def call(request, *args, **kwargs):
|
||||||
|
for r in roles:
|
||||||
|
if not r in request.session['site_perms']:
|
||||||
|
messages.error(request, "Nedostatečné oprávnění pro přístup. Detaily byly zaznamenány.")
|
||||||
|
return HttpResponseRedirect('/')
|
||||||
|
result = func(request, *args, **kwargs)
|
||||||
|
return result
|
||||||
|
return call
|
||||||
|
return decorate
|
||||||
|
|
||||||
|
def get_AppUser_objects(request):
|
||||||
|
''' Zkontroluje, že všechny objekty v 'objs' vyhovují přiděleným rolím.
|
||||||
|
Nevyhovující vyřadí.
|
||||||
|
'''
|
||||||
|
sp = request.session['site_perms']
|
||||||
|
if 'sso_kodo' not in sp:
|
||||||
|
return models.AppUser.objects.none()
|
||||||
|
|
||||||
|
if 'sso_admin' in sp:
|
||||||
|
return models.AppUser.objects.all()
|
||||||
|
|
||||||
|
objs = models.AppUser.objects.filter(district__in=request.session['spc']['dist'])
|
||||||
|
|
||||||
|
return objs
|
||||||
|
|
||||||
|
def get_AppUser_districts(request):
|
||||||
|
'''Vrátí dostupné kraje podle rolí.'''
|
||||||
|
|
||||||
|
|
||||||
|
if 'sso_admin' in request.session['site_perms']:
|
||||||
|
return list(models.AppUser.DISTRICT_CHOICES)
|
||||||
|
|
||||||
|
rslt = []
|
||||||
|
for d in models.AppUser.DISTRICT_CHOICES:
|
||||||
|
if d[0] in request.session['spc']['dist']:
|
||||||
|
rslt.append(d)
|
||||||
|
|
||||||
|
return rslt
|
||||||
|
|
||||||
|
|
||||||
|
@ensure_csrf_cookie
|
||||||
|
@login_required(login_url="/prihlaseni")
|
||||||
|
@role_required(['sso_kodo'])
|
||||||
|
def confirmed(request, newOnly=False, dist=None):
|
||||||
|
if dist is None:
|
||||||
|
dist = int(request.POST.get('dist', -1))
|
||||||
|
|
||||||
|
objs = get_AppUser_objects(request).order_by('last_name', 'first_name' ,'email')
|
||||||
|
|
||||||
|
if newOnly:
|
||||||
|
objs = objs.filter(status=models.AppUser.STATUS_NEW)
|
||||||
|
else:
|
||||||
|
objs = objs.filter(status=models.AppUser.STATUS_REG)
|
||||||
|
|
||||||
|
districts = [(-1, ' -- vše dostupné -- ')] + get_AppUser_districts(request)
|
||||||
|
# filtrujeme jen povolene kraje, nebo vse pro adminy
|
||||||
|
if dist != -1 and (dist in request.session['spc']['dist'] or 'sso_admin' in request.session['site_perms']):
|
||||||
|
selDist = dist
|
||||||
|
objs = objs.filter(district=selDist)
|
||||||
|
else:
|
||||||
|
selDist = -1
|
||||||
|
|
||||||
|
|
||||||
|
template = 'people/list.html'
|
||||||
|
context = {
|
||||||
|
'people' : objs,
|
||||||
|
'newOnly' : newOnly,
|
||||||
|
'distAvail' : districts,
|
||||||
|
'selDist' : selDist,
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
@ensure_csrf_cookie
|
||||||
|
@login_required(login_url="/prihlaseni")
|
||||||
|
@role_required(['sso_kodo'])
|
||||||
|
def pending(request):
|
||||||
|
''' List pending registrations from AppRegEmail. '''
|
||||||
|
|
||||||
|
show_all = request.GET.get("show_all","no") == "yes"
|
||||||
|
|
||||||
|
if request.method == "POST":
|
||||||
|
act = request.POST.get("action", None)
|
||||||
|
ids = request.POST.getlist('r[]')
|
||||||
|
|
||||||
|
objs = models.AppRegEmail.objects.filter(id__in = ids)
|
||||||
|
|
||||||
|
if act == "token":
|
||||||
|
email_counter = 0
|
||||||
|
skipped_counter = 0
|
||||||
|
for o in objs:
|
||||||
|
# kontrola, jestli uživatel již není registrován
|
||||||
|
rslt = models.AppUser.objects.filter(email__iexact=o.email.strip())
|
||||||
|
if len(rslt) != 0:
|
||||||
|
messages.info(request,'Uživatel s emailem %s již je registrován, požadavek na registaci odstraněn.' % o.email)
|
||||||
|
o.delete()
|
||||||
|
else:
|
||||||
|
if not ( o.etStamp and (datetime.now() - o.etStamp).total_seconds() < int(appSettings.TOKEN_VALID_SEC)):
|
||||||
|
# token not valid or not sent
|
||||||
|
nalodeni_auth.sendRegisterToken(o.email)
|
||||||
|
email_counter += 1
|
||||||
|
else:
|
||||||
|
skipped_counter += 1
|
||||||
|
if email_counter > 0:
|
||||||
|
messages.info(request,'Registrační emaily odeslány, celkem odesláno %s zpráv.' % email_counter)
|
||||||
|
if skipped_counter > 0:
|
||||||
|
messages.info(request,'Celkem %s registrací přeskočeno, ještě jsou platné.' % skipped_counter)
|
||||||
|
|
||||||
|
elif act == "delete":
|
||||||
|
objs.delete()
|
||||||
|
messages.info(request,'Registrace odstraněny.')
|
||||||
|
|
||||||
|
tokenValidAfter = datetime.now() - timedelta(seconds=int(appSettings.TOKEN_VALID_SEC))
|
||||||
|
|
||||||
|
objs = models.AppRegEmail.objects.all()
|
||||||
|
if not show_all:
|
||||||
|
# zobrazovat pouze nové registrace
|
||||||
|
objs = objs.filter(emailToken = None)
|
||||||
|
objs = objs.order_by('etStamp')
|
||||||
|
|
||||||
|
template = 'people/pending.html'
|
||||||
|
context = {
|
||||||
|
'people' : objs,
|
||||||
|
'tokenValidAfter' : tokenValidAfter,
|
||||||
|
'show_all' : show_all,
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ensure_csrf_cookie
|
||||||
|
@login_required(login_url="/prihlaseni")
|
||||||
|
@role_required(['sso_kodo'])
|
||||||
|
def update(request):
|
||||||
|
ids = request.POST.getlist('r[]')
|
||||||
|
val = request.POST.get('setStatus', None)
|
||||||
|
|
||||||
|
if val and val in ['reg','new']:
|
||||||
|
objs = get_AppUser_objects(request).filter(id__in=ids)
|
||||||
|
|
||||||
|
rec = 0
|
||||||
|
for o in objs:
|
||||||
|
rec += 1
|
||||||
|
o.status = o.STATUS_REG if val == 'reg' else o.STATUS_NEW
|
||||||
|
o.save()
|
||||||
|
|
||||||
|
messages.info(request, 'Upraveno celkem %s záznamů.' % rec)
|
||||||
|
|
||||||
|
if val == 'new':
|
||||||
|
return HttpResponseRedirect('/people/list/')
|
||||||
|
elif val == "reg":
|
||||||
|
return HttpResponseRedirect('/people/list-new/')
|
||||||
|
else:
|
||||||
|
messages.error(request, 'Špatný požadavek.')
|
||||||
|
|
||||||
|
|
||||||
|
return HttpResponseRedirect('/people/list/')
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,13 @@
|
||||||
|
.table-list th { text-align: left }
|
||||||
|
.table-list input[type="checkbox"] {margin:0; vertical-align: bottom;}
|
||||||
|
|
||||||
|
.messages .msg {
|
||||||
|
padding: 0.5em;
|
||||||
|
border: 1px solid #bababa;
|
||||||
|
}
|
||||||
|
.messages .error { background: #ffc9c9;}
|
||||||
|
.messages .info { background: #ccffc9;}
|
||||||
|
.messages .critical { background: #ff8a5c;}
|
||||||
|
|
||||||
|
ul.errorlist { margin:0.1em 0; }
|
||||||
|
ul.errorlist li { background: #ffc5b3; list-style: none; margin: 0; padding: 0.2em 0.5em; }
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue