From 17834aa692a60e0fac94fb32d7d56568be0bf4ff Mon Sep 17 00:00:00 2001 From: Nazar Kotjuk Date: Tue, 13 Oct 2015 17:14:17 +0300 Subject: [PATCH] newsletter announces --- article/admin.py | 2 +- article/forms.py | 2 +- .../commands/newsletter_create_dependies.py | 31 +++ emencia/django/newsletter/admin_forms.py | 2 - emencia/django/newsletter/admin_urls.py | 4 + emencia/django/newsletter/mailer.py | 132 +++++++++---- .../commands/newsletter_create_announce.py | 18 ++ emencia/django/newsletter/models.py | 74 +++++++- emencia/django/newsletter/settings.py | 2 + .../newsletter/announce_template.html | 176 ++++++++++++++++++ .../newsletter_link_unsubscribe.html | 16 +- emencia/django/newsletter/urls/__init__.py | 2 +- .../django/newsletter/views/admin_views.py | 32 +++- templates/admin/article/blog_form.html | 1 + .../admin/newsletters/newsletter_list.html | 2 + 15 files changed, 443 insertions(+), 53 deletions(-) create mode 100644 country/management/commands/newsletter_create_dependies.py create mode 100644 emencia/django/newsletter/management/commands/newsletter_create_announce.py create mode 100644 emencia/django/newsletter/templates/newsletter/announce_template.html diff --git a/article/admin.py b/article/admin.py index 29b59839..55b05d7d 100644 --- a/article/admin.py +++ b/article/admin.py @@ -231,7 +231,7 @@ class NewsList(ListView): class NewsView(BlogView): form_class = NewsForm - template_name = 'article/blog_form.html' + template_name = 'admin/article/blog_form.html' success_url = '/admin/article/news/all/' obj = None diff --git a/article/forms.py b/article/forms.py index 013ed960..c5d1508d 100644 --- a/article/forms.py +++ b/article/forms.py @@ -21,7 +21,7 @@ class BlogForm(forms.Form): type = Article.blog theme = forms.ModelMultipleChoiceField(label='Тематики', queryset=ThemeBlog.objects.all(), required=False, 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) tag = forms.CharField(label=u'Теги', widget=forms.HiddenInput(), required=False) logo = forms.ImageField(label=u'Лого', required=False) diff --git a/country/management/commands/newsletter_create_dependies.py b/country/management/commands/newsletter_create_dependies.py new file mode 100644 index 00000000..9b19b745 --- /dev/null +++ b/country/management/commands/newsletter_create_dependies.py @@ -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)) \ No newline at end of file diff --git a/emencia/django/newsletter/admin_forms.py b/emencia/django/newsletter/admin_forms.py index e2b9f064..a15c1667 100644 --- a/emencia/django/newsletter/admin_forms.py +++ b/emencia/django/newsletter/admin_forms.py @@ -58,8 +58,6 @@ class NewsletterForm(forms.ModelForm): fields = ('title', 'content', 'mailing_list', 'test_contacts', 'header_sender', 'header_reply', 'status', 'sending_date', 'slug') - def clean_test_contacts(self): - return [] class AttachmentForm(forms.ModelForm): class Meta: diff --git a/emencia/django/newsletter/admin_urls.py b/emencia/django/newsletter/admin_urls.py index ba0ac198..aafff25d 100644 --- a/emencia/django/newsletter/admin_urls.py +++ b/emencia/django/newsletter/admin_urls.py @@ -9,8 +9,12 @@ from emencia.django.newsletter.views.admin_views import ContactList, UpdateConta urlpatterns = patterns('', url(r'^newsletters/all/$', NewsletterListView.as_view(), name='newsletters_newsletters_list'), url(r'^newsletters/(?P\d+)/edit/', NewsletterUpdate.as_view(), name='newsletters_newsletters_update'), + url(r'^newsletters/(?P\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'^mailinglist/all/$', MailingListView.as_view(), name='newsletters_mailinglist'), url(r'^mailinglist/(?P\d+)/edit/', UpdateMailingList.as_view(), name='newsletters_mailinglist_update'), url(r'^mailinglist/', CreateMailingList.as_view(), name='newsletters_mailinglist_create'), diff --git a/emencia/django/newsletter/mailer.py b/emencia/django/newsletter/mailer.py index 9891a751..07308d8e 100644 --- a/emencia/django/newsletter/mailer.py +++ b/emencia/django/newsletter/mailer.py @@ -30,7 +30,7 @@ from email import message_from_file from html2text import html2text as html2text_orig from django.contrib.sites.models import Site 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_unicode from django.core.urlresolvers import reverse @@ -71,17 +71,7 @@ def html2text(html): https://github.com/aaronsw/html2text/issues/#issue/7 is not fixed""" txt = html2text_orig(html) 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): outfile = StringIO() @@ -101,14 +91,18 @@ class NewsLetterSender(object): self.newsletter = newsletter self.newsletter_template = Template(self.newsletter.content) 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 a multipart alternative for text (plain, HTML) plus all the attached files. """ - content_html = self.build_email_content(contact) + + content_html = self.build_email_content(contact, announce_context) + h = HTMLParser.HTMLParser() content_html = h.unescape(content_html) content_text = html2text(content_html) @@ -139,6 +133,12 @@ class NewsLetterSender(object): for attachment in self.attachments: 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(): message[header] = value @@ -147,8 +147,6 @@ class NewsLetterSender(object): message['List-Unsubscribe'] = '<' + unsubscribe_link + '>' message['List-Id'] = str(self.newsletter.id) - - return message def build_attachments(self): @@ -185,6 +183,61 @@ class NewsLetterSender(object): 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): """Generate the email title for a contact""" context = Context({'contact': contact, @@ -193,7 +246,7 @@ class NewsLetterSender(object): title = self.title_template.render(context) return title - def build_email_content(self, contact): + def build_email_content(self, contact, announce_context=None): """Generate the mail for a contact""" uidb36, token = tokenize(contact) context = Context({'contact': contact, @@ -201,13 +254,19 @@ class NewsLetterSender(object): 'newsletter': self.newsletter, 'tracking_image_format': TRACKING_IMAGE_FORMAT, 'uidb36': uidb36, 'token': token}) - - content = self.newsletter_template.render(context) + if self.announce: + # 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: content = track_links(content, context) - link_site = render_to_string('newsletter/newsletter_link_site.html', context) - content = body_insertion(content, link_site) + # uncomment if wanna include this link + #link_site = render_to_string('newsletter/newsletter_link_site.html', context) + #content = body_insertion(content, link_site) if INCLUDE_UNSUBSCRIPTION: @@ -217,9 +276,7 @@ class NewsLetterSender(object): if TRACKING_IMAGE: image_tracking = render_to_string('newsletter/newsletter_image_tracking.html', context) content = body_insertion(content, image_tracking, end=True) - #return content - #return encodestring(content) return smart_unicode(content) 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) return expedition_list - def update_contact_status(self, contact, exception): - if exception is None: + def update_contact_status(self, contact, exception, send): + if not send: + status = ContactMailingStatus.ANNOUNCE_NO_DATA + elif exception is None: status = (self.test and ContactMailingStatus.SENT_TEST or ContactMailingStatus.SENT) @@ -299,21 +358,30 @@ class Mailer(NewsLetterSender): i = 1 for contact in expedition_list: + send = True if self.verbose: print '- Processing %s/%s (%s)' % ( 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: exception = e else: exception = None - self.update_contact_status(contact, exception) + + self.update_contact_status(contact, exception, send) if SLEEP_BETWEEN_SENDING: time.sleep(SLEEP_BETWEEN_SENDING) diff --git a/emencia/django/newsletter/management/commands/newsletter_create_announce.py b/emencia/django/newsletter/management/commands/newsletter_create_announce.py new file mode 100644 index 00000000..94679626 --- /dev/null +++ b/emencia/django/newsletter/management/commands/newsletter_create_announce.py @@ -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) \ No newline at end of file diff --git a/emencia/django/newsletter/models.py b/emencia/django/newsletter/models.py index fd607fb2..f60497cd 100644 --- a/emencia/django/newsletter/models.py +++ b/emencia/django/newsletter/models.py @@ -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 DEFAULT_HEADER_REPLY 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 import make_activation_code @@ -152,6 +152,41 @@ class Contact(models.Model): return self.content_object.get_absolute_url() 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): if self.first_name and self.last_name: contact_name = '%s %s' % (self.last_name, self.first_name) @@ -246,6 +281,39 @@ class MailingList(models.Model): def __unicode__(self): 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: ordering = ('-creation_date',) verbose_name = _('mailing list') @@ -278,7 +346,7 @@ class Newsletter(models.Model): blank=True, null=True) 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, default=DEFAULT_HEADER_SENDER) header_reply = models.CharField(_('reply to'), max_length=255, @@ -376,6 +444,7 @@ class ContactMailingStatus(models.Model): OPENED_ON_SITE = 5 LINK_OPENED = 6 UNSUBSCRIPTION = 7 + ANNOUNCE_NO_DATA = 8 STATUS_CHOICES = ((SENT_TEST, _('sent in test')), (SENT, _('sent')), @@ -385,6 +454,7 @@ class ContactMailingStatus(models.Model): (OPENED_ON_SITE, _('opened on site')), (LINK_OPENED, _('link opened')), (UNSUBSCRIPTION, _('unsubscription')), + (ANNOUNCE_NO_DATA, _('announce no data')), ) newsletter = models.ForeignKey(Newsletter, verbose_name=_('newsletter')) diff --git a/emencia/django/newsletter/settings.py b/emencia/django/newsletter/settings.py index 29a05275..c072e28f 100644 --- a/emencia/django/newsletter/settings.py +++ b/emencia/django/newsletter/settings.py @@ -41,3 +41,5 @@ BASE_PATH = getattr(settings, 'NEWSLETTER_BASE_PATH', 'uploads/newsletter') ACTIVATION_SUBJECT = 'client/newsletters/activation_subject.txt' ACTIVATION_TEMPLATE = 'client/newsletters/activation_template.html' + +DEFAULT_SMPTSERVER_ID = 1 diff --git a/emencia/django/newsletter/templates/newsletter/announce_template.html b/emencia/django/newsletter/templates/newsletter/announce_template.html new file mode 100644 index 00000000..1b7731a9 --- /dev/null +++ b/emencia/django/newsletter/templates/newsletter/announce_template.html @@ -0,0 +1,176 @@ + + + + + +
+ {# header #} + + + + + +
+ + + + +
    +
  • Facebook
  • +
  • LinkedIn
  • +
  • В контакте
  • +
  • Twitter
  • +
+
+ {# end header #} + + {# events #} + {% with events=events %} + + + + + + + +
Выставки{% if place %} {{ place.inflect }}{% endif %} {% if themes|length == 1 %}по тематике:{{ themes.0.name }}{% endif %}
+ + + {% for event in events %} + + + + + + {% endfor %} + +
+ + + + +
+
+

{{ event.name }}

+

{{ obj.main_title|safe }}

+ добавить в расписание +
{{ event.country.name }}, {{ event.city.name }}{% if event.place %}, {{ event.place.name }}{% endif %}
+
+ {% include 'client/includes/show_date_block.html' with obj=event %} +
+ + + +
+ {% endwith %} + {# end events #} + + {% if news %} + + + + + + + +
Новости событий
+ + + {% for item in news %} + + + + + {% endfor %} + +
+ + + + +
+
+ + + + + +

{{ item.main_title }}

{{ item.publish_date|date:"d.m.Y" }}
+ +

{{ item.preview }} ...

+ +
+ + + +
+ {% endif %} + {% if blogs %} + + + + + + + +
Аналитика для профессионалов
+ + + {% for item in blogs %} + + + + + + {% endfor %} + +
+ + + + +
+
+ +

{{ item.main_title }}

+

{{ item.preview }} ...

+ +
+ +
+ {% endif %} + + + + + + +
+ + + +
+ +
+ \ No newline at end of file diff --git a/emencia/django/newsletter/templates/newsletter/newsletter_link_unsubscribe.html b/emencia/django/newsletter/templates/newsletter/newsletter_link_unsubscribe.html index b9a78f1f..b1a281a0 100644 --- a/emencia/django/newsletter/templates/newsletter/newsletter_link_unsubscribe.html +++ b/emencia/django/newsletter/templates/newsletter/newsletter_link_unsubscribe.html @@ -1,8 +1,14 @@ {% load i18n %}
- -

- {% trans "For unsubscribing to this mailing list," %} - {% trans "click here" %}. -

+ + + + + + +
Чтобы отписаться от этой рассылки, перейдите по ссылке. + © 2018 — 2013 Expomap.ru
+
+ + diff --git a/emencia/django/newsletter/urls/__init__.py b/emencia/django/newsletter/urls/__init__.py index 5d9a47b3..395d3c8a 100644 --- a/emencia/django/newsletter/urls/__init__.py +++ b/emencia/django/newsletter/urls/__init__.py @@ -16,5 +16,5 @@ urlpatterns = patterns('', url(r'^activation/complete/', TemplateView.as_view(template_name='client/newsletters/activation_complete.html'), name='subscription_activation_complete'), url(r'^activate/(?P.*)/$', ActivationView.as_view(), name='subscription_activation'), - url(r'^', SubscribeView.as_view(), name='newsletter_subscription'), + url(r'^$', SubscribeView.as_view(), name='newsletter_subscription'), ) diff --git a/emencia/django/newsletter/views/admin_views.py b/emencia/django/newsletter/views/admin_views.py index e0210c4e..8ee8804b 100644 --- a/emencia/django/newsletter/views/admin_views.py +++ b/emencia/django/newsletter/views/admin_views.py @@ -1,17 +1,17 @@ # -*- coding: utf-8 -*- from django.views.generic import TemplateView, CreateView, ListView, UpdateView, DeleteView, FormView 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.http import HttpResponseRedirect, HttpResponse 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.admin_forms import ContactSettingsForm, MailingListForm, NewsletterForm, AttachmentForm from django.core.urlresolvers import reverse_lazy -from emencia.django.newsletter.models import Contact, ContactSettings, MailingList, Newsletter -from emencia.django.newsletter.admin_forms import ContactSettingsForm, MailingListForm, NewsletterForm +from emencia.django.newsletter.mailer import Mailer from ..forms import ContactFilterForm +from ..utils.excel import ExcelResponse from functions.admin_views import paginate_results @@ -112,9 +112,6 @@ class NewsletterCreate(CreateView): success_url = '/admin/newsletters/newsletters/all/' def get_formset(self): - #if self.request.POST: - # AttachmentFormSet = formset_factory(Attachment) - #else: AttachmentFormSet = formset_factory(AttachmentForm) if self.request.POST: return AttachmentFormSet(self.request.POST, self.request.FILES) @@ -129,6 +126,8 @@ class NewsletterCreate(CreateView): def form_valid(self, form): self.object = form.save() + self.object.test_contacts = form.cleaned_data['test_contacts'] + formset = self.get_formset() if formset.is_valid(): for item in formset.forms: @@ -164,6 +163,8 @@ class NewsletterUpdate(UpdateView): def form_valid(self, form): self.object = form.save() + self.object.test_contacts = form.cleaned_data['test_contacts'] + formset = self.get_formset() if formset.is_valid(): for item in formset.forms: @@ -179,10 +180,23 @@ class NewsletterUpdate(UpdateView): class NewsletterListView(ListView): paginate_by = settings.ADMIN_PAGINATION 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): diff --git a/templates/admin/article/blog_form.html b/templates/admin/article/blog_form.html index 057b565d..521110e1 100644 --- a/templates/admin/article/blog_form.html +++ b/templates/admin/article/blog_form.html @@ -31,6 +31,7 @@ {% endblock %} {% block body %} + {{ form.errors }}
{% csrf_token %}
{% if article %} Изменить {% else %} Добавить {% endif %}статью{% if article %}(на сайте){% endif %} diff --git a/templates/admin/newsletters/newsletter_list.html b/templates/admin/newsletters/newsletter_list.html index 94d0164e..4f8cc05f 100644 --- a/templates/admin/newsletters/newsletter_list.html +++ b/templates/admin/newsletters/newsletter_list.html @@ -17,6 +17,7 @@ Статус Дата отправки   +   @@ -26,6 +27,7 @@ {{ item.mailing_list }} {{ item.get_status_text }} {{ item.sending_date|date:"Y-m-d H:i" }} + тест Изменить {% endfor %}