diff --git a/README.md b/README.md index 7e0fa3c7..763a9e38 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,6 @@ INSTALLED_APPS += ( ) RAVEN_CONFIG = { 'dsn': 'http://474617c96350412d80735900c6717b9a:330285c9034947a181cbae8b52bb15d8@88.198.17.35:9000/3', - 'release': raven.fetch_git_sha(os.path.dirname(os.pardir)), } ``` diff --git a/apps/accounts/urls.py b/apps/accounts/urls.py index 8f2a78f8..e47e2dda 100644 --- a/apps/accounts/urls.py +++ b/apps/accounts/urls.py @@ -8,7 +8,6 @@ from views import ( CalendarView, Feed, HomeView, - MailingSettings, NameView, PhoneView, ProfileCompanyView, @@ -21,7 +20,6 @@ from views import ( urlpatterns = patterns('', url(r'^profile/company/$', login_required(ProfileCompanyView.as_view())), - url(r'^profile/mailing/$', MailingSettings.as_view(), name='accounts-mailing_settings'), url(r'^profile/settings/$', login_required(SettingsView.as_view()), name='accounts_settings'), url(r'^profile/calendar/remove/$', 'accounts.views.remove_from_calendar'), url(r'^profile/calendar/export/$', 'core.views.download_workbook'), @@ -44,7 +42,7 @@ urlpatterns = patterns('', url(r'^profile/change-password/', 'accounts.views.change_password'), url( r'^profile/subscribe-themes-tags/$', - login_required(UserSubscribeThemesTagsView.as_view()), + UserSubscribeThemesTagsView.as_view(), name='user_subscribe_themes_tags' ), diff --git a/apps/accounts/views.py b/apps/accounts/views.py index 02f65870..7cedd3cd 100644 --- a/apps/accounts/views.py +++ b/apps/accounts/views.py @@ -27,7 +27,7 @@ from company.edit_forms import NameForm as CompNameForm, HomeForm as CompHomeFor EmailForm as CompEmailForm, WebPageForm as CompWebPageForm, SocialForm as CompSocialForm,\ TagForm as CompTagForm, DescriptionForm as CompDescr, StaffForm as CompStaff, \ FoundationForm as CompFound, SpecializationForm as CompSpec, AddressForm as CompAddress -from emencia.django.newsletter.forms import SubscribeSettingsForm, MailingSettingsForm +from emencia.django.newsletter.forms import SubscribeSettingsForm from emencia.django.newsletter.models import Contact, ContactSettings from .forms import ChangePasswordForm, FeedFilterForm @@ -115,41 +115,6 @@ class GetUserMixin(object): return instance -class MailingSettings(GetUserMixin, ContextMixin, AjaxableResponseMixin, CreateUpdateView): - form_class = MailingSettingsForm - template_name = 'client/newsletters/mailing_settings.html' - success_url = reverse_lazy('accounts-mailing_settings') - - def get_success_url(self): - return self.success_url - - def get_object(self): - self.extra_ctx.update({ - 'r_cities': City.used.russia(), - }) - instance = self.get_user() - if instance is not None: - self.extra_ctx.update({ - 'checked_f_countries': list(instance.f_countries.values_list('pk', flat=True)), - 'checked_r_cities': list(instance.r_cities.values_list('pk', flat=True)), - 'checked_tg': list(instance.tags.values_list('pk', flat=True)), - 'checked_th': list(instance.themes.values_list('pk', flat=True)), - 'contact': instance, - }) - if self.request.GET.get('unsibscribe') and instance.subscriber: - instance.unsubscribe() - self.extra_ctx.update({'unsubscribe_success': True}) - elif not instance.subscriber: - self.extra_ctx.update({'unsubscribed': True}) - return instance - - def form_valid(self, form): - return super(MailingSettings, self).form_valid(form) - - def form_invalid(self, form): - return super(MailingSettings, self).form_invalid(form) - - class CalendarView(TemplateView): """ display template with user calendar(one month) @@ -578,11 +543,14 @@ class UserSubscribeThemesTagsView(GetUserMixin, TemplateView): ctx = super(UserSubscribeThemesTagsView, self).get_context_data(**kwargs) data = [] instance = self.get_user() + user_themes = [] + user_tags = [] themes = Theme.objects.language().values('pk', 'name') - user_themes = instance.themes.values_list('pk', flat=True) - user_tags = instance.tags.values_list('pk', flat=True) + if instance is not None: + user_themes = instance.themes.values_list('pk', flat=True) + user_tags = instance.tags.values_list('pk', flat=True) for theme in themes: tags = [] @@ -607,4 +575,6 @@ class UserSubscribeThemesTagsView(GetUserMixin, TemplateView): def render_to_response(self, context, **response_kwargs): context.pop('view') - return HttpResponse(json.dumps(context), content_type=self.content_type) + return HttpResponse( + json.dumps(context), content_type=self.content_type + ) diff --git a/apps/conference/forms.py b/apps/conference/forms.py index e647c33e..8d034de4 100644 --- a/apps/conference/forms.py +++ b/apps/conference/forms.py @@ -56,7 +56,7 @@ class ConferenceCreateForm(forms.Form): #organiser = forms.MultipleChoiceField(label=u'Организаторы', required=False, # choices=[(item.id, item.name) for item in Organiser.objects.language().all()]) org = forms.CharField(required=False, label=_(u'Организатор')) - country = forms.ChoiceField(label=_(u'Страна'), choices=[(c.id, c.name) for c in Country.objects.language().all()]) + country = forms.ChoiceField(label=_(u'Страна'), choices=list(set([(c.id, c.name) for c in Country.objects.language().all()]))) theme = forms.MultipleChoiceField(label=_(u'Тематики'), choices=[(item.id, item.name) for item in Theme.objects.language().all()]) diff --git a/apps/emencia/django/newsletter/forms.py b/apps/emencia/django/newsletter/forms.py index 28623a57..ca385020 100644 --- a/apps/emencia/django/newsletter/forms.py +++ b/apps/emencia/django/newsletter/forms.py @@ -81,6 +81,12 @@ class MailingListSubscriptionForm(forms.ModelForm): class MailingSettingsForm(forms.ModelForm): + email = forms.EmailField( + error_messages={ + 'required': _(u'Поле e-mail обязательно для заполнения') + }, + widget=forms.TextInput(attrs={'placeholder': _(u'Ваш e-mail')}) + ) r_cities = ML_ModelMultipleChoiceField( label=_(u'Города России'), required=False, queryset=City.objects.all()) @@ -100,11 +106,12 @@ class MailingSettingsForm(forms.ModelForm): class Meta: model = Contact fields = [ - 'moscow', 'russia', 'r_cities', 'foreign', + 'email', 'first_name', 'moscow', 'russia', 'r_cities', 'foreign', 'periodic', 'periodic_day', 'content_news', 'content_overview', 'content_articles', ] widgets = { + 'first_name': forms.TextInput(attrs={'placeholder': _(u'Ваше имя')}), 'moscow': forms.CheckboxInput(), 'foreign': forms.CheckboxInput(), 'periodic': forms.RadioSelect(), @@ -119,6 +126,10 @@ class MailingSettingsForm(forms.ModelForm): for field in ['co', 'r_cities', 'tg', 'th', 'area']: self.fields[field].widget.attrs.update({'style': 'display: none;'}) if self.instance and self.instance.pk: + # если пользовать авторизован, у нас есть инстанс + # и там не нужно выводить для редактирования его личные данные + del self.fields['email'] + del self.fields['first_name'] # area self.initial['area'] = set(self.instance.area.values_list('pk', flat=True)) area_q = Area.objects.language().all().order_by('name') @@ -184,6 +195,19 @@ class MailingSettingsForm(forms.ModelForm): obj.save() return obj + def clean(self): + cleaned_data = super(MailingSettingsForm, self).clean() + if not cleaned_data.get('tg') and not cleaned_data.get('th'): + raise forms.ValidationError(_(u'Для успешной подписки необходимо ' + u'выбрать тематики событий, которые ' + u'вам интересны. На основе этих ' + u'настроек мы включим в ваше письмо ' + u'релевантные события!')) + if not cleaned_data.get('moscow') and not cleaned_data.get('russia') and not cleaned_data.get('foreign') and not cleaned_data.get('area') and not cleaned_data.get('r_cities') and not cleaned_data.get('co'): + raise forms.ValidationError(_(u'Необходимо выбрать минимум ' + u'1 вариант в гео-фильтрах')) + return cleaned_data + class AllMailingListSubscriptionForm(MailingListSubscriptionForm): """Form for subscribing to all mailing list""" @@ -221,9 +245,10 @@ class ContactForm(forms.ModelForm): def clean_email(self): email = self.cleaned_data['email'] try: - self.instance = Contact.objects.get(email__iexact=email) - return email - except (Contact.DoesNotExist, ): + Contact.objects.get(email__iexact=email) + raise forms.ValidationError(_(u'Указанный e-mail адрес уже ' + u'подписан на рассылку')) + except Contact.DoesNotExist: pass return email diff --git a/apps/emencia/django/newsletter/mailer.py b/apps/emencia/django/newsletter/mailer.py index 2f6fa504..dc9c041e 100644 --- a/apps/emencia/django/newsletter/mailer.py +++ b/apps/emencia/django/newsletter/mailer.py @@ -41,6 +41,7 @@ from django.template.loader import render_to_string, get_template from django.utils.encoding import smart_str from django.utils.encoding import smart_unicode from django.utils.translation import ugettext as _ +from django.utils import timezone from django.core.files.storage import default_storage from django.core.urlresolvers import reverse from django.core.exceptions import SuspiciousOperation @@ -167,7 +168,13 @@ class NewsLetterSender(object): all the attached files. """ - content_html = self.build_email_content(contact, announce_context, name) + content_html = self.build_email_content(contact, announce_context) + if '{name}' in content_html: + content_html = content_html.format( + **{ + 'name': name or contact.first_name or contact.last_name or _(u'Подписчик') + } + ) h = HTMLParser.HTMLParser() content_html = h.unescape(content_html) @@ -344,7 +351,6 @@ class NewsLetterSender(object): logo_path = default_storage.path('newsletter/images/no-logo.png') elif logo and not logo_path: logo_path = logo.path - print(logo, logo_path, obj) try: ctype, encoding = mimetypes.guess_type(logo_path) @@ -392,7 +398,7 @@ class NewsLetterSender(object): title = self.newsletter.title2.format(**self.preheader_ctx) return title - def build_email_content(self, contact, announce_context=None, name=None): + def build_email_content(self, contact, announce_context=None): """Generate the mail for a contact""" uidb36, token = tokenize(contact) context = Context({'contact': contact, @@ -400,7 +406,6 @@ class NewsLetterSender(object): 'newsletter': self.newsletter, 'tracking_image_format': TRACKING_IMAGE_FORMAT, 'uidb36': uidb36, 'token': token, - 'name': name or contact.first_name or contact.last_name or _(u'Подписчик'), 'settings_links': self.settings_links, }) if self.announce: @@ -642,16 +647,18 @@ class Mailer(NewsLetterSender): expedition_list = self.expedition_list - number_of_recipients = len(expedition_list) - if self.verbose: - print '%i emails will be sent' % number_of_recipients + # number_of_recipients = len(expedition_list) + # if self.verbose: + # print '%i emails will be sent' % number_of_recipients - i = 1 - for contact in expedition_list: + # FIXME: Временно сделана отправка только 1 письма. + # См FIXME в команде send_newsletter + for i, contact in enumerate(expedition_list, 1): + print 'sending to contact: %s, %s (%s)' % (contact.pk, contact.email, timezone.now()) send = True - if self.verbose: - print '- Processing %s/%s (%s)' % ( - i, number_of_recipients, contact.pk) + # if self.verbose: + # print '- Processing %s/%s (%s)' % ( + # i, number_of_recipients, contact.pk) # self.build_preheader_ctx(contact) @@ -693,8 +700,6 @@ class Mailer(NewsLetterSender): self.smtp.quit() self.smtp_connect() - i += 1 - # маркируем оставшиеся контакты на второй этап self.mark_contacts_for_second_stage() @@ -716,10 +721,13 @@ class Mailer(NewsLetterSender): return [] if self.newsletter.dailymail and not self.test: - return self.newsletter.get_dailymail_subscribers() + # FIXME: врменно добавил жёсткий срез для отправки 1 письма + return self.newsletter.get_dailymail_subscribers()[:1] qs = super(Mailer, self).expedition_list + # FIXME: врменно добавил жёсткий срез для отправки 1 письма + self.credits = 1 qs = qs[:self.credits] if self.test: diff --git a/apps/emencia/django/newsletter/management/commands/send_newsletter.py b/apps/emencia/django/newsletter/management/commands/send_newsletter.py index f88b2c12..3ea4e289 100644 --- a/apps/emencia/django/newsletter/management/commands/send_newsletter.py +++ b/apps/emencia/django/newsletter/management/commands/send_newsletter.py @@ -1,9 +1,11 @@ +# -*- coding: utf-8 -*- """Command for sending the newsletter""" from datetime import datetime, timedelta from time import sleep from django.conf import settings from django.utils.translation import activate +from django.utils import timezone from django.core.management.base import NoArgsCommand from emencia.django.newsletter.settings import SEND_HOUR_LOOP @@ -20,14 +22,17 @@ class Command(NoArgsCommand): self.verbose = int(options['verbosity']) activate(settings.LANGUAGE_CODE) - if SEND_HOUR_LOOP: - while self.end_time > datetime.now(): - self.send() - sleep(30) - else: - self.send() + # FIXME: временно делаем одну отправку и завершаем. + # Это сделано для того, чтобы отправка писем была более контролируемая + self.send() + # if SEND_HOUR_LOOP: + # while self.end_time > datetime.now(): + # self.send() + # sleep(30) + # else: + # self.send() if self.verbose: - print 'End session sending' + print 'End session sending (%s)' % timezone.now() def send(self): for newsletter in Newsletter.objects.exclude( diff --git a/apps/emencia/django/newsletter/models.py b/apps/emencia/django/newsletter/models.py index 87572e49..b88b4224 100644 --- a/apps/emencia/django/newsletter/models.py +++ b/apps/emencia/django/newsletter/models.py @@ -157,10 +157,10 @@ class Contact(models.Model): from_users = models.BooleanField(default=False) dailymailing = models.BooleanField(default=False) - moscow = models.BooleanField(_(u'Москва'), blank=True, default=True) - russia = models.BooleanField(_(u'Россия'), blank=True, default=True) + moscow = models.BooleanField(_(u'Москва'), blank=True, default=False) + russia = models.BooleanField(_(u'Россия'), blank=True, default=False) r_cities = models.ManyToManyField('city.City', blank=True, null=True, verbose_name=_(u'Города России')) - foreign = models.BooleanField(_(u'Зарубеж'), blank=True, default=True) + foreign = models.BooleanField(_(u'Зарубеж'), blank=True, default=False) f_countries = models.ManyToManyField('country.Country', blank=True, null=True, verbose_name=_(u'Зарубежные страны')) area = models.ManyToManyField('country.Area', blank=True, null=True, verbose_name=_(u'Географическая зона')) periodic = models.PositiveSmallIntegerField(_(u'Периодичность отправки'), diff --git a/apps/emencia/django/newsletter/urls/__init__.py b/apps/emencia/django/newsletter/urls/__init__.py index c9bf4298..14c82185 100644 --- a/apps/emencia/django/newsletter/urls/__init__.py +++ b/apps/emencia/django/newsletter/urls/__init__.py @@ -12,7 +12,7 @@ urlpatterns = patterns('', url(r'^statistics/', include('emencia.django.newsletter.urls.statistics')), url(r'^', include('emencia.django.newsletter.urls.newsletter')), - url(r'^test-letter/', TemplateView.as_view(template_name='client/newsletters/announce_template.html')), + url(r'^test-letter/', TemplateView.as_view(template_name='client/newsletters/announce_template.html'), name='newsletter_test_letter'), url(r'^activation/send/', TemplateView.as_view(template_name='client/newsletters/activation_send.html'), name='subscription_activation_send'), url(r'^activation/complete/', TemplateView.as_view(template_name='client/newsletters/activation_complete.html'), diff --git a/apps/emencia/django/newsletter/views/expo_views.py b/apps/emencia/django/newsletter/views/expo_views.py index 38b97bfc..45d16142 100644 --- a/apps/emencia/django/newsletter/views/expo_views.py +++ b/apps/emencia/django/newsletter/views/expo_views.py @@ -3,7 +3,7 @@ import json from django.core.urlresolvers import reverse_lazy from django.views.generic import TemplateView, FormView -from django.http import HttpResponseRedirect, HttpResponse +from django.http import HttpResponse from django.shortcuts import redirect from emencia.django.newsletter.forms import ContactForm @@ -13,82 +13,97 @@ from emencia.django.newsletter.forms import ( ) from accounts.models import User from accounts.views import GetUserMixin -from functions.custom_views import ContextMixin +from functions.http import JsonResponse from city.models import City -class SubscribeView(GetUserMixin, ContextMixin, FormView): - form_class = ContactForm +class SubscribeView(GetUserMixin, FormView): + """ + Представление для подписки не/авторизованных пользователей + """ template_name = 'client/newsletters/mailing_settings.html' - success_url = reverse_lazy('subscription_activation_send') - - def get_form(self, form_class): - if self.request.POST: - email = self.request.POST.get('email') - if email: - try: - contact = Contact.objects.get(email=email) - return form_class(instance=contact, - **self.get_form_kwargs()) - except Contact.DoesNotExist: - pass - - return form_class(**self.get_form_kwargs()) - else: - return form_class(**self.get_form_kwargs()) + form_class = MailingSettingsForm + + def get_object(self): + return self.get_user() + + def get(self, request, *args, **kwargs): + self.object = self.get_object() + return super(SubscribeView, self).get(request, *args, **kwargs) + + def post(self, request, *args, **kwargs): + self.object = self.get_object() + return super(SubscribeView, self).post(request, *args, **kwargs) def form_valid(self, form): - contact = form.save() - contact.send_activation() - return HttpResponseRedirect(self.success_url) + if self.request.POST.get('save'): + contact = form.save() + if not self.request.user.is_authenticated(): + contact.send_activation() + + if self.request.is_ajax(): + data = {'success': True} + if self.request.POST.get('save'): + data['redirect_url'] = str(self.get_success_url()) + return JsonResponse(data) + + return redirect(self.get_success_url()) + + def form_invalid(self, form): + if self.request.is_ajax(): + data = { + 'form_errors': form.errors, + } + return JsonResponse(data, status=400) + return self.render_to_response(self.get_context_data(form=form)) def get_initial(self): data = super(SubscribeView, self).get_initial() - if self.request.user.is_authenticated(): - email = getattr(self.request.user, 'email') - data['email'] = email - data['first_name'] = getattr(self.request.user, 'first_name') - if self.request.GET: - if self.request.GET.get('email'): - data['email'] = self.request.GET['email'] - if self.request.GET.get('first_name'): - data['first_name'] = self.request.GET['first_name'] + if self.request.GET.get('email'): + data['email'] = self.request.GET['email'] + if self.request.GET.get('first_name'): + data['first_name'] = self.request.GET['first_name'] return data + def get_form_kwargs(self): + kwargs = super(SubscribeView, self).get_form_kwargs() + if self.request.user.is_authenticated(): + kwargs.update({'instance': self.object}) + return kwargs + + def get_success_url(self): + if not self.request.user.is_authenticated(): + return reverse_lazy('subscription_activation_send') + return reverse_lazy('newsletter_subscription') + def get_context_data(self, **kwargs): ctx = super(SubscribeView, self).get_context_data(**kwargs) - ctx['object'] = self.get_mailsettings_object() - ctx['mailsettings_form'] = MailingSettingsForm( - instance=self.get_user() - ) + ctx['object'] = self.object + ctx['r_cities'] = self.get_russian_cities() + ctx['checked_th'] = self.get_checked_th() + + if self.object is not None: + if self.request.GET.get('unsibscribe') and self.object.subscriber: + self.object.unsubscribe() + ctx['unsubscribe_success'] = True + elif not self.object.subscriber: + ctx['unsubscribed'] = True + return ctx - def get_mailsettings_object(self): + def get_russian_cities(self): + """ + :return: города России + """ + return City.used.russia() + + def get_checked_th(self): """ - передаём контекст в шаблон по городам, странам, а так же выбранным - :return: instance of mail settings + :return: выбранные пользователем темы """ - self.extra_ctx.update({ - 'r_cities': City.used.russia(), - }) - instance = self.get_user() - if instance is not None: - self.extra_ctx.update({ - 'checked_f_countries': list( - instance.f_countries.values_list('pk', flat=True)), - 'checked_r_cities': list( - instance.r_cities.values_list('pk', flat=True)), - 'checked_tg': list(instance.tags.values_list('pk', flat=True)), - 'checked_th': list( - instance.themes.values_list('pk', flat=True)), - 'contact': instance, - }) - if self.request.GET.get('unsibscribe') and instance.subscriber: - instance.unsubscribe() - self.extra_ctx.update({'unsubscribe_success': True}) - elif not instance.subscriber: - self.extra_ctx.update({'unsubscribed': True}) - return instance + if self.object is not None: + return self.object.themes.values_list('pk', flat=True) + return [] class ActivationView(TemplateView): diff --git a/apps/exposition/admin.py b/apps/exposition/admin.py index ba930e68..a8d23af5 100644 --- a/apps/exposition/admin.py +++ b/apps/exposition/admin.py @@ -278,7 +278,7 @@ class ExpositionView(AdminView): 'expohit': obj.expohit, 'discount': obj.discount, 'canceled': obj.canceled, 'moved': obj.moved, 'logo': obj.logo, 'visitors': obj.visitors, 'members': obj.members, - 'audience':[item for item, bool in obj.audience.all() if bool], + 'audience': [item for item in obj.audience.all()], 'quality_label': [item for item, bool in obj.quality_label if bool], 'place_alt': obj.place_alt} diff --git a/apps/exposition/forms.py b/apps/exposition/forms.py index 05172454..ed495358 100644 --- a/apps/exposition/forms.py +++ b/apps/exposition/forms.py @@ -49,7 +49,7 @@ class ExpositionCreateForm(forms.Form): #company = forms.MultipleChoiceField(label=u'Компании', required=False, # choices=[(item.id, item.name) for item in Company.objects.language().all()] ) - country = forms.ChoiceField(label=_(u'Страна'), choices=[(c.id, c.name) for c in Country.objects.language().all()]) + country = forms.ChoiceField(label=_(u'Страна'), choices=list(set([(c.id, c.name) for c in Country.objects.language().all()]))) theme = forms.MultipleChoiceField( label=_(u'Тематики'), choices=[(item.id, item.name) for item in Theme.objects.language().filter(types=Theme.types.exposition)]) diff --git a/apps/file/admin.py b/apps/file/admin.py index 5a2a1b0a..fbb446c4 100644 --- a/apps/file/admin.py +++ b/apps/file/admin.py @@ -4,12 +4,15 @@ from django.views.decorators.csrf import csrf_exempt from django.db.models.loading import get_model from django.contrib.contenttypes.models import ContentType from django.http import HttpResponse -from django.views.generic import UpdateView +from django.views.generic import FormView +from django.shortcuts import get_object_or_404 +from django.conf import settings from .models import FileModel from .forms import FileForm, FileUpdateForm import json +import magic @csrf_exempt @@ -27,8 +30,15 @@ def ajax_post_file(request, obj_id): if request.is_ajax() and request.method == 'POST': file_form = FileForm(request.POST, request.FILES) if file_form.is_valid(): - file_form.save(request.FILES, obj) + f = file_form.save(request.FILES, obj) + mime = magic.Magic(mime=True) data['success'] = True + data['name'] = f.file_name or f.file_path.name + data['size'] = f.file_path.size + data['file'] = f.file_path.url + data['type'] = mime.from_file(f.file_path.path) + data['remove_url'] = reverse('ajax_delete_file', args=[f.pk]) + data['detail_link'] = reverse('file_update', args=[f.pk]) else: data['errors'] = file_form.errors @@ -39,11 +49,12 @@ def ajax_post_file(request, obj_id): ) files_data = [] for f in files: + mime = magic.Magic(mime=True) files_data.append({ 'name': f.file_name or f.file_path.name, 'size': f.file_path.size, 'file': f.file_path.url, - 'type': 'file', + 'type': mime.from_file(f.file_path.path), 'remove_url': reverse('ajax_delete_file', args=[f.pk]), 'detail_link': reverse('file_update', args=[f.pk]) }) @@ -66,10 +77,36 @@ def ajax_delete_file(request, id): return HttpResponse(json.dumps(data), content_type='application/json') -class FileUpdateView(UpdateView): +class FileUpdateView(FormView): """ Представление обновления файла """ template_name = 'c_admin/file/file_update.html' form_class = FileUpdateForm - model = FileModel + + def get_object(self): + pk = self.kwargs.get('pk') + return get_object_or_404(FileModel, pk=pk) + + def get_initial(self): + data = super(FileUpdateView, self).get_initial() + obj = self.get_object() + data['file_path'] = obj.file_path + data['purpose'] = obj.purpose + for lid, (code, name) in enumerate(settings.LANGUAGES): + data['file_name_%s' % code] = obj.file_name.translate(code) + data['description_%s' % code] = obj.description.translate(code) + return data + + def form_valid(self, form): + form.save(self.request, self.kwargs.get('pk')) + return super(FileUpdateView, self).form_valid(form) + + def get_success_url(self): + return reverse('file_update', args=[self.get_object().pk]) + + def get_context_data(self, **kwargs): + ctx = super(FileUpdateView, self).get_context_data(**kwargs) + ctx['object'] = self.get_object() + ctx['languages'] = settings.LANGUAGES + return ctx diff --git a/apps/file/forms.py b/apps/file/forms.py index c6d0c30a..9afb71ea 100644 --- a/apps/file/forms.py +++ b/apps/file/forms.py @@ -84,13 +84,46 @@ class FileForm(forms.Form): return file_obj -class FileUpdateForm(forms.ModelForm): +class FileUpdateForm(forms.Form): """ Форма обновления файла в админ панели """ - class Meta: - model = FileModel - fields = ('file_path',) + file_path = forms.FileField(label=_(u'Выберите файл')) + purpose = forms.ChoiceField(label=_(u'Назаначение'), choices=PURPOSES) + + def __init__(self, *args, **kwargs): + """ + Создаём динамические поля переводов + """ + super(FileUpdateForm, self).__init__(*args, **kwargs) + for lid, (code, name) in enumerate(settings.LANGUAGES): + self.fields['file_name_%s' % code] = forms.CharField( + label=_(u'Имя файла'), + required=False, + widget=forms.TextInput(attrs={'placeholder': 'Имя'}) + ) + self.fields['description_%s' % code] = forms.CharField( + label=_(u'Описание'), + required=False, + widget=CKEditorWidget() + ) + + def save(self, request, id=None): + data = self.cleaned_data + + if id is None: + f = FileModel() + else: + f = FileModel.objects.get(id=id) + + f.file_path = data['file_path'] + f.purpose = data['purpose'] + f.save() + + # fill translated fields and save object + fill_trans_fields_all(FileModel, f, data, id=id) + + return f class FileModelForm(forms.Form): diff --git a/apps/file/models.py b/apps/file/models.py index 5200d76b..c0bb61eb 100644 --- a/apps/file/models.py +++ b/apps/file/models.py @@ -22,7 +22,7 @@ PURPOSES = ( ('scheme teritory', _(u'Схема територии')), ('diplom', _(u'Дипломы')), ('preview', _(u'Превью')), - ('preview2', _(u'Превью')), + ('preview2', _(u'Превью 2')), ) diff --git a/apps/import_xls/excel_settings.py b/apps/import_xls/excel_settings.py index 770e1561..11a7af80 100644 --- a/apps/import_xls/excel_settings.py +++ b/apps/import_xls/excel_settings.py @@ -284,7 +284,7 @@ event_sett = { u'Основные темы': {u'field': u'main_themes', u'func': unicode}, u'Условия и скидка': {u'field': u'discount_description', u'func': unicode}, u'Периодичность': {u'field': u'periodic', u'func': to_periodic},### - u'Аудитория': {u'field': u'audience', u'func': to_audience}, + u'Аудитория': {u'field': u'audience', u'func': to_audience, u'method': True}, u'Официальный веб-сайт': {u'field': u'web_page', u'func': to_url}, u'Линк на регистрацию': {u'field': u'link', u'func': to_url}, u'Экспонируемые продукты': {u'field': u'products', u'func': unicode}, @@ -511,4 +511,4 @@ import_settings={ 'is_published': {'func': bool}, 'canceled_by_administrator': {'func': bool}, 'types': {'func': to_theme_type} -} \ No newline at end of file +} diff --git a/apps/import_xls/import_forms.py b/apps/import_xls/import_forms.py index 39b99f29..35dd6ef3 100644 --- a/apps/import_xls/import_forms.py +++ b/apps/import_xls/import_forms.py @@ -420,7 +420,6 @@ class ImportEventForm(ImportForm): if setting is None: continue - if setting.get('method'): # this cell contains data that must be written after creating object if cell != "": @@ -549,40 +548,39 @@ class ImportEventForm(ImportForm): list_dicts.append(d) + """ + # go through row cells + # field name current cell + field_name = field_names[col_number] + if field_name =='theme': + # need save object before saving manytomany field + #object.save() + setting = import_settings.get(field_name) + d[setting] = cell - """ - # go through row cells - # field name current cell - field_name = field_names[col_number] - if field_name =='theme': - # need save object before saving manytomany field - #object.save() - setting = import_settings.get(field_name) - d[setting] = cell - - if setting is not None: - # if setting exist for this field - func = setting.get('func') - if func is not None: - extra_value = setting.get('extra_values') - if extra_value is not None: - # if setting has extra value then - # it is some field like city, theme, tag - # that has relation and can be created - - # in function we add language(need for relation fields) - # and extra value from object (like for city need country) - if cell: - value = func(cell, lang, getattr(object, extra_value)) - else: - value = None - else: - value = func(cell) - if value: - setattr(object, field_name, value) - """ + if setting is not None: + # if setting exist for this field + func = setting.get('func') + if func is not None: + extra_value = setting.get('extra_values') + if extra_value is not None: + # if setting has extra value then + # it is some field like city, theme, tag + # that has relation and can be created + + # in function we add language(need for relation fields) + # and extra value from object (like for city need country) + if cell: + value = func(cell, lang, getattr(object, extra_value)) + else: + value = None + else: + value = func(cell) + if value: + setattr(object, field_name, value) + """ - #object.save() + #object.save() diff --git a/apps/import_xls/utils.py b/apps/import_xls/utils.py index 049451a5..3931adad 100644 --- a/apps/import_xls/utils.py +++ b/apps/import_xls/utils.py @@ -6,9 +6,8 @@ from PIL import Image from django.conf import settings from django.utils import translation from hvad.utils import get_translation_aware_manager -from bitfield import BitHandler from place_exposition.models import PlaceExposition -from exposition.models import Exposition +from events.models import TargetAudience from country.models import Country from city.models import City from theme.models import Theme, Tag @@ -134,32 +133,29 @@ def to_periodic(value): return periodic.get(value, 0) -def to_audience(value, model=Exposition): - if value: - translation.activate('ru') - l = value.split(', ') - if l: - new_list = [] - for value in l: - for item1, item2 in BIT_AUDIENCE: - if value == item2: - new_list.append(item1) - if new_list: - return reduce(lambda x,y: x|y, (getattr(model.audience, item) for item in new_list)) - return 0 +def to_audience(obj, value): + # new_list = [] + # if value: + # translation.activate('ru') + # l = value.split(', ') + # target_audience = TargetAudience.objects.all() + # print l + # for value in l: + # for ta in target_audience: + # if value == ta.title: + # new_list.append(ta.pk) + # return new_list + translation.activate('ru') + target_audience = TargetAudience.objects.filter(title__in=value.split(', ')).values_list('pk', flat=True) + obj.audience.clear() + obj.audience.add(*TargetAudience.objects.filter(id__in=target_audience)) + return None + def get_audience(value): - if isinstance(value, BitHandler): - l = [k for k, v in value.iteritems() if v] - if l: - new_list = [] - for value in l: - for item1, item2 in BIT_AUDIENCE: - if value == item1: - new_list.append(unicode(item2)) - - return ', '.join(new_list) - return '' + new_list = [x.title for x in value.all()] + return ', '.join(new_list) + import types def save_logo(obj, path): diff --git a/apps/place_exposition/forms.py b/apps/place_exposition/forms.py index aca9867d..ce0f721a 100644 --- a/apps/place_exposition/forms.py +++ b/apps/place_exposition/forms.py @@ -30,7 +30,7 @@ class ExpositionForm(forms.Form): type = forms.ChoiceField(required=False, choices=types) logo = forms.ImageField(label=_(u'Logo'), required=False, max_length=500) - country = forms.ChoiceField(label=_(u'Страна'), choices=[(c.id, c.name) for c in Country.objects.language().all()]) + country = forms.ChoiceField(label=_(u'Страна'), choices=list(set([(c.id, c.name) for c in Country.objects.language().all()]))) # creates select input with empty choices cause it will be filled with ajax city = forms.CharField(label=_(u'Город'), widget=forms.HiddenInput()) diff --git a/proj/admin.py b/proj/admin.py index 2f83ba63..aaf121a5 100644 --- a/proj/admin.py +++ b/proj/admin.py @@ -26,7 +26,7 @@ def ajax_city(request): returns html '; - } - }); - $('#id_tag').append(html); - //select previous selected values - $('#id_tag option').each(function() { - var check = $.inArray($(this).val(), selectedValues) - if (check != -1){ - $(this).attr('selected', 'selected'); - } - }); + $.get("/admin/ajax_tag/", {'id': $(this).serialize()}, function(data){ + var optionValues = []; + var getValues = []; + var selectedValues = []; + + //push values sended from server in array + $.each(data, function(i, elem){ + getValues.push(elem[0].toString()) + }); + + //delete options if they aren't in getvalues + //otherwise push it in array + //also push in array already selected values + $('#id_tag option').each(function() { + var check = $.inArray($(this), getValues); + if ($(this).is(':selected') ){ + selectedValues.push($(this).val()) + } + if (check == -1){ + $(this).remove() + } + else{ + optionValues.push($(this).val()); + } + }); + + //generate new options + //old options unchanged + var html = '' + $.each(data, function(i, elem){ + var check = $.inArray(elem[0].toString(), optionValues); + + if (check == -1){ + html += ''; + } + }); + + $('#id_tag').append(html); + //select previous selected values + $('#id_tag option').each(function() { + var check = $.inArray($(this).val(), selectedValues) + if (check != -1){ + $(this).attr('selected', 'selected'); + } + }); });//end get });//end change @@ -499,7 +506,11 @@ $(document).ready(function(){ allowClear: true }); - } catch (e){} + } catch (e){ + console.log('===== Error ====='); + console.warn(e); + console.log('================='); + } } diff --git a/static/custom_js/select_tag.js b/static/custom_js/select_tag.js index aab5af84..b7f2fdd9 100644 --- a/static/custom_js/select_tag.js +++ b/static/custom_js/select_tag.js @@ -1,52 +1,56 @@ // replace $(document).ready(function(){ - $('#id_theme').change(function(){ - $.get( - "/admin/ajax_tag/", {'id': $(this).serialize()}, function(data){ - var optionValues = []; - var getValues = []; - var selectedValues = [] - //push values sended from server in array - $.each(data, function(i, elem){ - getValues.push(elem[0].toString()) - }); - //delete options if they aren't in getvalues - //otherwise push it in array - //also push in array already selected values - $('#id_tag option').each(function() { - var check = $.inArray($(this), getValues); - if ($(this).is(':selected') ){ - selectedValues.push($(this).val()) - } - if (check == -1){ - $(this).remove() - } - else{ - optionValues.push($(this).val()); - } - }); - //generate new options - //old options unchanged - var html = '' - $.each(data, function(i, elem){ - var check = $.inArray(elem[0].toString(), optionValues); + $('#id_theme').change(function(){ + $.get("/admin/ajax_tag/", {'id': $(this).serialize()}, function(data){ + var optionValues = []; + var getValues = []; + var selectedValues = []; - if (check == -1){ - html += ''; - } - }); - $('#id_tag').append(html); - //select previous selected values - $('#id_tag option').each(function() { - var check = $.inArray($(this).val(), selectedValues) - if (check != -1){ - $(this).attr('selected', 'selected'); - } - }); - });//end get - });//end change - });//end ready + //push values sended from server in array + $.each(data, function(i, elem){ + getValues.push(elem[0].toString()) + }); + + //delete options if they aren't in getvalues + //otherwise push it in array + //also push in array already selected values + $('#id_tag option').each(function() { + var check = $.inArray($(this), getValues); + if ($(this).is(':selected') ){ + selectedValues.push($(this).val()) + } + if (check == -1){ + $(this).remove() + } + else{ + optionValues.push($(this).val()); + } + }); + + //generate new options + //old options unchanged + var html = ''; + $.each(data, function(i, elem){ + var check = $.inArray(elem[0].toString(), optionValues); + + if (check == -1){ + html += ''; + } + }); + + $('#id_tag').append(html); + + //select previous selected values + $('#id_tag option').each(function() { + var check = $.inArray($(this).val(), selectedValues) + if (check != -1){ + $(this).attr('selected', 'selected'); + } + }); + });//end get + });//end change +});//end ready diff --git a/static/mailing_settings/css/main.css b/static/mailing_settings/css/main.css index 3992b5ec..8f7f887d 100644 --- a/static/mailing_settings/css/main.css +++ b/static/mailing_settings/css/main.css @@ -842,35 +842,84 @@ a.themes_trigger{ } .pr-form .pr-row{ - overflow:hidden; + /*overflow:hidden;*/ margin:0 0 14px; } +.pr-form .pr-row:after{ + content: ''; + display: block; + clear: both; +} .pr-input{ - float:left; + display: inline-block; + vertical-align: bottom; + margin:0 0 0 13px; +} + +.errorlist{ + list-style: none; +} + +.text_error{ + display: block; + text-align: center; + font-weight: 300; + font-size: 14px; + color: #bd2626; + padding-bottom: 3px; +} +.pr-input input.field_error{ + box-shadow: 0 0 0 2px #f00; +} +.pr-input:first-child{ + margin:0; +} +.pr-input.pr-name input{ + background:#fff url(../images/pr-icon02.png) no-repeat 210px 50%; +} +.pr-input.pr-email input{ + background:#fff url(../images/pr-icon03.png) no-repeat 210px 50%; +} + +.pr-form input { + border:none; + color:#000; + font:17px/21px 'pf_dindisplay_promedium', Arial, Helvetica, sans-serif; height:46px; - width:186px; + width:247px; padding:0 44px 0 18px; - margin:0 0 0 13px; background:#fff; border-radius:4px; position:relative; + } -.pr-input:first-child{ - margin:0; +.pr-form input:focus::-webkit-input-placeholder { + color:transparent; +} +.pr-form input:focus:-moz-placeholder { + color:transparent; +} +.pr-form input:focus:-ms-input-placeholder { + color:transparent; +} +.pr-form input:focus::-moz-placeholder { + color:transparent; } -.pr-input:after{ - content:''; - position:absolute; - top:13px; - right:14px; - width:20px; - height:20px; +.pr-form input::-webkit-input-placeholder { /* WebKit browsers */ + color:#808080; + opacity:1; } -.pr-input.pr-name:after{ - background:url(../images/pr-icon02.png) no-repeat 50% 50%; +.pr-form input:-moz-placeholder { /* Mozilla Firefox 4 to 18 */ + color:#808080; + opacity:1; } -.pr-input.pr-email:after{ - background:url(../images/pr-icon03.png) no-repeat 50% 50%; +.pr-form input::-moz-placeholder { /* Mozilla Firefox 19+ */ + color:#808080; + opacity:1; +} +.pr-form input:-ms-input-placeholder { /* Internet Explorer 10+ */ + color:#808080; + opacity:1; } .pr-form button{ @@ -890,4 +939,8 @@ a.themes_trigger{ -ms-transition: all 100ms linear; -o-transition: all 100ms linear; transition: all 100ms linear; -} \ No newline at end of file +} + +.error_messages .errorlist{ + list-style: decimal inside; +} diff --git a/static/mailing_settings/js/main.js b/static/mailing_settings/js/main.js index f36d1d54..249e0ca6 100644 --- a/static/mailing_settings/js/main.js +++ b/static/mailing_settings/js/main.js @@ -1,13 +1,52 @@ 'use strict'; -function sendForm () { - var $form = $('#mailing_settings_form'); +function sendForm (show_modal) { + var show_modal = show_modal || false, + $form = $('#mailing_settings_form'); + + $form.find('.field_error').removeClass('field_error'); + $form.find('.text_error').remove(); + + var form_data = $form.serializeArray(); + if (show_modal) { + form_data.push({ + name: 'save', + value: true + }) + } + $.ajax({ url: $form.attr('action'), type: $form.attr('method'), - data: $form.serializeArray(), + data: form_data, success: function(response){ console.log(response); + if (response.hasOwnProperty('redirect_url')){ + window.location.pathname = response.redirect_url; + } + }, + error: function (error) { + var form_errors = error.responseJSON.form_errors; + + if (show_modal){ + var $error_list = $('
    ', {class: 'errorlist'}); + + $.each(form_errors, function (field, err) { + $error_list.append('
  1. ' + err + '
  2. '); + }); + + $('#error_messages').html($error_list); + + $.fancybox.open({ + href: '#error_modal' + }) + } else { + $.each(form_errors, function (field, err) { + var $field = $form.find('#id_' + field); + $field.addClass('field_error'); + $field.parent('.pr-input').prepend('' + err + ''); + }); + } } }); } @@ -123,7 +162,6 @@ function sendForm () { }); } - function isAllTagsChecked (theme_id) { return themes_data[theme_id].tags.every(function (obj) { return obj.checked; @@ -361,6 +399,13 @@ function sendForm () { $themes_modal.on('click', '.modal-approve', function () { var $selected = $selected_themes.find('li').removeClass('unsaved').clone(); + if ($('#pr-promo').length){ + if (!$('#id_moscow').is(':checked') && !$('#id_russia').is(':checked') && $selected_themes.children().length && !$('#selected_themes').children().length){ + $('#id_moscow').prop('checked', true); + $('#id_russia').prop('checked', true); + } + } + $('#selected_themes').html($selected); sendForm(); @@ -371,6 +416,12 @@ function sendForm () { $selected_themes.find('a').trigger('click'); }); + $('#mailing_settings_form button').on('click', function (event) { + event.preventDefault(); + var show_modal = true; + sendForm(show_modal); + }); + })(); diff --git a/templates/c_admin/article/blog_form.html b/templates/c_admin/article/blog_form.html index 127b7c17..9a840ed9 100644 --- a/templates/c_admin/article/blog_form.html +++ b/templates/c_admin/article/blog_form.html @@ -15,17 +15,17 @@ - - @@ -43,6 +43,7 @@

    {% trans "Основная информация" %}

    +
    {# main_title #} @@ -50,35 +51,38 @@
    -
    - {{ form.publish_date }} - {{ form.publish_date.errors }} -
    +
    + {{ form.publish_date }} + {{ form.publish_date.errors }} +
    + {% if not article %} -
    - +
    +
    {{ form.slug }} {{ form.slug.errors }}
    -
    +
    {% endif %} + {# theme #}
    -
    - {{ form.theme }} - {{ form.theme.errors }} -
    +
    + {{ form.theme }} + {{ form.theme.errors }} +
    + {# tag #}
    -
    - {{ form.tag }} - {{ form.tag.errors }} -
    +
    + {{ form.tag }} + {{ form.tag.errors }} +
    {# exposition #} {% if form.exposition %} @@ -160,78 +164,78 @@ {% block bot_scripts %} - + {% endblock %} diff --git a/templates/c_admin/file/file_update.html b/templates/c_admin/file/file_update.html index 5b67905e..3ee8d805 100644 --- a/templates/c_admin/file/file_update.html +++ b/templates/c_admin/file/file_update.html @@ -1,7 +1,13 @@ {% extends 'c_admin/base.html' %} +{% load static %} + +{% block scripts %} + +{% endblock %} {% block body %}
    + {% csrf_token %}
    {% if object %} Изменить {% else %} Добавить {% endif %} файл @@ -10,7 +16,33 @@

    Основная информация

    - {{ form.as_p }} +{# {{ form.as_p }}#} + + {% with field='file_name' form=form languages=languages %} + {% include 'c_admin/forms/multilang.html' %} + {% endwith %} + +
    + +
    {{ form.file_path }} + {{ form.file_path.errors }} +
    +
    + +
    + +
    {{ form.purpose }} + {{ form.purpose.errors }} +
    +
    + + {% with field='description' form=form languages=languages %} + {% include 'c_admin/forms/multilang.html' %} + {% endwith %}
    diff --git a/templates/c_admin/newsletters/contact_list.html b/templates/c_admin/newsletters/contact_list.html index 2b4c5103..6ffa8266 100644 --- a/templates/c_admin/newsletters/contact_list.html +++ b/templates/c_admin/newsletters/contact_list.html @@ -152,8 +152,8 @@
    Экспорт - Создать список рассылки из фильтра - Создать список рассылки + Создать список рассылки из фильтра + Создать список рассылки
    diff --git a/templates/c_admin/newsletters/newsletter_object.html b/templates/c_admin/newsletters/newsletter_object.html index e365bf1e..1f75a93f 100644 --- a/templates/c_admin/newsletters/newsletter_object.html +++ b/templates/c_admin/newsletters/newsletter_object.html @@ -65,6 +65,8 @@ {% trans "Доступные параметры для прехедера" %}:

    {% trans "{name} - имя пользователя" %}

    {% trans "{themes} - темы на которые пользователь подписан (первые 3 + кол-во оставшихся)" %}

    + {% trans "Доступные параметры для контента" %}: +

    {% trans "{name} - имя пользователя" %}

    diff --git a/templates/c_admin/place_exposition/place_exposition.html b/templates/c_admin/place_exposition/place_exposition.html index 59fd8060..6dfd6c08 100644 --- a/templates/c_admin/place_exposition/place_exposition.html +++ b/templates/c_admin/place_exposition/place_exposition.html @@ -16,13 +16,13 @@ {# google map не забыть скачать скрипты на локал #} - +{# #} {# selects #} - - - +{# #} +{# #} +{# #} {# ajax #} diff --git a/templates/client/newsletters/mailing_settings.html b/templates/client/newsletters/mailing_settings.html index 87e96a70..60e1929c 100644 --- a/templates/client/newsletters/mailing_settings.html +++ b/templates/client/newsletters/mailing_settings.html @@ -10,7 +10,6 @@ - @@ -20,16 +19,16 @@ {% else %}
    -
    +
    +7 (499) 999-12-07
      @@ -41,13 +40,215 @@
    - + {% blocktrans %}Писковик деловых событий{% endblocktrans %}
    {% endif %} -

    Страница в разработке

    + + {% csrf_token %} + + {% if not user.is_authenticated %} +
    +
    +

    {% trans 'Анонсы выставок' %}
    {% trans 'и конференций на ваш e-mail' %}

    +

    {% trans 'Хотите быть в курсе событий?' %}

    +
    +

    {% trans 'Получайте анонсы выставок и конференций на email каждую среду. Вы можете выбрать несколько интересующих вас тематических направлений.' %} {% trans 'Пример письма' %}

    +
    +
    +
    +
    + + {% if form.first_name.errors %}{{ form.first_name.errors }}{% endif %} + {{ form.first_name }} + + + + {% if form.email.errors %}{{ form.email.errors }}{% endif %} + {{ form.email }} + +
    + +
    +
    +
    +
    + {% endif %} + +
    +
    +

    {% trans 'Какие события включать в ваше письмо?' %}

    +
    +
    +

    {% trans 'Ваши темы:' %}

    +
      + {% for theme in object.themes.all %} +
    • + + {{ theme }} + × +
    • + {% endfor %} + {% for tag in object.tags.all %} +
    • + + {{ tag }} + × +
    • + {% endfor %} +
    + {% trans 'Уточнить темы' %} +
    +
    +

    {% trans 'Ваши гео-фильтры:' %}

    + +
    +
    +
    +
    +
    +
    +

    {% trans 'Включать ли новости / обзоры / статьи в письмо?' %}

    +
    +
    + {{ form.content_news }} + {{ form.content_news.label_tag }} +

    {% trans "Получайте новости выставок и конференций по выбранным тематикам" %}

    +
    +
    + {{ form.content_overview }} + {{ form.content_overview.label_tag }} +

    {% trans "Практические материалы, интервью, кейсы, которые помогут эффективно участвовать в выставках" %}

    +
    +
    + {{ form.content_articles }} + {{ form.content_articles.label_tag }} +

    {% trans "Блог о том, как создавать и продвигать крутые event`ы" %}

    +
    +
    +
    +
    +
    +
    +

    {% trans 'Регулярность получения писем' %}

    +
    +
    +
      + {% for field in form.periodic %} +
    • + +
    • + {% endfor %} +
    +
    +
    + {% for field in form.periodic_day %} + + {% endfor %} +
    +
    +
    +
    +
    +
    + {% if not user.is_authenticated %} +
    + {% trans 'Нажимая «Подписаться», вы соглашаетесь получать' %}
    {% trans 'материалы компании Expomap на свой электронный адрес' %}
    + {% trans "Пользовательское соглашение" %} +
    + {% endif %} + + + {% if user.is_authenticated %} + {% trans 'Не хочу быть в курсе событий (отписаться от всего)' %} + {% endif %} +
    +
    + + +
    +
    + {% include 'client/popups/new_themes.html' %} +
    +
    + {% include 'client/popups/russia_cities.html' %} +
    +
    + {% include 'client/popups/mailing_settings_countries.html' %} +
    + {% if unsubscribe_success or unsubscribed %} +
    + {% include 'client/popups/unsubscribed.html' %} +
    + {% endif %} + +
    + +
    +
    diff --git a/templates/client/service/participation.html b/templates/client/service/participation.html index acf7965c..fb7a2cfd 100644 --- a/templates/client/service/participation.html +++ b/templates/client/service/participation.html @@ -1,302 +1,301 @@ -{% extends 'base_catalog.html' %} -{% load static %} -{% load i18n %} -{% load template_filters %} - -{% block bread_scrumbs %} - -{% endblock %} - -{% block page_title %} -
    -

    {% if meta %}{{ meta.h1 }}{% else %}{% trans 'Участие в выставке' %}{% if object %} {{ object.name }} {% endif %}{% endif %}

    -
    -{% endblock %} - -{% block page_body %} -
    - -
    - -
    - -
    - -
    -

    {% trans 'Увеличим эффективность Вашего участия в выставке' %}{% if object %} {{ object.name }} {% endif %} {% trans 'как минимум на 50%' %}

    - -
    -
      -
    • {% trans 'Мы поможем превратить посетителей стенда в клиентов' %}
    • -
    • {% trans 'Мы удержим Вас от пустых трат' %}
    • -
    • {% trans 'Мы посчитаем отдачу и покажем результаты в цифрах' %}
    • -
    - -
      -
    • {% trans 'Мы начнем привлекать Вам клиентов еще до выставки' %}
    • -
    • {% trans 'Мы знаем тонкости и узкие места организации' %}
    • -
    -
    - -
    - -
    - -
    -
    {% csrf_token %} - -
    - -
    - -
    {% trans 'Информация об экспоместе' %}
    - -
    -
    - - {{ form.area }} -
    - {{ form.area.errors }} - -
    -
    - -
    - - {{ form.area_type }} -
    - {{ form.area_type.errors }} - -
    -
    -
    - -
    -
    - {{ form.company_inf }} -
    -
    - -
    - -
    - -
    - -
    {% trans 'Ваши контактные данные' %}
    - {% if not object %} -
    -
    - {{ form.event }} -
    - {{ form.event.errors }} - -
    -
    -
    - {% endif %} - -
    -
    - {{ form.person_inf }} -
    - {{ form.person_inf.errors }} - -
    -
    -
    - -
    -
    - {{ form.country }} -
    - {{ form.country.errors }} - -
    -
    - -
    - {{ form.city }} -
    - {{ form.city.errors }} - -
    -
    -
    - -
    -
    - {{ form.phone }} -
    - {{ form.phone.errors }} - -
    -
    - -
    - {{ form.person }} -
    - {{ form.person.errors }} - -
    -
    -
    - -
    - -
    - -
    - {% if service.price %} - - {% endif %} - - {% if service.price %} - - {% endif %} - - - -
    - -
    -
    - - - -
    -
    - {% if object %} -

    - {% trans 'Укажите в запросе исходную информацию о Ваших целях и задачах, и мы подберем' %} {{ object.name }} - {% trans 'которая будет им соответствовать. Далее мы свяжемся с организаторами, чтобы уточнить наличие свободных площадей и цены, и вместе с Вами начнем создавать концепцию Вашего участия." На "Укажите в запросе исходную информацию о Ваших целях и задачах, и мы проанализируем, насколько' %} {{ object.name }} - {% trans 'им соответствует. Далее мы свяжемся с организаторами, чтобы уточнить наличие свободных площадей и цены, и вместе с Вами начнем создавать концепцию Вашего участия.' %} -

    - {% else %} -

    - {% trans 'Укажите в запросе исходную информацию о Ваших целях и задачах, и мы подберем выставку которая будет им соответствовать. Далее мы свяжемся с организаторами, чтобы уточнить наличие свободных площадей и цены, и вместе с Вами начнем создавать концепцию Вашего участия.'%} -

    - {% endif %} -
    - - {% if messages %} -
    -
      - {% for message in messages %} - - {{ message }} - - {% endfor %} -
    -
    - {% endif %} - -
    - - - -
    -
    -

    {% trans 'Отзывы клиентов' %}:

    -
    - -
    -
    - - -
    -
    -
    -
    -
    - -
    -
    - -
    {% trans "Волкова Елизавета" %}
    - -
    - {% trans "Хочу поблагодарить команду маркетологов Expomap за организацию нашего участия в выставке ТрансРоссия. Для нас это было чем-то новым, и благодаря опыту ребят мы избежали большого количества проблем и решали все возникающие вопросы очень оперативно. Если говорить о результатах работы на выставке, то мы собрали за 4 дня более 300 рабочих контактов и запросов, часть из которых выглядят очень перспективно. Мы сделали это вместе с Expomap и хотим сказать спасибо за их идеи, глубокое погружение в наши задачи, креативный подход и четкую помощь в реализации! Будем рады работать с вами и в других проектах!" %} -
    -
    - -
    -
    - -
    -
    - - -
    -
    -
    - - -
    Anatolios Spyrlidis
    - -
    - {% trans "Мы принимали участие со своим стендом в выставке Boot Duesseldorf в Германии в январе 2014. Выражаем благодарность сотрудникам Expomap, а также персональному консультанту Руслану Шапилову за оперативность, мы остались довольны качеством оказанных услуг!" %} - -
    -
    - -
    -
    - -
    -
    - - - {% if object %} -
    - {% include 'client/includes/booking_block.html' with city=object.city place=object.place event=object %} -
    - {% endif %} -
    - - -
    -{% endblock %} +{% extends 'base_catalog.html' %} +{% load static %} +{% load i18n %} +{% load template_filters %} + +{% block bread_scrumbs %} + +{% endblock %} + +{% block page_title %} +
    +

    {% if meta %}{{ meta.h1 }}{% else %}{% trans 'Участие в выставке' %}{% if object %} {{ object.name }} {% endif %}{% endif %}

    +
    +{% endblock %} + +{% block page_body %} +
    + +
    + +
    + +
    + +
    +

    {% trans 'Увеличим эффективность Вашего участия в выставке' %}{% if object %} {{ object.name }} {% endif %} {% trans 'как минимум на 50%' %}

    + +
    +
      +
    • {% trans 'Мы поможем превратить посетителей стенда в клиентов' %}
    • +
    • {% trans 'Мы удержим Вас от пустых трат' %}
    • +
    • {% trans 'Мы посчитаем отдачу и покажем результаты в цифрах' %}
    • +
    + +
      +
    • {% trans 'Мы начнем привлекать Вам клиентов еще до выставки' %}
    • +
    • {% trans 'Мы знаем тонкости и узкие места организации' %}
    • +
    +
    + +
    + +
    + +
    +
    {% csrf_token %} + +
    + +
    + +
    {% trans 'Информация об экспоместе' %}
    + +
    +
    + + {{ form.area }} +
    + {{ form.area.errors }} + +
    +
    + +
    + + {{ form.area_type }} +
    + {{ form.area_type.errors }} + +
    +
    +
    + +
    +
    + {{ form.company_inf }} +
    +
    + +
    + +
    + +
    + +
    {% trans 'Ваши контактные данные' %}
    + {% if not object %} +
    +
    + {{ form.event }} +
    + {{ form.event.errors }} + +
    +
    +
    + {% endif %} + +
    +
    + {{ form.person_inf }} +
    + {{ form.person_inf.errors }} + +
    +
    +
    + +
    +
    + {{ form.country }} +
    + {{ form.country.errors }} + +
    +
    + +
    + {{ form.city }} +
    + {{ form.city.errors }} + +
    +
    +
    + +
    +
    + {{ form.phone }} +
    + {{ form.phone.errors }} + +
    +
    + +
    + {{ form.person }} +
    + {{ form.person.errors }} + +
    +
    +
    + +
    + +
    + +
    + {% if service.price %} + + {% endif %} + + {% if service.price %} + + {% endif %} + + + +
    + +
    +
    + + + +
    +
    + {% if object %} +

    + {% trans 'Укажите в запросе исходную информацию о ваших целях и задачах, и мы проанализируем, насколько' %} {{ object.name }} + {% trans 'им соответствует. Далее мы свяжемся с организаторами, чтобы уточнить наличие свободных площадей и цены, и вместе с вами начнем создавать концепцию вашего участия.' %} +

    + {% else %} +

    + {% trans 'Укажите в запросе исходную информацию о ваших целях и задачах, и мы проанализируем, насколько событие им соответствует. Далее мы свяжемся с организаторами, чтобы уточнить наличие свободных площадей и цены, и вместе с вами начнем создавать концепцию вашего участия.' %} +

    + {% endif %} +
    + + {% if messages %} +
    +
      + {% for message in messages %} + + {{ message }} + + {% endfor %} +
    +
    + {% endif %} + +
    + + + +
    +
    +

    {% trans 'Отзывы клиентов' %}:

    +
    + +
    +
    + + +
    +
    +
    +
    +
    + +
    +
    + +
    {% trans "Волкова Елизавета" %}
    + +
    + {% trans "Хочу поблагодарить команду маркетологов Expomap за организацию нашего участия в выставке ТрансРоссия. Для нас это было чем-то новым, и благодаря опыту ребят мы избежали большого количества проблем и решали все возникающие вопросы очень оперативно. Если говорить о результатах работы на выставке, то мы собрали за 4 дня более 300 рабочих контактов и запросов, часть из которых выглядят очень перспективно. Мы сделали это вместе с Expomap и хотим сказать спасибо за их идеи, глубокое погружение в наши задачи, креативный подход и четкую помощь в реализации! Будем рады работать с вами и в других проектах!" %} +
    +
    + +
    +
    + +
    +
    + + +
    +
    +
    + + +
    Anatolios Spyrlidis
    + +
    + {% trans "Мы принимали участие со своим стендом в выставке Boot Duesseldorf в Германии в январе 2014. Выражаем благодарность сотрудникам Expomap, а также персональному консультанту Руслану Шапилову за оперативность, мы остались довольны качеством оказанных услуг!" %} + +
    +
    + +
    +
    + +
    +
    + + + {% if object %} +
    + {% include 'client/includes/booking_block.html' with city=object.city place=object.place event=object %} +
    + {% endif %} +
    + + +
    +{% endblock %}