diff --git a/README.md b/README.md
index 4230f3c..db34259 100644
--- a/README.md
+++ b/README.md
@@ -47,6 +47,9 @@ pip install -r requirements.txt
./manage.py shell -c "from django.contrib.auth import get_user_model; get_user_model().objects.create_superuser('vasya', '1@abc.net', 'promprog')"
./manage.py runserver
```
+Создать токен
+
+Указать почту и токен в окружении
## Read more
- Zenpy: [http://docs.facetoe.com.au](http://docs.facetoe.com.au)
diff --git a/access_controller/settings.py b/access_controller/settings.py
index caf4521..a1f54ab 100644
--- a/access_controller/settings.py
+++ b/access_controller/settings.py
@@ -15,7 +15,6 @@ from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
-
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
@@ -27,7 +26,6 @@ DEBUG = True
ALLOWED_HOSTS = []
-
# Application definition
INSTALLED_APPS = [
@@ -37,6 +35,7 @@ INSTALLED_APPS = [
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
+ 'django_registration',
'main',
]
@@ -72,7 +71,6 @@ TEMPLATES = [
WSGI_APPLICATION = 'access_controller.wsgi.application'
-
# Database
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
@@ -83,7 +81,6 @@ DATABASES = {
}
}
-
# Password validation
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
@@ -102,7 +99,6 @@ AUTH_PASSWORD_VALIDATORS = [
},
]
-
# Internationalization
# https://docs.djangoproject.com/en/3.1/topics/i18n/
@@ -116,7 +112,6 @@ USE_L10N = True
USE_TZ = True
-
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.1/howto/static-files/
@@ -127,3 +122,7 @@ STATICFILES_DIRS = [
]
MEDIA_ROOT = BASE_DIR / 'media'
+MEDIA_URL = '/media/'
+LOGIN_REDIRECT_URL = '/'
+LOGOUT_REDIRECT_URL = '/'
+
diff --git a/access_controller/urls.py b/access_controller/urls.py
index bdd60eb..350e189 100644
--- a/access_controller/urls.py
+++ b/access_controller/urls.py
@@ -13,12 +13,23 @@ 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.conf.urls.static import static
from django.contrib import admin
-from django.urls import path
-from main.views import main_page
+from django.urls import path, include
+from django.contrib.auth.views import LoginView
+from django.urls import path, include
+from access_controller import settings
+from main.views import *
urlpatterns = [
- path('admin/', admin.site.urls),
+ path('admin/', admin.site.urls, name='admin'),
path('', main_page),
+ path('register/', CustomRegistrationView.as_view(), name='registration'),
+ # path('', include('django_registration.backends.one_step.urls')),
+ path('profile/', profile_page, name='profile'),
+ path('accounts/login/', LoginView.as_view(extra_context={})), # TODO add extra context
+ path('accounts/', include('django.contrib.auth.urls'))
]
+
+urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
diff --git a/docs/source/code.rst b/docs/source/code.rst
new file mode 100644
index 0000000..27ac9c9
--- /dev/null
+++ b/docs/source/code.rst
@@ -0,0 +1,9 @@
+*****
+TODOs
+*****
+
+Extra Functions
+---------------
+
+.. automodule:: main.extra_func
+ :members:
diff --git a/docs/source/index.rst b/docs/source/index.rst
index a95aa50..778a091 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -6,10 +6,13 @@
Welcome to ZenDesk Access Controller's documentation!
=====================================================
+
.. toctree::
:maxdepth: 2
:caption: Contents:
+ code.rst
+ todo.rst
Indices and tables
diff --git a/docs/source/todo.rst b/docs/source/todo.rst
new file mode 100644
index 0000000..d6ad446
--- /dev/null
+++ b/docs/source/todo.rst
@@ -0,0 +1,5 @@
+*****
+TODOs
+*****
+
+.. todolist::
diff --git a/layouts/authorization/authorization.jpg b/layouts/authorization/authorization.jpg
new file mode 100644
index 0000000..8860ad9
Binary files /dev/null and b/layouts/authorization/authorization.jpg differ
diff --git a/main/extra_func.py b/main/extra_func.py
new file mode 100644
index 0000000..305e897
--- /dev/null
+++ b/main/extra_func.py
@@ -0,0 +1,104 @@
+import os
+
+from zenpy import Zenpy
+from zenpy.lib.exception import APIException
+
+from main.models import UserProfile
+
+
+# Дополнительные функции
+def set_and_get_name(user_profile: UserProfile):
+ """
+ Функция устанавливает поле :class:`username` текущим именем в Zendesk
+
+ .. TODO::
+ Переделать с получением данных через API
+
+ :param UP: Объект профиля пользователя
+ :type UP: :class:`main.models.UserProfile`
+ :return: Имя пользователя
+ :rtype: :class:`str`
+ """
+ return user_profile.user.username
+
+
+def set_and_get_email(user_profile: UserProfile): # TODO: Переделать с получением данных через API
+ """
+ Функция устанавливает поле :class:`user.email` текущей почтой в Zendesk
+
+ :param UP: Объект профиля пользователя
+ :type UP: :class:`main.models.UserProfile`
+ :return: Почта пользователя
+ :rtype: :class:`str`
+ """
+ return user_profile.user.email
+
+
+def set_and_get_role(user_profile: UserProfile): # TODO: Переделать с получением данных через API
+ """
+ Функция устанавливает поле :class:`role` текущей ролью в Zendesk
+
+ :param UP: Объект профиля пользователя
+ :type UP: :class:`main.models.UserProfile`
+ :return: Роль пользователя
+ :rtype: :class:`str`
+ """
+ return user_profile.role
+
+
+def load_and_get_image(user_profile: UserProfile): # TODO: Переделать с получением изображения через API
+ """
+ Функция загружает и устанавливает изображение в поле :class:`image`
+
+ :param UP: Объект профиля пользователя
+ :type UP: :class:`main.models.UserProfile`
+ :return: Название изображения
+ :rtype: :class:`str`
+ """
+ return user_profile.image.name
+
+
+def check_user_exist(email: str) -> bool:
+ """
+ Функция проверяет, существует ли пользователь
+
+ :param email: Электронная почта пользователя
+ :type email: :class:`str`
+ :return: True, если существует, иначе False
+ :rtype: :class:`bool`
+ """
+ admin_creds = {
+ 'email': os.environ.get('Admin_email'),
+ 'subdomain': 'ngenix1612197338',
+ 'token': os.environ.get('Oauth_token'),
+ }
+ admin = Zenpy(**admin_creds)
+ zenpy_user = admin.search(email, type='user')
+ if zenpy_user:
+ return True
+ return False
+
+
+def check_user_auth(email: str, password: str) -> bool:
+ """
+ Функция проверяет, верны ли входные данные
+
+ :param email: Электроная почта пользователя
+ :type email: :class:`str`
+ :param password: Пароль пользователя
+ :type password: :class:`str`
+ :return: True, если входные данные верны, иначе False
+ :raise :class:`APIException`: исключение, вызываемое если пользователь не аутентифицирован
+ :rtype: :class:`bool`
+ """
+ try:
+ creds = {
+ 'email': email,
+ 'subdomain': 'ngenix1612197338',
+ 'password': password,
+ }
+ user = Zenpy(**creds)
+ user.search(email, type='user')
+ except APIException:
+ return False
+ return True
diff --git a/main/forms.py b/main/forms.py
new file mode 100644
index 0000000..fcd8a7f
--- /dev/null
+++ b/main/forms.py
@@ -0,0 +1,34 @@
+from django import forms
+from django_registration.forms import RegistrationFormUniqueEmail
+
+
+class CustomRegistrationForm(RegistrationFormUniqueEmail):
+ """
+ Форма для регистрации :class:`django_registration.forms.RegistrationFormUniqueEmail`
+ с полем для ввода пароля от Zendesk аккаунта и с добавлением bootstrap-класса 'form-control' для всех полей
+
+ :param password_zen: Поле для ввода пароля от Zendesk
+ :type password_zen: :class:`django.forms.CharField`
+ """
+ password_zen = forms.CharField(
+ required=True,
+ label="Пароль от Zendesk аккаунта",
+ strip=False,
+ widget=forms.PasswordInput(attrs={
+ 'class': 'form-control'
+ })
+ )
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ for visible in self.visible_fields():
+ if visible.field.widget.attrs.get('class', False):
+ print(visible.field.widget.attrs['class'].find('form-control'))
+ if visible.field.widget.attrs['class'].find('form-control') < 0:
+ visible.field.widget.attrs['class'] += 'form-control'
+ else:
+ visible.field.widget.attrs['class'] = 'form-control'
+
+ class Meta(RegistrationFormUniqueEmail.Meta):
+ fields = RegistrationFormUniqueEmail.Meta.fields
+ fields.insert(2, 'password_zen')
diff --git a/main/layout/base/base.png b/main/layout/base/base.png
new file mode 100644
index 0000000..0841add
Binary files /dev/null and b/main/layout/base/base.png differ
diff --git a/main/migrations/0002_userprofile_name.py b/main/migrations/0002_userprofile_name.py
new file mode 100644
index 0000000..1737da2
--- /dev/null
+++ b/main/migrations/0002_userprofile_name.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.1.6 on 2021-02-08 16:15
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('main', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='userprofile',
+ name='name',
+ field=models.CharField(default='None', max_length=100),
+ ),
+ ]
diff --git a/main/models.py b/main/models.py
index 73fb9ce..96d04db 100644
--- a/main/models.py
+++ b/main/models.py
@@ -8,3 +8,4 @@ class UserProfile(models.Model):
user = models.OneToOneField(to=User, on_delete=models.CASCADE)
role = models.IntegerField()
image = models.ImageField(upload_to='user_avatars')
+ name = models.CharField(default='None', max_length=100)
diff --git a/main/templates/base/base.html b/main/templates/base/base.html
index 071c914..0d18dcd 100644
--- a/main/templates/base/base.html
+++ b/main/templates/base/base.html
@@ -1,4 +1,5 @@
+
{% load static %}
@@ -32,7 +33,7 @@
-
+
{% block heading %}
{% endblock %}
@@ -52,5 +53,6 @@
integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW"
crossorigin="anonymous"
>
+
diff --git a/main/templates/base/menu.html b/main/templates/base/menu.html
index 4aabc46..22c2ef8 100644
--- a/main/templates/base/menu.html
+++ b/main/templates/base/menu.html
@@ -11,15 +11,15 @@
{% if request.user.is_authenticated %}
{% else %}
{% endif %}
diff --git a/main/templates/django_registration/registration_closed.html b/main/templates/django_registration/registration_closed.html
new file mode 100644
index 0000000..4fffce6
--- /dev/null
+++ b/main/templates/django_registration/registration_closed.html
@@ -0,0 +1,15 @@
+{% extends 'base/base.html' %}
+{% load static %}
+
+{% block title %}
+ Регистрация завершена
+{% endblock %}
+
+{% block heading %}
+ Регистрация
+{% endblock %}
+
+{% block content %}
+
+ Нет пользователя с указаным адресом электронной почты, либо был введён неверный пароль
+{% endblock %}
diff --git a/main/templates/django_registration/registration_complete.html b/main/templates/django_registration/registration_complete.html
new file mode 100644
index 0000000..bb064bb
--- /dev/null
+++ b/main/templates/django_registration/registration_complete.html
@@ -0,0 +1,14 @@
+{% extends 'base/base.html' %}
+
+{% block title %}
+ Регистрация завершена
+{% endblock %}
+
+{% block heading %}
+ Регистрация
+{% endblock %}
+
+{% block content %}
+
+ Регистрация прошла успешно. Войти сейчас
+{% endblock %}
diff --git a/main/templates/django_registration/registration_form.html b/main/templates/django_registration/registration_form.html
new file mode 100644
index 0000000..c4bb388
--- /dev/null
+++ b/main/templates/django_registration/registration_form.html
@@ -0,0 +1,24 @@
+{% extends 'base/base.html' %}
+{% load static %}
+
+{% block title %}
+ Регистрация
+{% endblock %}
+
+{% block heading %}
+ Регистрация
+{% endblock %}
+{% block content %}
+
+{% endblock %}
diff --git a/main/templates/pages/profile.html b/main/templates/pages/profile.html
new file mode 100644
index 0000000..0b855b4
--- /dev/null
+++ b/main/templates/pages/profile.html
@@ -0,0 +1,46 @@
+{% extends 'base/base.html' %}
+
+{% load static %}
+
+
+{% block title %}{{ pagename }}{% endblock %}
+
+
+{% block heading %}Профиль{% endblock %}
+
+
+{% block extra_css %}
+
+{% endblock %}
+
+{% block content %}
+
+
+
+
+ {% if image_name %}
+

+ {% else %}
+

+ {% endif %}
+
+
+
+
+
Имя пользователя {{name}}
+ Электронная почта {{email}}
+ Текущая роль {{role}}
+
+
+
+
+{% endblock %}
diff --git a/main/templates/registration/login.html b/main/templates/registration/login.html
new file mode 100644
index 0000000..0917b0c
--- /dev/null
+++ b/main/templates/registration/login.html
@@ -0,0 +1,40 @@
+{% extends 'base/base.html' %}
+{% block title %}
+ Авторизация
+{% endblock %}
+{% block heading %}
+ Авторизация
+{% endblock %}
+{% block content %}
+
+{% endblock %}
diff --git a/main/views.py b/main/views.py
index a1eb178..8dbfe9d 100644
--- a/main/views.py
+++ b/main/views.py
@@ -1,4 +1,82 @@
from django.shortcuts import render
+from django.urls import reverse_lazy
+
+from main.extra_func import set_and_get_name, set_and_get_email, load_and_get_image, set_and_get_role, check_user_exist, \
+ check_user_auth
+from main.models import UserProfile
+
+from django.contrib.auth.models import User
+from main.forms import CustomRegistrationForm
+from django_registration.views import RegistrationView
+
+from django.contrib.auth.decorators import login_required
+
+from zenpy import Zenpy
+
+
+class CustomRegistrationView(RegistrationView):
+ """
+ Отображение и логика работы страницы регистрации пользователя
+ """
+ form_class = CustomRegistrationForm
+ template_name = 'django_registration/registration_form.html'
+ success_url = reverse_lazy('django_registration_complete')
+ is_allowed = True
+
+ def register(self, form):
+ if check_user_exist(form.data['email']) and check_user_auth(form.data['email'], form.data['password_zen']):
+ user = User.objects.create_user(
+ username=form.data['username'],
+ email=form.data['email'],
+ password=form.data['password1']
+ )
+ profile = UserProfile(
+ image='None.png',
+ user=user,
+ role=0,
+ )
+ set_and_get_name(profile)
+ set_and_get_email(profile)
+ set_and_get_role(profile)
+ load_and_get_image(profile)
+ profile.save()
+ else:
+ self.is_allowed = False
+
+ def get_success_url(self, user=None):
+ """
+ Возвращает url-адрес страницы, куда нужно перейти после успешной/неуспешной регистрации
+ Используется самой django-registration
+ """
+ if self.is_allowed:
+ return reverse_lazy('django_registration_complete')
+ else:
+ return reverse_lazy('django_registration_disallowed')
+
+
+@login_required()
+def profile_page(request):
+ """
+ Отображение страницы профиля
+
+
+ :param request: объект с деталями запроса
+ :type request: :class:`django.http.HttpResponse`
+ :return: объект ответа сервера с HTML-кодом внутри
+ """
+ if request.user.is_authenticated:
+ # UP = UserProfile.objects.get(user=request.user)
+ UP = UserProfile.objects.get(user=request.user)
+ # else: # TODO: Убрать после появления регистрации и авторизации, добавить login_required()
+ # UP = UserProfile.objects.get(user=1)
+ context = {
+ 'name': set_and_get_name(UP),
+ 'email': set_and_get_email(UP),
+ 'role': set_and_get_role(UP),
+ 'image_name': load_and_get_image(UP),
+ 'pagename': 'Страница профиля'
+ }
+ return render(request, 'pages/profile.html', context)
def main_page(request):
diff --git a/requirements.txt b/requirements.txt
index 2f0966d..e40b267 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,7 +1,11 @@
# Engine
Django==3.1.6
Pillow==8.1.0
+zenpy~=2.0.24
+django-registration==3.1.1
+
# Documentation
Sphinx==3.4.3
sphinx-rtd-theme==0.5.1
+
diff --git a/static/no_avatar.png b/static/no_avatar.png
new file mode 100644
index 0000000..2593874
Binary files /dev/null and b/static/no_avatar.png differ