newsletter announces

remotes/origin/1203
Nazar Kotjuk 10 years ago
parent 9963687752
commit 17834aa692
  1. 2
      article/admin.py
  2. 2
      article/forms.py
  3. 31
      country/management/commands/newsletter_create_dependies.py
  4. 2
      emencia/django/newsletter/admin_forms.py
  5. 4
      emencia/django/newsletter/admin_urls.py
  6. 132
      emencia/django/newsletter/mailer.py
  7. 18
      emencia/django/newsletter/management/commands/newsletter_create_announce.py
  8. 74
      emencia/django/newsletter/models.py
  9. 2
      emencia/django/newsletter/settings.py
  10. 176
      emencia/django/newsletter/templates/newsletter/announce_template.html
  11. 16
      emencia/django/newsletter/templates/newsletter/newsletter_link_unsubscribe.html
  12. 2
      emencia/django/newsletter/urls/__init__.py
  13. 32
      emencia/django/newsletter/views/admin_views.py
  14. 1
      templates/admin/article/blog_form.html
  15. 2
      templates/admin/newsletters/newsletter_list.html

@ -231,7 +231,7 @@ class NewsList(ListView):
class NewsView(BlogView): class NewsView(BlogView):
form_class = NewsForm form_class = NewsForm
template_name = 'article/blog_form.html' template_name = 'admin/article/blog_form.html'
success_url = '/admin/article/news/all/' success_url = '/admin/article/news/all/'
obj = None obj = None

@ -21,7 +21,7 @@ class BlogForm(forms.Form):
type = Article.blog type = Article.blog
theme = forms.ModelMultipleChoiceField(label='Тематики', queryset=ThemeBlog.objects.all(), required=False, theme = forms.ModelMultipleChoiceField(label='Тематики', queryset=ThemeBlog.objects.all(), required=False,
widget=forms.SelectMultiple(attrs={'style':'width: 550px'})) widget=forms.SelectMultiple(attrs={'style':'width: 550px'}))
slug = forms.SlugField(label=u'URL', max_length=255, min_length=1) slug = forms.SlugField(label=u'URL', max_length=255, min_length=1, required=False)
publish_date = forms.DateField(label=u'Дата публикации', input_formats=['%Y-%m-%d', '%d.%m.%Y'], required=False) publish_date = forms.DateField(label=u'Дата публикации', input_formats=['%Y-%m-%d', '%d.%m.%Y'], required=False)
tag = forms.CharField(label=u'Теги', widget=forms.HiddenInput(), required=False) tag = forms.CharField(label=u'Теги', widget=forms.HiddenInput(), required=False)
logo = forms.ImageField(label=u'Лого', required=False) logo = forms.ImageField(label=u'Лого', required=False)

@ -0,0 +1,31 @@
# create default test newsletter for announce with attachments
"""Command for sending the newsletter"""
from django.conf import settings
from django.utils.translation import activate
from django.core.management.base import NoArgsCommand
from emencia.django.newsletter.mailer import Mailer
from emencia.django.newsletter.models import Newsletter, MailingList, SMTPServer, Contact
class Command(NoArgsCommand):
"""Send the newsletter in queue"""
help = 'create the announce every week'
def handle(self, *args, **options):
# creating smtp server
try:
server = SMTPServer.objects.get(id=1)
except SMTPServer.DoesNotExist:
server = SMTPServer.objects.create(id=1, name='default', host='smtp.gmail.com', user='kotzillla',
password='fitteR2006!!', port=587, tls=True, mails_hour=1000)
# mailinglist for announce
try:
mail_list = MailingList.objects.get(id=1)
except SMTPServer.DoesNotExist:
mail_list = MailingList.objects.create(id=1, name=u'Анонсы', announce=True)
# add subscribers
mail_list.subscribers.add(Contact.objects.filter(contactsettings__theme__isnull=False))

@ -58,8 +58,6 @@ class NewsletterForm(forms.ModelForm):
fields = ('title', 'content', 'mailing_list', 'test_contacts', 'header_sender', fields = ('title', 'content', 'mailing_list', 'test_contacts', 'header_sender',
'header_reply', 'status', 'sending_date', 'slug') 'header_reply', 'status', 'sending_date', 'slug')
def clean_test_contacts(self):
return []
class AttachmentForm(forms.ModelForm): class AttachmentForm(forms.ModelForm):
class Meta: class Meta:

@ -9,8 +9,12 @@ from emencia.django.newsletter.views.admin_views import ContactList, UpdateConta
urlpatterns = patterns('', urlpatterns = patterns('',
url(r'^newsletters/all/$', NewsletterListView.as_view(), name='newsletters_newsletters_list'), url(r'^newsletters/all/$', NewsletterListView.as_view(), name='newsletters_newsletters_list'),
url(r'^newsletters/(?P<pk>\d+)/edit/', NewsletterUpdate.as_view(), name='newsletters_newsletters_update'), url(r'^newsletters/(?P<pk>\d+)/edit/', NewsletterUpdate.as_view(), name='newsletters_newsletters_update'),
url(r'^newsletters/(?P<pk>\d+)/test/',
'emencia.django.newsletter.views.admin_views.send_test_newsletter',
name='newsletters_newsletters_send_test'),
url(r'^newsletters/', NewsletterCreate.as_view(), name='newsletters_newsletters_create'), url(r'^newsletters/', NewsletterCreate.as_view(), name='newsletters_newsletters_create'),
url(r'^mailinglist/all/$', MailingListView.as_view(), name='newsletters_mailinglist'), url(r'^mailinglist/all/$', MailingListView.as_view(), name='newsletters_mailinglist'),
url(r'^mailinglist/(?P<pk>\d+)/edit/', UpdateMailingList.as_view(), name='newsletters_mailinglist_update'), url(r'^mailinglist/(?P<pk>\d+)/edit/', UpdateMailingList.as_view(), name='newsletters_mailinglist_update'),
url(r'^mailinglist/', CreateMailingList.as_view(), name='newsletters_mailinglist_create'), url(r'^mailinglist/', CreateMailingList.as_view(), name='newsletters_mailinglist_create'),

@ -30,7 +30,7 @@ from email import message_from_file
from html2text import html2text as html2text_orig from html2text import html2text as html2text_orig
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.template import Context, Template from django.template import Context, Template
from django.template.loader import render_to_string from django.template.loader import render_to_string, get_template
from django.utils.encoding import smart_str from django.utils.encoding import smart_str
from django.utils.encoding import smart_unicode from django.utils.encoding import smart_unicode
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
@ -71,17 +71,7 @@ def html2text(html):
https://github.com/aaronsw/html2text/issues/#issue/7 is not fixed""" https://github.com/aaronsw/html2text/issues/#issue/7 is not fixed"""
txt = html2text_orig(html) txt = html2text_orig(html)
return txt return txt
"""
links = list(LINK_RE.finditer(txt))
out = StringIO()
pos = 0
for l in links:
out.write(txt[pos:l.start()])
out.write(l.group().replace('\n', ''))
pos = l.end()
out.write(txt[pos:])
return out.getvalue()
"""
def encodestring(instring, tabs=0): def encodestring(instring, tabs=0):
outfile = StringIO() outfile = StringIO()
@ -101,14 +91,18 @@ class NewsLetterSender(object):
self.newsletter = newsletter self.newsletter = newsletter
self.newsletter_template = Template(self.newsletter.content) self.newsletter_template = Template(self.newsletter.content)
self.title_template = Template(self.newsletter.title) self.title_template = Template(self.newsletter.title)
#
self.announce = self.newsletter.mailing_list.announce
def build_message(self, contact): def build_message(self, contact, announce_context=None):
""" """
Build the email as a multipart message containing Build the email as a multipart message containing
a multipart alternative for text (plain, HTML) plus a multipart alternative for text (plain, HTML) plus
all the attached files. all the attached files.
""" """
content_html = self.build_email_content(contact)
content_html = self.build_email_content(contact, announce_context)
h = HTMLParser.HTMLParser() h = HTMLParser.HTMLParser()
content_html = h.unescape(content_html) content_html = h.unescape(content_html)
content_text = html2text(content_html) content_text = html2text(content_html)
@ -139,6 +133,12 @@ class NewsLetterSender(object):
for attachment in self.attachments: for attachment in self.attachments:
message.attach(attachment) message.attach(attachment)
if announce_context:
# add announce attachments
announce_attachments = self.build_announce_attachments(announce_context)
for attachment in announce_attachments:
message.attach(attachment)
for header, value in self.newsletter.server.custom_headers.items(): for header, value in self.newsletter.server.custom_headers.items():
message[header] = value message[header] = value
@ -147,8 +147,6 @@ class NewsLetterSender(object):
message['List-Unsubscribe'] = '<' + unsubscribe_link + '>' message['List-Unsubscribe'] = '<' + unsubscribe_link + '>'
message['List-Id'] = str(self.newsletter.id) message['List-Id'] = str(self.newsletter.id)
return message return message
def build_attachments(self): def build_attachments(self):
@ -185,6 +183,61 @@ class NewsLetterSender(object):
return attachments return attachments
def build_announce_attachments(self, context):
# todo: move hardcoded prefixes to setting
events = context['events']
news = context.get('news')
blogs = context.get('blogs')
attachments = []
for event in events:
message_attachment = self.gen_attachment_logo(event, prefix='mail_expo_logo_')
if message_attachment:
attachments.append(message_attachment)
if news:
for item in news:
message_attachment = self.gen_attachment_logo(item, prefix='mail_news_logo_')
if message_attachment:
attachments.append(message_attachment)
if blogs:
for item in blogs:
message_attachment = self.gen_attachment_logo(item, prefix='mail_blogs_logo_')
if message_attachment:
attachments.append(message_attachment)
return attachments
def gen_attachment_logo(self, obj, prefix='logo_'):
logo = getattr(obj, 'logo')
if not logo:
return None
ctype, encoding = mimetypes.guess_type(logo.path)
if ctype is None or encoding is not None:
ctype = 'application/octet-stream'
maintype, subtype = ctype.split('/', 1)
try:
fd = open(logo.path, 'rb')
except IOError:
return None
if maintype == 'image':
message_attachment = MIMEImage(fd.read(), _subtype=subtype)
else:
message_attachment = MIMEBase(maintype, subtype)
message_attachment.set_payload(fd.read())
encode_base64(message_attachment)
fd.close()
cid = prefix + '%d'%obj.id
message_attachment.add_header('Content-ID', '<%s>'%cid)
return message_attachment
def build_title_content(self, contact): def build_title_content(self, contact):
"""Generate the email title for a contact""" """Generate the email title for a contact"""
context = Context({'contact': contact, context = Context({'contact': contact,
@ -193,7 +246,7 @@ class NewsLetterSender(object):
title = self.title_template.render(context) title = self.title_template.render(context)
return title return title
def build_email_content(self, contact): def build_email_content(self, contact, announce_context=None):
"""Generate the mail for a contact""" """Generate the mail for a contact"""
uidb36, token = tokenize(contact) uidb36, token = tokenize(contact)
context = Context({'contact': contact, context = Context({'contact': contact,
@ -201,13 +254,19 @@ class NewsLetterSender(object):
'newsletter': self.newsletter, 'newsletter': self.newsletter,
'tracking_image_format': TRACKING_IMAGE_FORMAT, 'tracking_image_format': TRACKING_IMAGE_FORMAT,
'uidb36': uidb36, 'token': token}) 'uidb36': uidb36, 'token': token})
if self.announce:
content = self.newsletter_template.render(context) # render template by default announce template
template = get_template('newsletter/announce_template.html')
context.update(announce_context)
content = template.render(context)
else:
content = self.newsletter_template.render(context)
if TRACKING_LINKS: if TRACKING_LINKS:
content = track_links(content, context) content = track_links(content, context)
link_site = render_to_string('newsletter/newsletter_link_site.html', context) # uncomment if wanna include this link
content = body_insertion(content, link_site) #link_site = render_to_string('newsletter/newsletter_link_site.html', context)
#content = body_insertion(content, link_site)
if INCLUDE_UNSUBSCRIPTION: if INCLUDE_UNSUBSCRIPTION:
@ -217,9 +276,7 @@ class NewsLetterSender(object):
if TRACKING_IMAGE: if TRACKING_IMAGE:
image_tracking = render_to_string('newsletter/newsletter_image_tracking.html', context) image_tracking = render_to_string('newsletter/newsletter_image_tracking.html', context)
content = body_insertion(content, image_tracking, end=True) content = body_insertion(content, image_tracking, end=True)
#return content
#return encodestring(content)
return smart_unicode(content) return smart_unicode(content)
def update_newsletter_status(self): def update_newsletter_status(self):
@ -258,8 +315,10 @@ class NewsLetterSender(object):
expedition_list = self.newsletter.mailing_list.expedition_set().exclude(id__in=already_sent) expedition_list = self.newsletter.mailing_list.expedition_set().exclude(id__in=already_sent)
return expedition_list return expedition_list
def update_contact_status(self, contact, exception): def update_contact_status(self, contact, exception, send):
if exception is None: if not send:
status = ContactMailingStatus.ANNOUNCE_NO_DATA
elif exception is None:
status = (self.test status = (self.test
and ContactMailingStatus.SENT_TEST and ContactMailingStatus.SENT_TEST
or ContactMailingStatus.SENT) or ContactMailingStatus.SENT)
@ -299,21 +358,30 @@ class Mailer(NewsLetterSender):
i = 1 i = 1
for contact in expedition_list: for contact in expedition_list:
send = True
if self.verbose: if self.verbose:
print '- Processing %s/%s (%s)' % ( print '- Processing %s/%s (%s)' % (
i, number_of_recipients, contact.pk) i, number_of_recipients, contact.pk)
message = self.build_message(contact)
self.smtp.sendmail(self.newsletter.header_sender,
contact.email,
message.as_string())
try:
pass
if self.announce:
# check if events for this newsletter exists
announce_context = contact.get_announce_context()
if not announce_context:
send = False
else:
announce_context = None
try:
if send:
message = self.build_message(contact, announce_context)
self.smtp.sendmail(self.newsletter.header_sender,
contact.email,
message.as_string())
except Exception, e: except Exception, e:
exception = e exception = e
else: else:
exception = None exception = None
self.update_contact_status(contact, exception)
self.update_contact_status(contact, exception, send)
if SLEEP_BETWEEN_SENDING: if SLEEP_BETWEEN_SENDING:
time.sleep(SLEEP_BETWEEN_SENDING) time.sleep(SLEEP_BETWEEN_SENDING)

@ -0,0 +1,18 @@
"""Command for sending the newsletter"""
from datetime import date, timedelta
from django.conf import settings
from django.utils.translation import activate
from django.core.management.base import NoArgsCommand
from emencia.django.newsletter.mailer import Mailer
from emencia.django.newsletter.models import Newsletter, MailingList
class Command(NoArgsCommand):
"""this command run every day. check date and creates newsletter the day before announces need to send"""
help = 'create the announce every week.'
def handle(self, *args, **options):
announce_list = MailingList.objects.get(id=1)
day = date.today()# + timedelta(days=1)
announce_list.generate_announce_newsletter(day)

@ -26,7 +26,7 @@ from emencia.django.newsletter.settings import BASE_PATH
from emencia.django.newsletter.settings import MAILER_HARD_LIMIT from emencia.django.newsletter.settings import MAILER_HARD_LIMIT
from emencia.django.newsletter.settings import DEFAULT_HEADER_REPLY from emencia.django.newsletter.settings import DEFAULT_HEADER_REPLY
from emencia.django.newsletter.settings import DEFAULT_HEADER_SENDER from emencia.django.newsletter.settings import DEFAULT_HEADER_SENDER
from emencia.django.newsletter.settings import ACTIVATION_SUBJECT, ACTIVATION_TEMPLATE from emencia.django.newsletter.settings import ACTIVATION_SUBJECT, ACTIVATION_TEMPLATE, DEFAULT_SMPTSERVER_ID
from emencia.django.newsletter.utils.vcard import vcard_contact_export from emencia.django.newsletter.utils.vcard import vcard_contact_export
from emencia.django.newsletter.utils import make_activation_code from emencia.django.newsletter.utils import make_activation_code
@ -152,6 +152,41 @@ class Contact(models.Model):
return self.content_object.get_absolute_url() return self.content_object.get_absolute_url()
return reverse('admin:newsletter_contact_change', args=(self.pk,)) return reverse('admin:newsletter_contact_change', args=(self.pk,))
def get_announce_context(self):
from exposition.models import Exposition
from article.models import Article
from django.utils.translation import activate
activate('ru')
events = Exposition.enable.upcoming()
themes = list(self.contactsettings.theme.all())
try:
country = self.contactsettings.country.all()[0]
except IndexError:
country = None
if themes:
theme_ids = [item.id for item in themes]
events = events.filter(theme__in=theme_ids)
else:
return None
if country:
events = events.filter(country=country)
events = list(events[:3])
if events:
context = {'themes': themes, 'place': country, 'events': events}
else:
# no events for announce
return None
news = list(Article.objects.news().filter(publish_date__gt=datetime.now()-timedelta(days=7))[:3])
if news:
context['news'] = news
blogs = list(Article.objects.blogs().filter(publish_date__gt=datetime.now()-timedelta(days=7))[:3])
if blogs:
context['blogs'] = blogs
return context
def __unicode__(self): def __unicode__(self):
if self.first_name and self.last_name: if self.first_name and self.last_name:
contact_name = '%s %s' % (self.last_name, self.first_name) contact_name = '%s %s' % (self.last_name, self.first_name)
@ -246,6 +281,39 @@ class MailingList(models.Model):
def __unicode__(self): def __unicode__(self):
return self.name return self.name
def check_day(self, day):
weekday = day.weekday()
if self.announce_public_day > 0:
return (self.announce_public_day - 1) == weekday
else:
return weekday == 6
def generate_announce(self, day):
# simple data
sending_date = day + timedelta(days=1)
start_sending_time = datetime.combine(sending_date, datetime.min.time())
title = u'announce_%s'%sending_date.strftime('%Y_%m_%y')
newsletter = Newsletter(status=Newsletter.WAITING, sending_date=start_sending_time,
title=title, mailing_list=self, slug=title)
newsletter.save()
# add default attacments copied from default announce newsletter
default_newsletter = Newsletter.objects.get(id=1)
attacments = list(default_newsletter.attachment_set.all())
for a in attacments:
a.id = None
a.newsletter = newsletter
a.save()
return newsletter
def generate_announce_newsletter(self, day):
if self.check_day(day):
return self.generate_announce(day)
return None
class Meta: class Meta:
ordering = ('-creation_date',) ordering = ('-creation_date',)
verbose_name = _('mailing list') verbose_name = _('mailing list')
@ -278,7 +346,7 @@ class Newsletter(models.Model):
blank=True, null=True) blank=True, null=True)
server = models.ForeignKey(SMTPServer, verbose_name=_('smtp server'), server = models.ForeignKey(SMTPServer, verbose_name=_('smtp server'),
default=lambda: SMTPServer.objects.get(id=1)) default=lambda: SMTPServer.objects.get(id=DEFAULT_SMPTSERVER_ID))
header_sender = models.CharField(_('sender'), max_length=255, header_sender = models.CharField(_('sender'), max_length=255,
default=DEFAULT_HEADER_SENDER) default=DEFAULT_HEADER_SENDER)
header_reply = models.CharField(_('reply to'), max_length=255, header_reply = models.CharField(_('reply to'), max_length=255,
@ -376,6 +444,7 @@ class ContactMailingStatus(models.Model):
OPENED_ON_SITE = 5 OPENED_ON_SITE = 5
LINK_OPENED = 6 LINK_OPENED = 6
UNSUBSCRIPTION = 7 UNSUBSCRIPTION = 7
ANNOUNCE_NO_DATA = 8
STATUS_CHOICES = ((SENT_TEST, _('sent in test')), STATUS_CHOICES = ((SENT_TEST, _('sent in test')),
(SENT, _('sent')), (SENT, _('sent')),
@ -385,6 +454,7 @@ class ContactMailingStatus(models.Model):
(OPENED_ON_SITE, _('opened on site')), (OPENED_ON_SITE, _('opened on site')),
(LINK_OPENED, _('link opened')), (LINK_OPENED, _('link opened')),
(UNSUBSCRIPTION, _('unsubscription')), (UNSUBSCRIPTION, _('unsubscription')),
(ANNOUNCE_NO_DATA, _('announce no data')),
) )
newsletter = models.ForeignKey(Newsletter, verbose_name=_('newsletter')) newsletter = models.ForeignKey(Newsletter, verbose_name=_('newsletter'))

@ -41,3 +41,5 @@ BASE_PATH = getattr(settings, 'NEWSLETTER_BASE_PATH', 'uploads/newsletter')
ACTIVATION_SUBJECT = 'client/newsletters/activation_subject.txt' ACTIVATION_SUBJECT = 'client/newsletters/activation_subject.txt'
ACTIVATION_TEMPLATE = 'client/newsletters/activation_template.html' ACTIVATION_TEMPLATE = 'client/newsletters/activation_template.html'
DEFAULT_SMPTSERVER_ID = 1

@ -0,0 +1,176 @@
<body style="margin: 0; padding: 0; min-height: 100%; background: #f4f2ee;">
<table cellpadding="0" cellspacing="0" border="0" width="100%" height="100%" bgcolor="#f4f2ee" style="font-family: Arial, sans-serif; background: #f4f2ee;">
<tr>
<td align="center" style="padding: 50px 0">
{# header #}
<table cellpadding="0" cellspacing="0" border="0" width="600" style="width: 600px;">
<tr>
<td style="vertical-align: top;">
<div class="logo">
<a style="text-decoration: none; color: #a2a2a2; font-size: 12px;" href="/">
<img src="cid:logo" alt="Expomap.ru" />
<b style="display: block; padding-left: 67px; margin-top: -5px;">Выставки, конференции, семинары</b>
</a>
</div>
</td>
<td style="vertical-align: top; padding-top: 22px;">
<ul class="t-links" style="margin: 0 0 15px; padding: 0; list-style: none; text-align: right; font-size: 16px; line-height: 17px; font-weight: bold;">
<li style="display: inline-block;"><a style="text-decoration: none; color: #ff6600" href="/expo/">СОБЫТИЯ</a></li>
<li style="display: inline-block; margin-left: 20px;"><a style="text-decoration: none; color: #ff6600" href="/places/">МЕСТА</a></li>
<li style="display: inline-block; margin-left: 20px;"><a style="text-decoration: none; color: #ff6600" href="/members/">УЧАСТНИКИ</a></li>
</ul>
<ul class="soc-media-buttons" style="margin: 0; padding: 0; list-style: none; text-align: right;">
<li style="display: inline-block; margin-left: 5px;"><a href="https://www.facebook.com/Expomap"><img src="cid:media_fb" title="Facebook" alt="Facebook" /></a></li>
<li style="display: inline-block; margin-left: 5px;"><a href="http://www.linkedin.com/company/expomap-ru/"><img src="cid:media_lin" title="LinkedIn" alt="LinkedIn" /></a></li>
<li style="display: inline-block; margin-left: 5px;"><a href="http://vk.com/expomap"><img src="cid:media_vk" title="В контакте" alt="В контакте" /></a></li>
<li style="display: inline-block; margin-left: 5px;"><a href="https://twitter.com/expomap_ru"><img src="cid:media_twit" title="Twitter" alt="Twitter" /></a></li>
</ul>
</td>
</tr>
</table>
{# end header #}
{# events #}
{% with events=events %}
<table cellpadding="0" cellspacing="0" border="0" width="600" style="width: 600px; margin-bottom: 10px;">
<tr>
<td style="padding: 20px 0 0;"><a style="display: block; padding: 25px 30px; text-decoration: none; background: #ff6600; color: #ffffff; font-size: 20px; line-height: 26px;" href="/expo/search/?{% for th in themes %}th={{ th.id }}&{% endfor %}{% if place %}co={{ place.id }}&{% endif %}">Выставки{% if place %} {{ place.inflect }}{% endif %} {% if themes|length == 1 %}по тематике:<b style="display: block; font-size: 26px;">{{ themes.0.name }}</b>{% endif %}</a></td>
</tr>
<tr>
<td style="padding: 10px 30px 15px; background: #faf9f7;">
<table cellpadding="0" cellspacing="0" border="0" width="540" style="margin-bottom: 15px;">
{% for event in events %}
<tr valign="top">
<td style="padding: 15px 15px 15px 0; width: 76px; border-bottom: 1px dotted #cccccc;">
<table cellpadding="0" cellspacing="0" border="0">
<tr>
<td style="background: #ffffff; padding: 3px; width: 70px; height: 70px; vertical-align: middle; text-align: center; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px;"><a href="http://{{ domain }}{{ event.get_permanent_url }}"><img src="cid:mail_expo_logo_{{ event.id }}" style="width: 100%;" alt="" /></a></td>
</tr>
</table>
</td>
<td style="padding: 15px 15px 15px 0; border-bottom: 1px dotted #cccccc;">
<h2 style="margin: 0 0 5px; font-family: Tahoma, Arial, sans-serif; font-size: 18px; line-height: 21px;"><a style="color: #464646; text-decoration: none;" href="http://{{ domain }}{{ event.get_permanent_url }}">{{ event.name }}</a></h2>
<p style="margin: 0 0 7px; font-size: 12px; line-height: 15px; color: #a2a2a2"><a style="color: #a2a2a2; text-decoration: none;" href="http://{{ domain }}{{ event.get_permanent_url }}">{{ obj.main_title|safe }}</a></p>
<a class="button" style="display: inline-block; padding: 4px 10px 3px; text-decoration: none; color: #ff6600; font-size: 11px; font-weight: bold; line-height: 14px; border: 1px solid #feb17d; text-transform: uppercase; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px;" href="http://{{ domain }}{{ event.get_permanent_url }}">добавить в расписание</a>
<div class="addr" style="margin-top: 10px; font-size: 13px; line-height: 15px;"><img src="cid:pin" width="10" height="15" alt="" style="vertical-align: middle; margin-top: -1px;" /> {{ event.country.name }}, {{ event.city.name }}{% if event.place %}, {{ event.place.name }}{% endif %}</div>
</td>
<td style="padding: 17px 0; text-align: right; font-size: 13px; line-height: 16px; color: #ff6600; width: 140px; border-bottom: 1px dotted #cccccc;">
<img src="cid:clock" width="14" height="15" style="vertical-align: middle; margin-top: -1px;" alt="" /> {% include 'client/includes/show_date_block.html' with obj=event %}
</td>
</tr>
{% endfor %}
</table>
<div class="more" style="text-align: center;">
<a class="button" style="display: inline-block; padding: 4px 10px 3px; text-decoration: none; color: #2592c5; font-size: 11px; font-weight: bold; line-height: 14px; border: 1px solid #90c7e0; text-transform: uppercase; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; width: 336px;" href="/expo/">посмотреть все события</a>
</div>
</td>
</tr>
</table>
{% endwith %}
{# end events #}
{% if news %}
<table cellpadding="0" cellspacing="0" border="0" width="600" style="width: 600px; margin-bottom: 10px;">
<tr>
<td style="padding: 20px 0 0;"><a style="display: block; padding: 25px 30px; text-decoration: none; background: #ff6600; color: #ffffff; font-size: 20px; line-height: 26px;" href="/news/">Новости событий</a></td>
</tr>
<tr>
<td style="padding: 10px 30px 15px; background: #faf9f7;">
<table cellpadding="0" cellspacing="0" border="0" width="540" style="margin-bottom: 15px;">
{% for item in news %}
<tr valign="top">
<td style="padding: 15px 15px 15px 0; width: 76px; border-bottom: 1px dotted #cccccc;">
<table cellpadding="0" cellspacing="0" border="0">
<tr>
<td style="background: #ffffff; padding: 0; width: 76px; height: 76px; vertical-align: middle; text-align: center;"><a href="{{ item.get_permanent_url }}"><img style="width: 100%; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px;" alt="" src="cid:mail_news_logo_{{ item.id }}" /></a></td>
</tr>
</table>
</td>
<td style="padding: 15px 0; border-bottom: 1px dotted #cccccc;">
<table cellpadding="0" cellspacing="0" border="0" width="100%" style="margin-bottom: 5px;">
<tr>
<td><h2 style="margin: 0 0 5px; font-family: Tahoma, Arial, sans-serif; font-size: 18px; line-height: 21px;"><a style="color: #464646; text-decoration: none;" href="{{ item.get_permanent_url }}">{{ item.main_title }}</a></h2></td>
<td style="font-size: 13px; line-height: 15px; text-align: right; color: #ff6600;">{{ item.publish_date|date:"d.m.Y" }}</td>
</tr>
</table>
<p style="margin: 0 0 7px; font-size: 13px; line-height: 17px;"><a style="color: #464646; text-decoration: none;" href="{{ item.get_permanent_url }}">{{ item.preview }} <span style="text-decoration: underline; color: #ff6600;">...</span></a></p>
</td>
</tr>
{% endfor %}
</table>
<div class="more" style="text-align: center;">
<a class="button" style="display: inline-block; padding: 4px 10px 3px; text-decoration: none; color: #2592c5; font-size: 11px; font-weight: bold; line-height: 14px; border: 1px solid #90c7e0; text-transform: uppercase; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; width: 336px;" href="/news/">посмотреть все новости</a>
</div>
</td>
</tr>
</table>
{% endif %}
{% if blogs %}
<table cellpadding="0" cellspacing="0" border="0" width="600" style="width: 600px; margin-bottom: 10px;">
<tr>
<td style="padding: 20px 0 0;"><a style="display: block; padding: 25px 30px; text-decoration: none; background: #ff6600; color: #ffffff; font-size: 20px; line-height: 26px;" href="/blogs/">Аналитика для профессионалов</a></td>
</tr>
<tr>
<td style="padding: 10px 30px 15px; background: #faf9f7;">
<table cellpadding="0" cellspacing="0" border="0" width="540">
{% for item in blogs %}
<tr valign="top">
<td style="padding: 15px 15px 0 0; width: 76px;">
<table cellpadding="0" cellspacing="0" border="0">
<tr>
<td style="background: #ffffff; padding: 0; width: 76px; height: 76px; vertical-align: middle; text-align: center;"><a href="{{ item.get_permanent_url }}"><img style="width: 100%; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px;" alt="" src="cid:mail_blogs_logo_{{ item.id }}" /></a></td>
</tr>
</table>
</td>
<td style="padding: 15px 0 0;">
<h2 style="margin: 0 0 5px; font-family: Tahoma, Arial, sans-serif; font-size: 18px; line-height: 21px;"><a style="color: #464646; text-decoration: none;" href="{{ item.get_permanent_url }}">{{ item.main_title }}</a></h2>
<p style="margin: 0 0 7px; font-size: 13px; line-height: 17px;"><a style="color: #464646; text-decoration: none;" href="{{ item.get_permanent_url }}">{{ item.preview }} <span style="text-decoration: underline; color: #ff6600;">...</span></a></p>
</td>
</tr>
{% endfor %}
</table>
</td>
</tr>
</table>
{% endif %}
<table cellpadding="0" cellspacing="0" border="0" width="600" style="width: 600px; border-bottom: 1px dotted #cccccc;">
<tr>
<td style="vertical-align: top; padding: 15px 0 10px;">
<div class="logo">
<a style="text-decoration: none; color: #a2a2a2; font-size: 12px;" href="/">
<img src="cid:logo_2" alt="Expomap.ru" />
</a>
</div>
</td>
<td style="vertical-align: top; padding: 25px 0 5px;">
<ul class="t-links" style="margin: 0 0 15px; padding: 0; list-style: none; text-align: right; font-size: 14px; line-height: 15px; font-weight: bold;">
<li style="display: inline-block;"><a style="text-decoration: none; color: #ff6600" href="/expo/">СОБЫТИЯ</a></li>
<li style="display: inline-block; margin-left: 20px;"><a style="text-decoration: none; color: #ff6600" href="/places/">МЕСТА</a></li>
<li style="display: inline-block; margin-left: 20px;"><a style="text-decoration: none; color: #ff6600" href="/members/">УЧАСТНИКИ</a></li>
</ul>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>

@ -1,8 +1,14 @@
{% load i18n %} {% load i18n %}
<div id="unsubscription"> <div id="unsubscription">
<a name="unsubscription"></a> <table border="0" cellpadding="0" cellspacing="0" style="font-size: 12px; line-height: 15px; font-family: Arial, sans-serif; background: #f4f2ee;" width="100%" bgcolor="#f4f2ee" >
<p> <tbody>
{% trans "For unsubscribing to this mailing list," %} <tr>
<a href="{% if uidb36 and token %}http://{{ domain }}{% url 'newsletter_mailinglist_unsubscribe' slug=newsletter.slug uidb36=uidb36 token=token %}{% else %}#{% endif %}">{% trans "click here" %}</a>. <td style="vertical-align: top; padding: 15px 0 15px; text-align: center;">Чтобы отписаться от этой рассылки, перейдите <a href="{% if uidb36 and token %}http://{{ domain }}{% url 'newsletter_mailinglist_unsubscribe' slug=newsletter.slug uidb36=uidb36 token=token %}{% else %}#{% endif %}">по ссылке</a>.
</p> <td style="vertical-align: top; padding: 15px 0 15px; color: #a2a2a2; text-align: left;">&copy; 2018 &mdash; 2013 <a href="#" style="color: #a2a2a2; text-decoration: none;">Expomap.ru</a></td>
</tr>
</tbody>
</table>
</div> </div>

@ -16,5 +16,5 @@ urlpatterns = patterns('',
url(r'^activation/complete/', TemplateView.as_view(template_name='client/newsletters/activation_complete.html'), url(r'^activation/complete/', TemplateView.as_view(template_name='client/newsletters/activation_complete.html'),
name='subscription_activation_complete'), name='subscription_activation_complete'),
url(r'^activate/(?P<activation_key>.*)/$', ActivationView.as_view(), name='subscription_activation'), url(r'^activate/(?P<activation_key>.*)/$', ActivationView.as_view(), name='subscription_activation'),
url(r'^', SubscribeView.as_view(), name='newsletter_subscription'), url(r'^$', SubscribeView.as_view(), name='newsletter_subscription'),
) )

@ -1,17 +1,17 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.views.generic import TemplateView, CreateView, ListView, UpdateView, DeleteView, FormView from django.views.generic import TemplateView, CreateView, ListView, UpdateView, DeleteView, FormView
from django.conf import settings from django.conf import settings
from django.http import HttpResponseRedirect from django.utils.translation import ugettext_lazy as _
from django.forms.formsets import BaseFormSet, formset_factory from django.forms.formsets import BaseFormSet, formset_factory
from django.http import HttpResponseRedirect, HttpResponse from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.core.urlresolvers import reverse from HTMLParser import HTMLParseError
from emencia.django.newsletter.models import Contact, ContactSettings, MailingList, Newsletter, Attachment from emencia.django.newsletter.models import Contact, ContactSettings, MailingList, Newsletter, Attachment
from emencia.django.newsletter.admin_forms import ContactSettingsForm, MailingListForm, NewsletterForm, AttachmentForm from emencia.django.newsletter.admin_forms import ContactSettingsForm, MailingListForm, NewsletterForm, AttachmentForm
from django.core.urlresolvers import reverse_lazy from django.core.urlresolvers import reverse_lazy
from emencia.django.newsletter.models import Contact, ContactSettings, MailingList, Newsletter from emencia.django.newsletter.mailer import Mailer
from emencia.django.newsletter.admin_forms import ContactSettingsForm, MailingListForm, NewsletterForm
from ..forms import ContactFilterForm from ..forms import ContactFilterForm
from ..utils.excel import ExcelResponse
from functions.admin_views import paginate_results from functions.admin_views import paginate_results
@ -112,9 +112,6 @@ class NewsletterCreate(CreateView):
success_url = '/admin/newsletters/newsletters/all/' success_url = '/admin/newsletters/newsletters/all/'
def get_formset(self): def get_formset(self):
#if self.request.POST:
# AttachmentFormSet = formset_factory(Attachment)
#else:
AttachmentFormSet = formset_factory(AttachmentForm) AttachmentFormSet = formset_factory(AttachmentForm)
if self.request.POST: if self.request.POST:
return AttachmentFormSet(self.request.POST, self.request.FILES) return AttachmentFormSet(self.request.POST, self.request.FILES)
@ -129,6 +126,8 @@ class NewsletterCreate(CreateView):
def form_valid(self, form): def form_valid(self, form):
self.object = form.save() self.object = form.save()
self.object.test_contacts = form.cleaned_data['test_contacts']
formset = self.get_formset() formset = self.get_formset()
if formset.is_valid(): if formset.is_valid():
for item in formset.forms: for item in formset.forms:
@ -164,6 +163,8 @@ class NewsletterUpdate(UpdateView):
def form_valid(self, form): def form_valid(self, form):
self.object = form.save() self.object = form.save()
self.object.test_contacts = form.cleaned_data['test_contacts']
formset = self.get_formset() formset = self.get_formset()
if formset.is_valid(): if formset.is_valid():
for item in formset.forms: for item in formset.forms:
@ -179,10 +180,23 @@ class NewsletterUpdate(UpdateView):
class NewsletterListView(ListView): class NewsletterListView(ListView):
paginate_by = settings.ADMIN_PAGINATION paginate_by = settings.ADMIN_PAGINATION
model = Newsletter model = Newsletter
template_name = 'admin/newsletters/newsletter_list.html' success_url = '/admin/newsletters/newsletters/all/' template_name = 'admin/newsletters/newsletter_list.html'
success_url = '/admin/newsletters/newsletters/all/'
from ..utils.excel import ExcelResponse def send_test_newsletter(request, pk):
newsletter = get_object_or_404(Newsletter, pk=pk)
if newsletter.test_contacts.count():
mailer = Mailer(newsletter, test=True)
try:
mailer.run()
except HTMLParseError:
return HttpResponse(_('Unable send newsletter, due to errors within HTML.'))
else:
return HttpResponse(u'Нет тестовых контактов')
redirect = request.META.get('HTTP_REFERER', '/admin/newsletter/newsletter/all/')
return HttpResponseRedirect(redirect)
class ExportContacts(FormView): class ExportContacts(FormView):

@ -31,6 +31,7 @@
{% endblock %} {% endblock %}
{% block body %} {% block body %}
{{ form.errors }}
<form method="post" class="form-horizontal" name="form2" id="form2"enctype="multipart/form-data"> {% csrf_token %} <form method="post" class="form-horizontal" name="form2" id="form2"enctype="multipart/form-data"> {% csrf_token %}
<fieldset> <fieldset>
<legend><i class="icon-edit"></i>{% if article %} Изменить {% else %} Добавить {% endif %}статью{% if article %}(<a href="{{ article.get_permanent_url }}" target="_blank">на сайте</a>){% endif %}</legend> <legend><i class="icon-edit"></i>{% if article %} Изменить {% else %} Добавить {% endif %}статью{% if article %}(<a href="{{ article.get_permanent_url }}" target="_blank">на сайте</a>){% endif %}</legend>

@ -17,6 +17,7 @@
<th>Статус</th> <th>Статус</th>
<th>Дата отправки</th> <th>Дата отправки</th>
<th>&nbsp;</th> <th>&nbsp;</th>
<th>&nbsp;</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -26,6 +27,7 @@
<td>{{ item.mailing_list }}</td> <td>{{ item.mailing_list }}</td>
<td>{{ item.get_status_text }}</td> <td>{{ item.get_status_text }}</td>
<td>{{ item.sending_date|date:"Y-m-d H:i" }}</td> <td>{{ item.sending_date|date:"Y-m-d H:i" }}</td>
<td><a href="{% url 'newsletters_newsletters_send_test' item.id %}">тест</a> </td>
<td><a href="{% url 'newsletters_newsletters_update' item.id %}">Изменить</a> </td> <td><a href="{% url 'newsletters_newsletters_update' item.id %}">Изменить</a> </td>
</tr> </tr>
{% endfor %} {% endfor %}

Loading…
Cancel
Save