Giter Site home page Giter Site logo

emencia-django-newsletter's Introduction

Emencia Django Newsletter

The problematic was :

How to couple a contact base to a mailing list and sending newsletters throught Django ?

Imagine that we have an application containing some kind of profiles or something like the django.contrib.auth and you want to send newsletters to them and tracking the activity.

More than a long speech, here the list of the main features :

  • Coupling capacities with another django model.
  • Variables can be used in the newsletter's templates.
  • Mailing list managements (merging, importing...).
  • Import/Export of the contact in VCard 3.0.
  • Configurable SMTP servers with flow limit management.
  • Working groups.
  • Can send newsletter previews.
  • Subscriptions and unsubscriptions to mailing list.
  • Attachments in newsletters.
  • Unique urls for an user.
  • Tracking statistics.

At the level of the application architecture, we can see 2 originalities who need to be explained.

The content types application is used to link any Contact model instance to another model instance. This allow you to create different kinds of contact linked to differents application, and retrieve the association at anytime.

This is particulary usefull with the templates variables if certain informations are located in the model instance linked.

The emencia.django.newsletter application will never send the newsletters registered in the site until you launch the send_newsletter command.

$ python manage.py send_newsletter

This command will launch the newsletters who need to be launched accordingly to the credits of the SMTP server of the newsletter. That's mean that not all newsletters will be expedied at the end of the command because if you use a public SMTP server you can be banished temporarly if you reach the sending limit. To avoid banishment all the newsletters are not sended in the same time and immediately.

So it is recommanded to create a cronjob for launching this command every hours for example.

Make sure to install these packages prior to installation :

  • Django >= 1.2
  • html2text
  • BeautifulSoup
  • django-tagging
  • vobject
  • xlwt
  • xlrd

The package below is optionnal but handy for rendering a webpage in your newsletter.

  • lxml

You could retrieve the last sources from http://github.com/Fantomas42/emencia-django-newsletter and running the installation script

$ python setup.py install

or use pip

$ pip install -e git://github.com/Fantomas42/emencia-django-newsletter.git#egg=emencia.django.newsletter

For the latest stable version use easy_install

$ easy_install emencia.django.newsletter

Then register emencia.django.newsletter, admin, contenttypes and tagging in the INSTALLED_APPS section of your project's settings.

INSTALLED_APPS = (
  # Your favorites apps
  'django.contrib.contenttypes',
  'django.contrib.sites',
  'django.contrib.admin',
  'django.contrib.sessions',
  'tagging',
  'emencia.django.newsletter',)

In your project urls.py adding this following line to include the newsletter's urls for serving the newsletters in HTML.

url(r'^newsletters/', include('emencia.django.newsletter.urls')),

Note this urlset is provided for convenient usage, but you can do something like that if you want to customize your urls :

url(r'^newsletters/', include('emencia.django.newsletter.urls.newsletter')),
url(r'^mailing/', include('emencia.django.newsletter.urls.mailing_list')),
url(r'^tracking/', include('emencia.django.newsletter.urls.tracking')),
url(r'^statistics/', include('emencia.django.newsletter.urls.statistics')),

You have to make a symbolic link from emencia/django/newsletter/media/edn/ directory to your media directory or make a copy named edn, but if want to change this value, define NEWSLETTER_MEDIA_URL in the settings.py as appropriate.

Don't forget to serve this url.

Now you can run a syncdb for installing the models into your database.

You have to add in your settings the email address used to send the newsletter :

NEWSLETTER_DEFAULT_HEADER_SENDER = 'My NewsLetter <[email protected]>'

It's not recommended to use SQLite for production use. Because is limited to 999 variables into a SQL query, you can not create a Mailing List greater than this limitations in the Django's admin modules. Prefer MySQL ou PgSQL.

It can be usefull for the end user to have a WYSIWYG editor for the creation of the newsletter. The choice of the WYSIWYG editor is free and the described method can be applied for anything, but we will focus on TinyMCE because he has many features and a usefull plugin for loading templates within it.

First of all install the django-tinymce application into your project.

That's done, enjoy !

If you wan to quickly import your contacts into a mailing list for example, you can write an admin's action for your model.

We suppose that we have the fields email, first_name and last_name in a models name Profile.

In his AdminModel definition add this method and register it into the actions property.

class ProfileAdmin(admin.ModelAdmin):

    def make_mailing_list(self, request, queryset):
        from emencia.django.newsletter.models import Contact
        from emencia.django.newsletter.models import MailingList

        subscribers = []
        for profile in queryset:
            contact, created = Contact.objects.get_or_create(email=profile.mail,
                                                             defaults={'first_name': profile.first_name,
                                                                       'last_name': profile.last_name,
                                                                       'content_object': profile})
            subscribers.append(contact)
        new_mailing = MailingList(name='New mailing list',
                                  description='New mailing list created from admin/profile')
        new_mailing.save()
        new_mailing.subscribers.add(*subscribers)
        new_mailing.save()
        self.message_user(request, '%s succesfully created.' % new_mailing)
    make_mailing_list.short_description = 'Create a mailing list'

    actions = ['make_mailing_list']

This action will create or retrieve all the Contact instances needed for the mailing list creation.

After this you can send a newsletter to this mailing list.

A Buildout script is provided to properly initialize the project for anybody who wants to contribute.

First of all, please use VirtualEnv to protect your system.

Follow these steps to start the development :

$ git clone git://github.com/Fantomas42/emencia-django-newsletter.git
$ virtualenv --no-site-packages emencia-django-newsletter
$ cd emencia-django-newsletter
$ source ./bin/activate
$ python bootstrap.py
$ ./bin/buildout

The buildout script will resolve all the dependancies needed to develop the application.

Once these operations are done, you are ready to develop on the project.

Run this command to launch the tests.

$ ./bin/test

Or you can also launch the demo.

$ ./bin/demo syncdb
$ ./bin/demo runserver

Pretty easy no ?

If you want to contribute by updating a translation or adding a translation in your language, it's simple: create a account on Transifex.net and you will be able to edit the translations at this URL :

http://www.transifex.net/projects/p/emencia-django-newsletter/resource/djangopo/

http://www.transifex.net/projects/p/emencia-django-newsletter/resource/djangopo/chart/image_png

The translations hosted on Transifex.net will be pulled periodically in the repository, but if you are in a hurry, send me a message.

https://github.com/Fantomas42/emencia-django-newsletter/raw/master/docs/graph_model.png

emencia-django-newsletter's People

Contributors

alesasnouski avatar alexgarel avatar bvallant avatar drye avatar emencia75 avatar fantomas42 avatar jdavid avatar jfache avatar lucalenardi avatar martip avatar vvarp avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

emencia-django-newsletter's Issues

language of mail - possible extension

How can the mailer determine the language of the client for the unsubscribe and canviewsite links. A solution would be to introduce a language field in the contact model and make the newsletter model also multilingual. Any interest ?

WYSIWYG

Hi,

do you plan to put there any WYSIWYG editor for newsletter's body editing?
If not, I'll put it on my own ;)

Hardcoded French?

The two templates:

newsletter_link_site.html
newsletter_link_unsubscribe.html

Seem to contain hardcoded (no i18n translation) French strings for viewing the newsletter onsite or unsubscribing. Surely this isn't the way it's meant to be? Am I supposed to override these templates with my own, or wouldn't it make sense to use translated strings here...

Admin unicode' object has no attribute 'day' error/bug

I am using the latest version of emencia-django-newsletter with all the dependencies satisfied. It appears everything is working - i can create a newsletter but than when I attempt to visit the admin: /admin/newsletter/newsletter/ -- I get the below error -- do you have any suggestions?

Environment:

Request Method: GET
Request URL: http://dev.klinsight.com:1000/admin/newsletter/newsletter/

Django Version: 1.3
Python Version: 2.6.6
Installed Applications:
['django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.flatpages',
'django.contrib.admin',
'django.contrib.gis',
'taggit',
'tagging',
'ckeditor',
'simplemenu',
'emencia.django.newsletter',
'contact_form',
'registration',
'floppyforms',
'guardian',
'htmlpages',
'flatpages_ext',
'miscellaneous',
'filelibrary',
'showcase',
'accounts',
'golf_shafts',
'golf_tours',
'fitters',
'django.contrib.admindocs',
'django.contrib.webdesign',
'django_extensions']
Installed Middleware:
('django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware')

Traceback:
File "/usr/lib/pymodules/python2.6/django/core/handlers/base.py" in get_response

  1.                     response = callback(request, _callback_args, *_callback_kwargs)
    
    File "/usr/lib/pymodules/python2.6/django/contrib/admin/options.py" in wrapper
  2.             return self.admin_site.admin_view(view)(_args, *_kwargs)
    
    File "/usr/lib/pymodules/python2.6/django/utils/decorators.py" in _wrapped_view
  3.                 response = view_func(request, _args, *_kwargs)
    
    File "/usr/lib/pymodules/python2.6/django/views/decorators/cache.py" in _wrapped_view_func
  4.     response = view_func(request, _args, *_kwargs)
    
    File "/usr/lib/pymodules/python2.6/django/contrib/admin/sites.py" in inner
  5.         return view(request, _args, *_kwargs)
    
    File "/usr/lib/pymodules/python2.6/django/utils/decorators.py" in _wrapper
  6.         return bound_func(_args, *_kwargs)
    
    File "/usr/lib/pymodules/python2.6/django/utils/decorators.py" in _wrapped_view
  7.                 response = view_func(request, _args, *_kwargs)
    
    File "/usr/lib/pymodules/python2.6/django/utils/decorators.py" in bound_func
  8.             return func(self, _args2, *_kwargs2)
    
    File "/usr/lib/pymodules/python2.6/django/contrib/admin/options.py" in changelist_view
  9.     ], context, context_instance=context_instance)
    
    File "/usr/lib/pymodules/python2.6/django/shortcuts/init.py" in render_to_response
  10. return HttpResponse(loader.render_to_string(_args, *_kwargs), **httpresponse_kwargs)
    
    File "/usr/lib/pymodules/python2.6/django/template/loader.py" in render_to_string
  11.     return t.render(context_instance)
    
    File "/usr/lib/pymodules/python2.6/django/template/base.py" in render
  12.         return self._render(context)
    
    File "/usr/lib/pymodules/python2.6/django/template/base.py" in _render
  13.     return self.nodelist.render(context)
    
    File "/usr/lib/pymodules/python2.6/django/template/base.py" in render
  14.             bits.append(self.render_node(node, context))
    
    File "/usr/lib/pymodules/python2.6/django/template/base.py" in render_node
  15.     return node.render(context)
    
    File "/usr/lib/pymodules/python2.6/django/template/loader_tags.py" in render
  16.     return compiled_parent._render(context)
    
    File "/usr/lib/pymodules/python2.6/django/template/base.py" in _render
  17.     return self.nodelist.render(context)
    
    File "/usr/lib/pymodules/python2.6/django/template/base.py" in render
  18.             bits.append(self.render_node(node, context))
    
    File "/usr/lib/pymodules/python2.6/django/template/base.py" in render_node
  19.     return node.render(context)
    
    File "/usr/lib/pymodules/python2.6/django/template/loader_tags.py" in render
  20.     return compiled_parent._render(context)
    
    File "/usr/lib/pymodules/python2.6/django/template/base.py" in _render
  21.     return self.nodelist.render(context)
    
    File "/usr/lib/pymodules/python2.6/django/template/base.py" in render
  22.             bits.append(self.render_node(node, context))
    
    File "/usr/lib/pymodules/python2.6/django/template/base.py" in render_node
  23.     return node.render(context)
    
    File "/usr/lib/pymodules/python2.6/django/template/loader_tags.py" in render
  24.         result = block.nodelist.render(context)
    
    File "/usr/lib/pymodules/python2.6/django/template/base.py" in render
  25.             bits.append(self.render_node(node, context))
    
    File "/usr/lib/pymodules/python2.6/django/template/base.py" in render_node
  26.     return node.render(context)
    
    File "/usr/lib/pymodules/python2.6/django/template/loader_tags.py" in render
  27.         result = block.nodelist.render(context)
    
    File "/usr/lib/pymodules/python2.6/django/template/base.py" in render
  28.             bits.append(self.render_node(node, context))
    
    File "/usr/lib/pymodules/python2.6/django/template/base.py" in render_node
  29.     return node.render(context)
    
    File "/usr/lib/pymodules/python2.6/django/template/base.py" in render
  30.                 dict = func(*args)
    
    File "/usr/lib/pymodules/python2.6/django/contrib/admin/templatetags/admin_list.py" in date_hierarchy
  31.             } for day in days]
    

Exception Type: AttributeError at /admin/newsletter/newsletter/
Exception Value: 'unicode' object has no attribute 'day'

Can't add contact to mailing list if workgroup not activated

If workgroup are not activated and user is not super user, it is impossible to add any contact as it filters on an empty set of workgroups.

Problem is in admin/mailinglist.py line 54

if 'subscribers' in db_field.name and not request.user.is_superuser:
        contacts_pk = request_workgroups_contacts_pk(request)
        kwargs['queryset'] = Contact.objects.filter(pk__in=contacts_pk)

Solution may be to add a control on workgroup activation.

No module named newsletter

When I install and then run the "validate" it throws the errors that the newsletter is not found

"Error: No module named newsletter"

No module named emencia when run send_newsletter

Ubuntu 11.10
Python 2.7.2+

vladka@sonic:~/PycharmProjects/divyaradio$ ./manage.py send_newsletter
Unknown command: 'send_newsletter'
Type 'manage.py help' for usage.

in django/core/management/init.py:

50 try:
51 f, path, descr = imp.find_module(part,path)
52 except ImportError,e:
53 if os.path.basename(os.getcwd()) != part:
54 print e
55 raise e

print e show this message - No module named emencia

i think you'll have to add empty init.py file in emencia and in emincia/django directory.

And Thank you for you application!

already_sent problem?

Hi Julien, it's me again.

I found a little problem in mailer.py.
If I understood well, the already_sent list is supposed to contain a list of Contact objects id. Instead, the following line in mailer.py (line 92 in current trunk)

already_sent = ContactMailingStatus.objects.filter(status=ContactMailingStatus.SENT,
            newsletter=self.newsletter).values_list('id', flat=True)

populates the list with ContactMailingStatus objects id!
I believe that the line should be written as:

already_sent = ContactMailingStatus.objects.filter(status=ContactMailingStatus.SENT,
            newsletter=self.newsletter).values_list('contact__id', flat=True)

What do you think?
Paolo

Add Tags for tracking/web viewing/unsubscribing

I have a Use Case where I want to include the unsubscribe link on some newsletters but not others. This could also apply to tracking as well as being able to view the newsletter in a browser.

It would be advantageous to allow users to include [[tracking]] , [[webview]], [[subscribe]] tags in the newsletter to have finer control.

Server hard limit does not work for multiple newsletters

When the server defaults to MAILER_HARD_LIMIT, it doesn't look at the emails it's already sent when returning credits(). Thus, if multiple newsletters are sent within an hour that collectively send more than MAILER_HARD_LIMIT, the limit will be exceeded even though none of the newsletters send more than MAILER_HARD_LIMIT individually. I think the SMTPServer.credits() function should read:

def credits(self):
    """Return how many mails the server can send"""
    if not self.mails_hour:
        allowed_mails = MAILER_HARD_LIMIT
    else:
        allowed_mails = self.mails_hour

    last_hour = datetime.now() - timedelta(hours=1)
    sent_last_hour = ContactMailingStatus.objects.filter(
        models.Q(status=ContactMailingStatus.SENT) |
        models.Q(status=ContactMailingStatus.SENT_TEST),
        newsletter__server=self,
        creation_date__gte=last_hour).count()
    return allowed_mails - sent_last_hour

Accent in newsletter title makes followinga link fail

If newsletter title has an accent (non ascii char) we get a traceback :

 File "/home/www/emencia-django-newsletter/emencia/django/newsletter/views/tracking.py", line 56, in view_newsletter_tracking_link
    url_parts.params, urlencode(query_dict), url_parts.fragment))
  File "/usr/lib/python2.6/urllib.py", line 1269, in urlencode
    v = quote_plus(str(v))
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in position 9: ordinal not in range(128)

The problem is that we have a unicode in query_dict instead of a string.

So I propose to change line 54 to encode string in utf-8

-                        'utm_campaign': newsletter.title})
+                        'utm_campaign': newsletter.title.encode('utf-8')})

Being able to avoid tracking on some links

While tracking some links, my client wishes some links not to be tracked (ethical reason or avoiding to much redirects).

Here is a patch which disable tracking of links having the offtrack class.

eg. :

 <a href="http://my.url" class="offtrack">blah<\a>

Won't be tracked.

Add gender for contacts

I propose to add gender field in Contact model. This one permits us to personalize better the text of the Newsletter.

The choises:

    GENDER_CHOICES = (
            ('u',          _('Undefined')),
            ('m',          _('Male')),
            ('f',          _('Female')),
        )

the field:

    gender = models.CharField(_('gender'), max_length=1,
            choices=GENDER_CHOICES)

For instance to greet a person we can add this piece of code in our Content:

Hello 
{% if contact.gender == "f" %}
Mrs.
{% else %}{% if contact.gender == "m" %}
Mr.
{% else %}
dear 
{% endif %}{% endif %}
 {{ contact.first_name }} {{ contact.last_name }}.

So the output can be:

  1. for female:
    Hello Mrs. Mary Louis.
  2. for male:
    Hello Mr. Barack Obama.
  3. undefined, we don't have the gender:
    Hello Orange Face.

I hope that you enjoy it!

More "visual" statistics display

Hi there,

First of all thank you for your amazing work.

It would be nice if statistics were displayed in a more "visual" fashion, like below:

example

Error when sending via Amazon SES SMTP

There is problem with sending via Amazon's SMTP. The thing is that when you try to send to an address which is blacklisted (and that unfortunately happens from time to time), it is not reported as SMTPRecipientsRefused. Instead, it is raised as normal exception with a following string in it's message: "Address blacklisted".

It would be nice if mailer could handle this since it effectively prevents the newsletter to be marked as "sent". Error statuses are not considered final and mailer tries to send it again and again with the same result.

Slug of newsletter should be unique

The slug of the newsletter model should be "unique=True", if you save multiple newsletters with the same slug you run into a lot of problems...

Lack of documentation

Can you please explain what means TRACKING_LINKS(this moment can't understand at all), TRACKING_IMAGES , what means :

Total openings
Openings on site
Total openings unique
Unknow delivery
Unsubscriptions
Openings average
Total clicked links
Total clicked links unique
Clicked links by openings
Clicked links average

Sending utf-8 e-mail

Hello, I'm having an issue when trying to send newsletter with utf-8, it raises the following error

Traceback (most recent call last):
File "./manage.py", line 11, in
execute_manager(settings)
File "/home/anderson/.virtualenvs/svbcwb/lib/python2.6/site-packages/django/core/management/init.py", line 362, in execute_manager
utility.execute()
File "/home/anderson/.virtualenvs/svbcwb/lib/python2.6/site-packages/django/core/management/init.py", line 303, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/home/anderson/.virtualenvs/svbcwb/lib/python2.6/site-packages/django/core/management/base.py", line 195, in run_from_argv
self.execute(_args, *_options.dict)
File "/home/anderson/.virtualenvs/svbcwb/lib/python2.6/site-packages/django/core/management/base.py", line 222, in execute
output = self.handle(_args, *_options)
File "/home/anderson/.virtualenvs/svbcwb/lib/python2.6/site-packages/django/core/management/base.py", line 351, in handle
return self.handle_noargs(**options)
File "/home/anderson/.virtualenvs/svbcwb/lib/python2.6/site-packages/emencia/django/newsletter/management/commands/send_newsletter.py", line 19, in handle_noargs
mailer.run()
File "/home/anderson/.virtualenvs/svbcwb/lib/python2.6/site-packages/emencia/django/newsletter/mailer.py", line 47, in run
message = self.build_message(contact)
File "/home/anderson/.virtualenvs/svbcwb/lib/python2.6/site-packages/emencia/django/newsletter/mailer.py", line 77, in build_message
message.attach(MIMEText(content_text, 'plain', 'UTF-8'))
File "/usr/lib/python2.6/email/mime/text.py", line 30, in init
self.set_payload(_text, _charset)
File "/usr/lib/python2.6/email/message.py", line 224, in set_payload
self.set_charset(charset)
File "/usr/lib/python2.6/email/message.py", line 266, in set_charset
self._payload = charset.body_encode(self._payload)
File "/usr/lib/python2.6/email/charset.py", line 387, in body_encode
return email.base64mime.body_encode(s)
File "/usr/lib/python2.6/email/base64mime.py", line 147, in encode
enc = b2a_base64(s[i:i + max_unencoded])
UnicodeEncodeError: 'ascii' codec can't encode characters in position 29-30: ordinal not in range(128)

But when I change these two lines on mailer.py

message.attach(MIMEText(content_text.encode('utf-8'), 'plain', 'UTF-8'))
message.attach(MIMEText(content_html.encode('utf-8'), 'html', 'UTF-8'))

It sends fine, but the text gets wrong encoding. I'm using SQLite3 so I don't think it should be a problem of table encoding and when the content don't have any utf-8 character but the template has, it also raises that error, so it's not database encoding for sure.

Post importation signal

Hi.

I think it could be useful to have a signal to be triggered post importation inside ContactAdmin. This will make easier the extraction of other useful information form (for example) a vCard to be saved in a auxiliary model associated with Contact (e.g. Profile).

Per hour count of messages on smtp could be improved

EDN has a per hour limit on smtp which is very useful

In my scenario I had two mailing lists to send where the client is very picky on mail schedule over time because i falls on its equipment.

One was to be send at a rate of 4400 / h and the other at 2200 / h all day long, so I set up each newsletter on it's own smtp.

I set my send_newsletter cron 4 times an hour.

so far, so good.

The problem is that I had a shift over time, putting me behind schedule.

In fact first wave is ok. But first mail is sent at 9:00 whereas last is sint at 9:15
So next time at 10:00 only few mails get sent as those sent at 9:01 are taken into account in last hour charge, so we catch up with 10:15 cron task but last mail is sent at maybe 10:30. After some time we are quite far from schedule !

I don't really see how to solve this problem right now but I may think about it :-)

subscribe / unsubscribe forms won't work if base.html does not exists

Taking the buildout and using the demo, the unsubscribe form was broken.

In fact it extends templates/newsletter/base.html which in turns extends base.html which is not found.

Either demo project (or new project) shall contain a base.html template, either it may test for its existence.

To circumvent this problem, I did :

  • add a templates directory in my app
  • add a base.html inside this directory with same content as here : http://www.djangobook.com/en/beta/chapter04/#cn330
  • change my settings.py to add :
    TEMPLATE_DIRS = (
    '/home/www/emencia-django-newsletter/demo/templates',
    )

Beautifulsoup's prettify is harmful in some cases

In some cases Internet Explorer's HTML parser will take newlines and additional spaces generated by prettify and mangle the rendered table output. In the following example the white space between the image tag and the closing td tag will corrupt the rendered layout - alas only in Internet explorer (the cell gets additional margin space)

This is what beautiful soup does:

    <td style="width: 30px; height: 111px;">
     <img width="30" height="111" src="https://pohrust.si/static/img/ur.jpg" alt="" />
    </td>

This is how IE sees it:

    <TD style="WIDTH: 30px; HEIGHT: 111px"><IMG alt="" src="https://pohrust.si/static/img/ur.jpg" width=30 height=111> </TD> </TR>

Is it possible to instruct beautiful soup to simply "not prettify"? If so I'd really like to see a setting for that.

Thanks!

Workgroups and permissions

Hi.

I'm experiencing a strange problem that seems to be connected with the workgroups permissions management.

As far as I know, there is a setting that set the workgroup to enabled/disabled. Anyway, even if I have set it to False the admin section is removed as expected, but all the contents are still visible only to admins or to users belonging to the workgroup.

I've checked the admin files and I've seen that the queryset method has been overridden to filter the queryset on a per-workgroup basis. Probably it doesn't check against the setting and still works even with workgroups disabled.

Am I missing something?

Smart_str for links header in csv report

In newsletter/views/statistics.py, line 70, use the 'smart_str' function around the link.title in order to avoid UnicodeEncodingErrors.

def header_line(links):
    link_cols = [smart_str(link.title) for link in links]

Thanks !

admin static resources

I may be missing something here, but the links to JS and CSS media in the admin backend are all relative, which means when you look at (for instance) the history and statistics pages for a particular newsletter, you're trying to draw JS pages from URLs like:

http://mywebsite.com/newsletters/statistics/some-newsletter-slug/edn/js/jquery.form.js

That's obviously wrong, right? All the "edn" static files only work with absolute URLs, from the root site.

Am I getting something wrong here?

E

Sintax error

I know it's not very important but there is a sintax error in the file admin/contact.py in line 94.
Where it says for "workgroup in request_workgroup(request):" it should say "for workgroup in request_workgroups(request):".
Like I said, it's not a big deal but it sure is anoying.

Newsletter status is updated to sent although not all contacts received it.

If my mailing list consists on 2000 contacts, the SMTP server emails per hour set to 100, it will only generate 100 emails and send them, and change the newsletters status to sent. The next time the cron is ran, it doesnt create the newsletter for the other contacts because the status is sent.

My fix (line numbers may be different) in mailer.py:
<-- removed -->

Suscribe to a mailing list

Another feature request, I suppose.

I'm looking for a simple form to let users subscribe to a mailing list within a public site. It could be something similar to how the view_mailinglist_unsubscribe view works, but with the ability to create a new contact before adding it to the selected mailing list.

Do you believe that such a feature could be useful?

Thanks.

Amazon SES Python API

Hello, first compliments for your projects : )

I'm writing to suggest a possible feature request, I think it would be quite interesting adding Amazon SES (Simple Email Service) capabilities integrating this python API:
https://github.com/pankratiev/python-amazon-ses-api

I would try, to a certain degree, to do it by myself but I'm quite new with Django and I think I
would need some guidance...

Cheers
Fabio

Draft fix for proper output log encoding of the send_newsletter management command

Wrong encoding leads to message from the cron like:

/usr/local/lib/python2.6/site-packages/MySQLdb/__init__.py:34: DeprecationWarning: the sets module is deprecated
  from sets import ImmutableSet
Starting sending newsletters...
Traceback (most recent call last):
  File "/data/www/django/djangosite/manage.py", line 11, in <module>
    execute_manager(settings)
  File "/usr/local/lib/python2.6/site-packages/django/core/management/__init__.py", line 438, in execute_manager
    utility.execute()
  File "/usr/local/lib/python2.6/site-packages/django/core/management/__init__.py", line 379, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python2.6/site-packages/django/core/management/base.py", line 191, in run_from_argv
    self.execute(*args, **options.__dict__)
  File "/usr/local/lib/python2.6/site-packages/django/core/management/base.py", line 220, in execute
    output = self.handle(*args, **options)
  File "/usr/local/lib/python2.6/site-packages/django/core/management/base.py", line 351, in handle
    return self.handle_noargs(**options)
  File "/data/www/django/djangosite/emencia/django/newsletter/management/commands/send_newsletter.py", line 23, in handle_noargs
    print 'Start emailing %s' % newsletter.title
UnicodeEncodeError: 'ascii' codec can't encode characters in position 25-31: ordinal not in range(128)

This fix is a draft, because proper encoding fix should probably use current system locale, fixing unprintable characters

--- send_newsletter.py.orig
+++ send_newsletter.py 
@@ -20,7 +20,7 @@
             mailer = Mailer(newsletter)
             if mailer.can_send:
                 if verbose:
-                    print 'Start emailing %s' % newsletter.title
+                    print 'Start emailing %s' % unicode(newsletter.title).encode('utf-8')
                 mailer.run()

         if verbose:

send_newsletter command not available

I followed install instructions but send_newsletter command is not available.

Thanks.

python manage.py send_newsletter
Unknown command: 'send_newsletter'
Type 'manage.py help' for usage.

Avoid a ValueError in SMTPServer

I propose to add a try block on the line 84 in newsletter/models.py

so, the code:

    @property
    def custom_headers(self):
    if self.headers:
        headers = {}
        for header in self.headers.split('\r\n'):
            if header:
                key, value = header.split(':')
                headers[key.strip()] = value.strip()
        return headers
    return {}

becomes:

    @property
    def custom_headers(self):
    if self.headers:
        headers = {}
        for header in self.headers.split('\r\n'):
            if header:
                try:
                    key, value = header.split(':')
                    headers[key.strip()] = value.strip()
                except ValueError:
                    pass
        return headers
    return {}

also it can be useful to validate custom_headers on saving SMTPServer.

Without this precautions, when we run send_newsletter with a wrong format of custom_headers we get:

denis@denis-linux:~/workspace/dnxproject$ python manage.py send_newsletter
Starting sending newsletters...
Start emailing New service
2 emails will be sent
- Processing First Email | first email tags
Traceback (most recent call last):
  File "manage.py", line 14, in <module>
    execute_manager(settings)
  File "/usr/local/lib/python2.6/dist-packages/django/core/management/__init__.py", line 438, in execute_manager
    utility.execute()
  File "/usr/local/lib/python2.6/dist-packages/django/core/management/__init__.py", line 379, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python2.6/dist-packages/django/core/management/base.py", line 191, in run_from_argv
    self.execute(*args, **options.__dict__)
  File "/usr/local/lib/python2.6/dist-packages/django/core/management/base.py", line 220, in execute
    output = self.handle(*args, **options)
  File "/usr/local/lib/python2.6/dist-packages/django/core/management/base.py", line 351, in handle
    return self.handle_noargs(**options)
  File "/home/denis/workspace/dnxproject/apps/emencia/django/newsletter/management/commands/send_newsletter.py", line 28, in handle_noargs
    mailer.run()
  File "/home/denis/workspace/dnxproject/apps/emencia/django/newsletter/mailer.py", line 72, in run
    message = self.build_message(contact)
  File "/home/denis/workspace/dnxproject/apps/emencia/django/newsletter/mailer.py", line 115, in build_message
    for header, value in self.newsletter.server.custom_headers.items():
  File "/home/denis/workspace/dnxproject/apps/emencia/django/newsletter/models.py", line 84, in custom_headers
    key, value = header.split(':')
ValueError: need more than 1 value to unpack

Thanks.

Only one send newsletter implementation.

Hi All,

We now have send_newsletter_continuous and send_newsletter in the core, I think this is confusing and complicates code maintainability. What are your thoughts on picking one implementation and making it the default send_newsletter.

  1. send_newsletter - is the original which with a few bug fixes and tweaks works relatively well and uses predictable cron.
  2. send_newsletter_continuous - is a new merged implementation as per https://github.com/Fantomas42/emencia-django-newsletter/pull/53#issuecomment-4193230 and from my limited understanding of it it uses threads, is not cron friendly, is persistent --- I have no experience using it however.

Bottom line I am arguing we ought to have just one manage.py command regardless of which approach is used.

pip installs wrong tagging package

When I run

pip install -e git+https://github.com/Fantomas42/emencia-django-newsletter.git#egg=emencia.django.newsletter

pip installs the package 'tagging' as a dependency. This (as far as I can tell) is an old version of the tagging library and does not work with the django 1.2.x or dev. It should be installing 'django-tagging' instead.

TypeError: can't compare offset-naive and offset-aware datetimes

I wasn't able to use ./manage.py send_newsletter with timezone aware sites.

Try with django1.4 and leave TIMEZONE setting as True and it will throw TypeError: can't compare offset-naive and offset-aware datetimes.

mailer.py line 198, in can_send
if self.newsletter.sending_date <= datetime.now()

Disposition notification aka receipt

RFC 3798 defines a receipt notification mechanism.
I implemented this for a client. It maybe useful for internal company message.

Of course you'll have to provide a mechanism to read receipt notification mail if it as to get into statistics.

Don't know if it's worth including.

Escaped HTML in e-mail body

Hi,

Whenever I send an e-mail, the first and last bits of the e-mail (i.e. viewing the e-mail on the website or unsubscribing) is not rendered as HTML. The newsletter content is shown as properly rendered HTML. This is how an e-mail would look like:

<div id="links"> <a name="links"></a> <p> If you cannot see this email, <a href="http://localhost:8000/newsletter/html-test/1-7fb23b4e5ab0e9eeff80/">click here</a>. </p> </div>

Newsletter HTML content goes here!

<div id="unsubscription"> <a name="unsubscription"></a> <p> For unsubscribing to this mailing list, <a href="http://localhost:8000/newsletter/mailing/unsubscribe/html-test/1-7fb23b4e5ab0e9eeff80/">click here</a>. </p> </div> <img src="http://localhost:8000/newsletter/tracking/newsletter/html-test/1-7fb23b4e5ab0e9eeff80.jpg" width="1" height="1" />

I tried reading all the docs, double-checked that all the dependencies are properly installed, nothing worked so far.

Thanks!

Update translations

Please update translations from Transifex -- it's been a little more than a year and at least German translation is buggy. Thanks.

Contact mailing statuses and Links table not showing up in admin

I have installed the app locally on windows and everything works good. But when i have tried to install it on production server on ubuntu using

pip install -e git://github.com/emencia/emencia-django-newsletter.git#egg=emencia.django.newsletter

Then a weird problem occur that the Contact mailing statuses and Links table are not showing up in admin, rest of the four tables are there. What i have done wrong?

Using TLS and gmail

Hello, I was having a problem sending newsletter using a gmail account and I had to change the tls implementation so it could work. On mailer.py I changed the smtp_connect method to:

def smtp_connect(self):
    """Make a connection to the SMTP"""
    server = self.newsletter.server
    self.smtp = SMTP(server.host, int(server.port))
    if server.tls:
        #having to do this because of google
        self.smtp.ehlo()
        self.smtp.starttls()
        self.smtp.ehlo()
    if server.user or server.password:
        self.smtp.login(server.user.encode('utf-8'), server.password.encode('utf-8'))

I don't couldn't try on any other TLS, so I'm not sure if it will work for other situations

ZeroDivisionError

A ZeroDivisionError could happen @ get_newsletter_opening_statistics view @ utils/statistics.py, if all newsletter's mailling list's subscribers have unsubscribed.

Small fix for proper "To" field encoding

--- emencia/django/newsletter/models.py.orig
+++ emencia/django/newsletter/models.py
@@ -126,7 +126,7 @@

     def mail_format(self):
         if self.first_name and self.last_name:
-            return '%s %s <%s>' % (self.last_name, self.first_name, self.email)
+            return '"%s %s" <%s>' % (unicode(self.last_name).encode('utf-8'), unicode(self.first_name).encode('utf-8'), unicode(self.email).encode('utf-8'))
         return self.email
     mail_format.short_description = _('mail format')

SMTP issues + locale

In my setup (Python 2.6 on Linux) smtplib requires server port to be passed as an integer but Django keeps passing it as a long integer even if in the SMTPServer model port is defined as integer.
I fixed the issue changing self.port to int(self.port) in models.py (SMTPServer.connection_valid) and changing server.port to int(server.port) in mailer.py (Mailer.smtp_connect).

I fixed other SMTP login issues by encoding username and password to 'utf-8' before passing them to smtplib:

in models.py (SMTPServer.connection_valid) I changed smtp.login(self.user, self.password) to smtp.login(self.user.encode('utf-8'), self.password.encode('utf-8'))

in mailer.py (Mailer.smtp_connect) I changed self.smtp.login(server.user, server.password) to smtp.login(server.user.encode('utf-8'), server.password.encode('utf-8'))

I don't know if these problems occur only on my machine, so I'm not submitting any patch.
But if there's anyone in my situation, maybe this helps.

Last, a quick question (off-topic). I'm quite new to django. I'm translating the app to Italian (and I'll submit my language file to Transifex as soon as I'm done with it). But... where do I have to save the locale directory? I tried to add it to the installation path but it doesn't work.

Thank you very much for sharing the app. I'm beginning to use it and it looks very promising and it's saving me a lot of time.

Paolo

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.