parent
9800358b25
commit
a181c54093
12 changed files with 324 additions and 0 deletions
@ -0,0 +1 @@ |
|||||||
|
default_app_config = 'contact_us.apps.ContactUsConfig' |
||||||
@ -0,0 +1,7 @@ |
|||||||
|
from django.apps import AppConfig |
||||||
|
from django.utils.translation import ugettext_lazy as _ |
||||||
|
|
||||||
|
|
||||||
|
class ContactUsConfig(AppConfig): |
||||||
|
name = 'contact_us' |
||||||
|
verbose_name = _("Contact requests") |
||||||
@ -0,0 +1,5 @@ |
|||||||
|
from contact_us.forms import ContactUsForm |
||||||
|
|
||||||
|
|
||||||
|
def contact_us_form(request): |
||||||
|
return {'contact_us_form': ContactUsForm()} |
||||||
@ -0,0 +1,85 @@ |
|||||||
|
from crispy_forms.helper import FormHelper |
||||||
|
from crispy_forms.layout import Layout, Div, HTML, Field, Submit |
||||||
|
from django import forms |
||||||
|
from django.conf import settings |
||||||
|
from django.urls import reverse_lazy |
||||||
|
from django.utils.translation import ugettext_lazy as _ |
||||||
|
|
||||||
|
from ckeditor.widgets import CKEditorWidget |
||||||
|
|
||||||
|
|
||||||
|
from .mixins import RequestNotifiable |
||||||
|
from .models import ( |
||||||
|
Request, |
||||||
|
STATUS_NEW, STATUS_REJECTED, |
||||||
|
STATUS_PROCESSED, STATUS_IN_PROCESSING |
||||||
|
) |
||||||
|
|
||||||
|
# -------------------------------- Client-side forms -----------------------------------# |
||||||
|
|
||||||
|
|
||||||
|
class ContactUsForm(RequestNotifiable, forms.ModelForm): |
||||||
|
|
||||||
|
field_template = 'bootstrap/forms/product_search.html' |
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs): |
||||||
|
self.helper = FormHelper() |
||||||
|
self.helper.form_action = reverse_lazy('contact_us:send') |
||||||
|
self.helper.form_method = 'post' |
||||||
|
self.helper.layout = Layout( |
||||||
|
HTML(""" |
||||||
|
<div class="left-menu__title"> |
||||||
|
Не нашли нужную программу или разработчика? |
||||||
|
Заполните форму и с Вами свяжется наш специалист. |
||||||
|
</div>"""), |
||||||
|
Field('name', css_class="left-menu__text", placeholder=_("Имя"), template=self.field_template), |
||||||
|
Field('email', css_class="left-menu__text", placeholder=_("Email"), template=self.field_template), |
||||||
|
Field('subject', css_class="left-menu__text", placeholder=_("Название продукта"), template=self.field_template), |
||||||
|
Field('phone', css_class="left-menu__text", placeholder=_("Номер телефона"), template=self.field_template), |
||||||
|
Submit('send', _("Отправить"), css_class="left-menu__btn") |
||||||
|
) |
||||||
|
super(ContactUsForm, self).__init__(*args, **kwargs) |
||||||
|
self.init_fields(self.fields) |
||||||
|
|
||||||
|
def init_fields(self, fields): |
||||||
|
for field_name in fields: |
||||||
|
fields[field_name].required = True |
||||||
|
|
||||||
|
def save(self, commit=True): |
||||||
|
self.instance.status = STATUS_NEW |
||||||
|
return super().save(commit) |
||||||
|
|
||||||
|
def send_email(self, request, files=()): |
||||||
|
context = { |
||||||
|
'from_email': self.instance.type.author.email, |
||||||
|
'bcc': self.get_def_email(settings) if self.get_def_email(settings) is not None else (), |
||||||
|
'recipients': self.instance.type.author.email, |
||||||
|
'context': { |
||||||
|
'username': self.instance.name, |
||||||
|
'email': self.instance.email, |
||||||
|
'subject': self.instance.subject, |
||||||
|
'message': self.instance.message, |
||||||
|
'sent_at': self.instance.create_at, |
||||||
|
'attachments': files, |
||||||
|
'edit_link': request.scheme + "://" + request.get_host() + reverse_lazy( |
||||||
|
'admin:{}_{}_change'.format( |
||||||
|
self.instance._meta.app_label, |
||||||
|
self.instance._meta.object_name.lower()), |
||||||
|
args=(self.instance.id,) |
||||||
|
).__str__(), |
||||||
|
}, |
||||||
|
'files': [], |
||||||
|
'send_at_date': self.instance.create_at, |
||||||
|
|
||||||
|
} |
||||||
|
self.send_request_notification( |
||||||
|
context, |
||||||
|
) |
||||||
|
|
||||||
|
@property |
||||||
|
def is_request_sent(self): |
||||||
|
return self.notification_sent |
||||||
|
|
||||||
|
class Meta: |
||||||
|
model = Request |
||||||
|
fields = ('name', 'email', 'subject', 'phone') |
||||||
@ -0,0 +1,58 @@ |
|||||||
|
from django.template.loader import get_template |
||||||
|
from django.utils.translation import ugettext_lazy as _ |
||||||
|
|
||||||
|
|
||||||
|
class RequestNotifiable(object): |
||||||
|
MAIL_CATEGORY = 'Request' |
||||||
|
|
||||||
|
SLUG_NOTIFICATION = 'notification' |
||||||
|
SLUG_RESPONSE = 'response' |
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs): |
||||||
|
super(RequestNotifiable, self).__init__(*args, **kwargs) |
||||||
|
|
||||||
|
def get_mail_template(self, **kwargs): |
||||||
|
return None |
||||||
|
# return MailTemplate.objects.filter(**kwargs).first() |
||||||
|
|
||||||
|
def send_request_notification(self, context): |
||||||
|
""" |
||||||
|
:param context |
||||||
|
:param template_path string: |
||||||
|
:return None: |
||||||
|
""" |
||||||
|
notification = self.get_mail_template( |
||||||
|
slug=self.SLUG_NOTIFICATION, |
||||||
|
) |
||||||
|
|
||||||
|
kwargs = { |
||||||
|
'subject': _('New contact us request'), |
||||||
|
'message': get_template('contact_us/contact_email_notification.html').template.source, |
||||||
|
} |
||||||
|
if not notification: |
||||||
|
kwargs.update({ |
||||||
|
'name': 'Admin notification', |
||||||
|
'slug': self.SLUG_NOTIFICATION, |
||||||
|
'num_of_retries': 3, |
||||||
|
'is_html': True |
||||||
|
}) |
||||||
|
# MailTemplate.objects.create(**kwargs) |
||||||
|
else: |
||||||
|
notification.subject = kwargs.get('subject') |
||||||
|
notification.message = kwargs.get('message') |
||||||
|
notification.save() |
||||||
|
|
||||||
|
# send_db_mail( |
||||||
|
# self.SLUG_NOTIFICATION, |
||||||
|
# context.get('recipients'), |
||||||
|
# context.get('subject'), |
||||||
|
# context.get('context'), |
||||||
|
# from_email=context.get('from_email', tuple()), |
||||||
|
# bcc=context.get('bcc', tuple()), |
||||||
|
# files=context.get('files', tuple()), |
||||||
|
# send_at_date=context.get('send_at_date'), |
||||||
|
# retry=True, |
||||||
|
# retry_delay=300, |
||||||
|
# max_retries=3, |
||||||
|
# use_celery=celery_supported() |
||||||
|
# ) |
||||||
@ -0,0 +1,69 @@ |
|||||||
|
import os |
||||||
|
from django.conf import settings |
||||||
|
from django.contrib.auth import get_user_model |
||||||
|
from django.core.validators import RegexValidator |
||||||
|
from django.db import models |
||||||
|
from django.utils.translation import ugettext_lazy as _ |
||||||
|
|
||||||
|
# Create your models here. |
||||||
|
from core.models import AbstractStatusModel, STATUS_DELETED |
||||||
|
|
||||||
|
# --------------------- REQUEST STATUS LIST -------------------- |
||||||
|
|
||||||
|
STATUS_NEW = 0 |
||||||
|
from core.models import AbstractStatusModel, STATUS_NEW, STATUS_CHOICES, STATUS_ACTIVE, STATUS_DELETED |
||||||
|
|
||||||
|
# --------------------- REQUEST STATUS LIST -------------------- |
||||||
|
|
||||||
|
STATUS_IN_PROCESSING = 10 |
||||||
|
STATUS_PROCESSED = 20 |
||||||
|
STATUS_REJECTED = 40 |
||||||
|
|
||||||
|
REQUEST_STATUS_CHOICES = ( |
||||||
|
STATUS_CHOICES[0], |
||||||
|
(STATUS_IN_PROCESSING, _('Processing')), |
||||||
|
(STATUS_PROCESSED, _('Processed')), |
||||||
|
(STATUS_REJECTED, _('Rejected')), |
||||||
|
STATUS_CHOICES[-1] |
||||||
|
) |
||||||
|
|
||||||
|
REQUEST_DEFAULT_STATUS = STATUS_NEW |
||||||
|
|
||||||
|
# ----------------- REQUEST FILE STATUS LIST ------------------ |
||||||
|
|
||||||
|
FILE_REQUEST_STATUS_CHOICES = ( |
||||||
|
(STATUS_ACTIVE, _('Active')), |
||||||
|
(STATUS_DELETED, _('Deleted')), |
||||||
|
) |
||||||
|
|
||||||
|
FILE_REQUEST_DEFAULT_STATUS = STATUS_ACTIVE |
||||||
|
|
||||||
|
|
||||||
|
class Request(AbstractStatusModel): |
||||||
|
name = models.CharField(_('Name'), max_length=255) |
||||||
|
email = models.EmailField(_('Email')) |
||||||
|
subject = models.CharField(_('Subject'), max_length=500) |
||||||
|
message = models.TextField(blank=True, null=True) |
||||||
|
phone_regex = RegexValidator(regex=r'^\((+7)|8)?\d{10}$', |
||||||
|
message="Phone number must be entered in the format: '+99999999999'. Up to 12 digits allowed.") |
||||||
|
phone = models.CharField(validators=[phone_regex], max_length=12, blank=True, null=True) |
||||||
|
status = models.SmallIntegerField(_('Status'), default=REQUEST_DEFAULT_STATUS, choices=REQUEST_STATUS_CHOICES) |
||||||
|
|
||||||
|
@property |
||||||
|
def is_status_processed(self): |
||||||
|
return self.status == STATUS_PROCESSED |
||||||
|
|
||||||
|
@property |
||||||
|
def is_status_rejected(self): |
||||||
|
return self.status == STATUS_REJECTED |
||||||
|
|
||||||
|
@property |
||||||
|
def is_status_processing(self): |
||||||
|
return self.status == STATUS_IN_PROCESSING |
||||||
|
|
||||||
|
def __str__(self): |
||||||
|
return self.subject |
||||||
|
|
||||||
|
class Meta: |
||||||
|
verbose_name = _('Request') |
||||||
|
verbose_name_plural = _('Requests') |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
from django.contrib.admin.templatetags.admin_modify import submit_row, register |
||||||
|
|
||||||
|
|
||||||
|
@register.inclusion_tag('admin/contact_us/custom_submit_line.html', takes_context=True) |
||||||
|
def custom_submit_row(context): |
||||||
|
ctx = submit_row(context) |
||||||
|
ctx['show_answer'] = context.get('show_answer',True) |
||||||
|
return ctx |
||||||
@ -0,0 +1,22 @@ |
|||||||
|
from django.test import TestCase |
||||||
|
|
||||||
|
from core.tests import BehaviourTestCaseMixin |
||||||
|
|
||||||
|
from .models import Request |
||||||
|
|
||||||
|
|
||||||
|
# Create your tests here. |
||||||
|
class SendRequestWithTestCase(BehaviourTestCaseMixin, TestCase): |
||||||
|
model = Request |
||||||
|
|
||||||
|
def create_instance(self, **kwargs): |
||||||
|
return self.get_model() |
||||||
|
|
||||||
|
def send_form_without_attachments_by_db_mailer(self): |
||||||
|
pass |
||||||
|
|
||||||
|
def send_form_with_attachments_by_db_mailer(self): |
||||||
|
pass |
||||||
|
|
||||||
|
def save_attachments_locally(self): |
||||||
|
pass |
||||||
@ -0,0 +1,7 @@ |
|||||||
|
from django.conf.urls import url |
||||||
|
|
||||||
|
from .views import ContactUsFormView |
||||||
|
|
||||||
|
urlpatterns = [ |
||||||
|
url(r'^send/$', ContactUsFormView.as_view(), name='send'), |
||||||
|
] |
||||||
@ -0,0 +1,62 @@ |
|||||||
|
import logging |
||||||
|
|
||||||
|
from django.contrib import messages |
||||||
|
from django.contrib.messages.views import SuccessMessageMixin |
||||||
|
from django.db import transaction |
||||||
|
from django.urls import reverse_lazy |
||||||
|
from django.views.generic import FormView |
||||||
|
from django.utils.translation import ugettext_lazy as _ |
||||||
|
|
||||||
|
from .forms import ContactUsForm |
||||||
|
|
||||||
|
logger = logging.getLogger(__name__) |
||||||
|
|
||||||
|
|
||||||
|
# Create your views here. |
||||||
|
class ContactUsFormView(SuccessMessageMixin, FormView): |
||||||
|
template_name = None |
||||||
|
form_class = ContactUsForm |
||||||
|
http_method_names = ['post'] |
||||||
|
success_message = _('Request has been sent successfully!') |
||||||
|
error_message = _('Some errors occurred during sending the request. Check the input fields or try latter.') |
||||||
|
|
||||||
|
def get_initial(self): |
||||||
|
initial = super().get_initial() |
||||||
|
if self.request.method == "GET": |
||||||
|
initial['type'] = RequestType.objects.order_by('order').first() |
||||||
|
if self.request.user.is_authenticated(): |
||||||
|
initial['name'] = self.request.user.profile.get_full_name(self.request, self.request.user.profile) |
||||||
|
initial['email'] = self.request.user.email |
||||||
|
return initial |
||||||
|
|
||||||
|
def form_valid(self, form): |
||||||
|
try: |
||||||
|
with transaction.atomic(): |
||||||
|
if self.request.user.is_authenticated(): |
||||||
|
form.instance.user = self.request.user |
||||||
|
form.save() |
||||||
|
files = [] |
||||||
|
if len(self.request.FILES) > 0: |
||||||
|
attachments = AttachmentFormSet(self.request.POST, self.request.FILES, form_kwargs={'request': form.instance}) |
||||||
|
if attachments.is_valid(): |
||||||
|
attachments.save() |
||||||
|
files = attachments.get_file_absolute_urls(self.request) |
||||||
|
form.send_email(self.request, files) |
||||||
|
return super().form_valid(form) |
||||||
|
except ValueError as ve: |
||||||
|
logger.critical(ve) |
||||||
|
messages.error(self.request, self.error_message, 'danger') |
||||||
|
except Exception as e: |
||||||
|
logger.critical(e) |
||||||
|
messages.error(self.request, self.error_message, 'danger') |
||||||
|
|
||||||
|
return self.form_invalid(form) |
||||||
|
|
||||||
|
def form_invalid(self, form): |
||||||
|
context = self.get_context_data() |
||||||
|
if len(form.errors) > 0: |
||||||
|
context['form_show_errors'] = True |
||||||
|
return self.render_to_response(context) |
||||||
|
|
||||||
|
def get_success_url(self): |
||||||
|
return super().get_success_url() |
||||||
Loading…
Reference in new issue