diff --git a/emencia/__init__.py b/emencia/__init__.py new file mode 100644 index 00000000..f48ad105 --- /dev/null +++ b/emencia/__init__.py @@ -0,0 +1,6 @@ +# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages +try: + __import__('pkg_resources').declare_namespace(__name__) +except ImportError: + from pkgutil import extend_path + __path__ = extend_path(__path__, __name__) diff --git a/emencia/django/__init__.py b/emencia/django/__init__.py new file mode 100644 index 00000000..f48ad105 --- /dev/null +++ b/emencia/django/__init__.py @@ -0,0 +1,6 @@ +# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages +try: + __import__('pkg_resources').declare_namespace(__name__) +except ImportError: + from pkgutil import extend_path + __path__ = extend_path(__path__, __name__) diff --git a/emencia/django/newsletter/__init__.py b/emencia/django/newsletter/__init__.py new file mode 100644 index 00000000..8cd2bd61 --- /dev/null +++ b/emencia/django/newsletter/__init__.py @@ -0,0 +1,8 @@ +"""emencia.django.newsletter""" +__version__ = '0.3.dev' +__license__ = 'BSD License' + +__author__ = 'Fantomas42' +__email__ = 'fantomas42@gmail.com' + +__url__ = 'http://emencia.fr/' diff --git a/emencia/django/newsletter/admin/__init__.py b/emencia/django/newsletter/admin/__init__.py new file mode 100644 index 00000000..9105faf4 --- /dev/null +++ b/emencia/django/newsletter/admin/__init__.py @@ -0,0 +1,35 @@ +"""Admin for emencia.django.newsletter""" +from django.contrib import admin +from django.conf import settings + +from emencia.django.newsletter.models import Link +from emencia.django.newsletter.models import Contact +from emencia.django.newsletter.models import WorkGroup +from emencia.django.newsletter.models import SMTPServer +from emencia.django.newsletter.models import Newsletter +from emencia.django.newsletter.models import MailingList +from emencia.django.newsletter.models import ContactMailingStatus + +from emencia.django.newsletter.settings import USE_WORKGROUPS +from emencia.django.newsletter.admin.contact import ContactAdmin +from emencia.django.newsletter.admin.workgroup import WorkGroupAdmin +from emencia.django.newsletter.admin.newsletter import NewsletterAdmin +from emencia.django.newsletter.admin.smtpserver import SMTPServerAdmin +from emencia.django.newsletter.admin.mailinglist import MailingListAdmin + + +admin.site.register(Contact, ContactAdmin) +admin.site.register(SMTPServer, SMTPServerAdmin) +admin.site.register(Newsletter, NewsletterAdmin) +admin.site.register(MailingList, MailingListAdmin) + +if USE_WORKGROUPS: + admin.site.register(WorkGroup, WorkGroupAdmin) + + +class LinkAdmin(admin.ModelAdmin): + list_display = ('title', 'url', 'creation_date') + +if settings.DEBUG: + admin.site.register(Link, LinkAdmin) + admin.site.register(ContactMailingStatus) diff --git a/emencia/django/newsletter/admin/contact.py b/emencia/django/newsletter/admin/contact.py new file mode 100644 index 00000000..3d5de5a3 --- /dev/null +++ b/emencia/django/newsletter/admin/contact.py @@ -0,0 +1,184 @@ +"""ModelAdmin for Contact""" +import StringIO +from django.conf import settings +from datetime import datetime + +from django.contrib import admin +from django.dispatch import Signal +from django.conf.urls.defaults import url +from django.conf.urls.defaults import patterns +from django.core.urlresolvers import reverse +from django.shortcuts import render_to_response +from django.template import RequestContext +from django.utils.translation import ugettext_lazy as _ +from django.http import HttpResponseRedirect +from django.contrib.admin.views.main import ChangeList +from django.db import DatabaseError + +from emencia.django.newsletter.models import MailingList +from emencia.django.newsletter.settings import USE_WORKGROUPS +from emencia.django.newsletter.utils.importation import import_dispatcher +from emencia.django.newsletter.utils.workgroups import request_workgroups +from emencia.django.newsletter.utils.workgroups import request_workgroups_contacts_pk +from emencia.django.newsletter.utils.vcard import vcard_contacts_export_response +from emencia.django.newsletter.utils.excel import ExcelResponse + + +contacts_imported = Signal(providing_args=['source', 'type']) + + +class ContactAdmin(admin.ModelAdmin): + date_hierarchy = 'creation_date' + list_display = ('email', 'first_name', 'last_name', 'tags', 'tester', 'subscriber', + 'valid', 'total_subscriptions', 'creation_date', 'related_object_admin') + list_filter = ('subscriber', 'valid', 'tester', 'creation_date', 'modification_date') + search_fields = ('email', 'first_name', 'last_name', 'tags') + fieldsets = ((None, {'fields': ('email', 'first_name', 'last_name')}), + (None, {'fields': ('tags',)}), + (_('Status'), {'fields': ('subscriber', 'valid', 'tester')}), + (_('Advanced'), {'fields': ('object_id', 'content_type'), + 'classes': ('collapse',)}), + ) + actions = ['create_mailinglist', 'export_vcard', 'export_excel'] + actions_on_top = False + actions_on_bottom = True + + def queryset(self, request): + queryset = super(ContactAdmin, self).queryset(request) + if not request.user.is_superuser and USE_WORKGROUPS: + contacts_pk = request_workgroups_contacts_pk(request) + queryset = queryset.filter(pk__in=contacts_pk) + return queryset + + def save_model(self, request, contact, form, change): + workgroups = [] + if not contact.pk and not request.user.is_superuser \ + and USE_WORKGROUPS: + workgroups = request_workgroups(request) + contact.save() + for workgroup in workgroups: + workgroup.contacts.add(contact) + + def related_object_admin(self, contact): + """Display link to related object's admin""" + if contact.content_type and contact.object_id: + admin_url = reverse('admin:%s_%s_change' % (contact.content_type.app_label, + contact.content_type.model), + args=(contact.object_id,)) + return '%s: %s' % (contact.content_type.model.capitalize(), + admin_url, + contact.content_object.__unicode__()) + return _('No relative object') + related_object_admin.allow_tags = True + related_object_admin.short_description = _('Related object') + + def total_subscriptions(self, contact): + """Display user subscriptions to unsubscriptions""" + subscriptions = contact.subscriptions().count() + unsubscriptions = contact.unsubscriptions().count() + return '%s / %s' % (subscriptions - unsubscriptions, subscriptions) + total_subscriptions.short_description = _('Total subscriptions') + + def export_vcard(self, request, queryset, export_name=''): + """Export selected contact in VCard""" + return vcard_contacts_export_response(queryset) + export_vcard.short_description = _('Export contacts as VCard') + + def export_excel(self, request, queryset, export_name=''): + """Export selected contact in Excel""" + if not export_name: + export_name = 'contacts_edn_%s' % datetime.now().strftime('%d-%m-%Y') + return ExcelResponse(queryset, export_name) + export_excel.short_description = _('Export contacts in Excel') + + def create_mailinglist(self, request, queryset): + """Create a mailing list from selected contact""" + when = str(datetime.now()).split('.')[0] + new_mailing = MailingList(name=_('New mailinglist at %s') % when, + description=_('New mailing list created in admin at %s') % when) + new_mailing.save() + + if 'lite' in settings.DATABASES['default']['ENGINE']: + self.message_user(request, _('SQLite3 or a SpatialLite database type detected, ' \ + 'please note you will be limited to 999 contacts ' \ + 'per mailing list.')) + try: + new_mailing.subscribers = queryset.all() + except DatabaseError: + new_mailing.subscribers = queryset.none() + + if not request.user.is_superuser and USE_WORKGROUPS: + for workgroup in request_workgroups(request): + workgroup.mailinglists.add(new_mailing) + + self.message_user(request, _('%s succesfully created.') % new_mailing) + return HttpResponseRedirect(reverse('admin:newsletter_mailinglist_change', + args=[new_mailing.pk])) + create_mailinglist.short_description = _('Create a mailinglist') + + def importation(self, request): + """Import contacts from a VCard""" + opts = self.model._meta + + if request.POST: + source = request.FILES.get('source') or \ + StringIO.StringIO(request.POST.get('source', '')) + if not request.user.is_superuser and USE_WORKGROUPS: + workgroups = request_workgroups(request) + else: + workgroups = [] + inserted = import_dispatcher(source, request.POST['type'], + workgroups) + if inserted: + contacts_imported.send(sender=self, source=source, + type=request.POST['type']) + + self.message_user(request, _('%s contacts succesfully imported.') % inserted) + + context = {'title': _('Contact importation'), + 'opts': opts, + 'root_path': self.admin_site.root_path, + 'app_label': opts.app_label} + + return render_to_response('newsletter/contact_import.html', + context, RequestContext(request)) + + def filtered_request_queryset(self, request): + """Return queryset filtered by the admin list view""" + cl = ChangeList(request, self.model, self.list_display, + self.list_display_links, self.list_filter, + self.date_hierarchy, self.search_fields, + self.list_select_related, self.list_per_page, + self.list_editable, self) + return cl.get_query_set() + + def creation_mailinglist(self, request): + """Create a mailing list form the filtered contacts""" + return self.create_mailinglist(request, self.filtered_request_queryset(request)) + + def exportation_vcard(self, request): + """Export filtered contacts in VCard""" + return self.export_vcard(request, self.filtered_request_queryset(request), + 'contacts_edn_%s' % datetime.now().strftime('%d-%m-%Y')) + + def exportation_excel(self, request): + """Export filtered contacts in Excel""" + return self.export_excel(request, self.filtered_request_queryset(request), + 'contacts_edn_%s' % datetime.now().strftime('%d-%m-%Y')) + + def get_urls(self): + urls = super(ContactAdmin, self).get_urls() + my_urls = patterns('', + url(r'^import/$', + self.admin_site.admin_view(self.importation), + name='newsletter_contact_import'), + url(r'^create_mailinglist/$', + self.admin_site.admin_view(self.creation_mailinglist), + name='newsletter_contact_create_mailinglist'), + url(r'^export/vcard/$', + self.admin_site.admin_view(self.exportation_vcard), + name='newsletter_contact_export_vcard'), + url(r'^export/excel/$', + self.admin_site.admin_view(self.exportation_excel), + name='newsletter_contact_export_excel'),) + return my_urls + urls diff --git a/emencia/django/newsletter/admin/mailinglist.py b/emencia/django/newsletter/admin/mailinglist.py new file mode 100644 index 00000000..5f143720 --- /dev/null +++ b/emencia/django/newsletter/admin/mailinglist.py @@ -0,0 +1,125 @@ +"""ModelAdmin for MailingList""" +from datetime import datetime + +from django.contrib import admin +from django.conf.urls.defaults import url +from django.conf.urls.defaults import patterns +from django.utils.encoding import smart_str +from django.core.urlresolvers import reverse +from django.shortcuts import get_object_or_404 +from django.utils.translation import ugettext_lazy as _ +from django.http import HttpResponseRedirect + +from emencia.django.newsletter.models import Contact +from emencia.django.newsletter.models import MailingList +from emencia.django.newsletter.settings import USE_WORKGROUPS +from emencia.django.newsletter.utils.workgroups import request_workgroups +from emencia.django.newsletter.utils.workgroups import request_workgroups_contacts_pk +from emencia.django.newsletter.utils.workgroups import request_workgroups_mailinglists_pk +from emencia.django.newsletter.utils.vcard import vcard_contacts_export_response +from emencia.django.newsletter.utils.excel import ExcelResponse + + +class MailingListAdmin(admin.ModelAdmin): + date_hierarchy = 'creation_date' + list_display = ('creation_date', 'name', 'description', + 'subscribers_count', 'unsubscribers_count', + 'exportation_links') + list_editable = ('name', 'description') + list_filter = ('creation_date', 'modification_date') + search_fields = ('name', 'description',) + filter_horizontal = ['subscribers', 'unsubscribers'] + fieldsets = ((None, {'fields': ('name', 'description',)}), + (None, {'fields': ('subscribers',)}), + (None, {'fields': ('unsubscribers',)}), + ) + actions = ['merge_mailinglist'] + actions_on_top = False + actions_on_bottom = True + + def queryset(self, request): + queryset = super(MailingListAdmin, self).queryset(request) + if not request.user.is_superuser and USE_WORKGROUPS: + mailinglists_pk = request_workgroups_mailinglists_pk(request) + queryset = queryset.filter(pk__in=mailinglists_pk) + return queryset + + def save_model(self, request, mailinglist, form, change): + workgroups = [] + if not mailinglist.pk and not request.user.is_superuser \ + and USE_WORKGROUPS: + workgroups = request_workgroups(request) + mailinglist.save() + for workgroup in workgroups: + workgroup.mailinglists.add(mailinglist) + + def formfield_for_manytomany(self, db_field, request, **kwargs): + if 'subscribers' in db_field.name and not request.user.is_superuser \ + and USE_WORKGROUPS: + contacts_pk = request_workgroups_contacts_pk(request) + kwargs['queryset'] = Contact.objects.filter(pk__in=contacts_pk) + return super(MailingListAdmin, self).formfield_for_manytomany( + db_field, request, **kwargs) + + def merge_mailinglist(self, request, queryset): + """Merge multiple mailing list""" + if queryset.count() == 1: + self.message_user(request, _('Please select a least 2 mailing list.')) + return None + + subscribers = {} + unsubscribers = {} + for ml in queryset: + for contact in ml.subscribers.all(): + subscribers[contact] = '' + for contact in ml.unsubscribers.all(): + unsubscribers[contact] = '' + + when = str(datetime.now()).split('.')[0] + new_mailing = MailingList(name=_('Merging list at %s') % when, + description=_('Mailing list created by merging at %s') % when) + new_mailing.save() + new_mailing.subscribers = subscribers.keys() + new_mailing.unsubscribers = unsubscribers.keys() + + if not request.user.is_superuser and USE_WORKGROUPS: + for workgroup in request_workgroups(request): + workgroup.mailinglists.add(new_mailing) + + self.message_user(request, _('%s succesfully created by merging.') % new_mailing) + return HttpResponseRedirect(reverse('admin:newsletter_mailinglist_change', + args=[new_mailing.pk])) + merge_mailinglist.short_description = _('Merge selected mailinglists') + + def exportation_links(self, mailinglist): + """Display links for exportation""" + return u'%s / %s' % ( + reverse('admin:newsletter_mailinglist_export_excel', + args=[mailinglist.pk]), _('Excel'), + reverse('admin:newsletter_mailinglist_export_vcard', + args=[mailinglist.pk]), _('VCard')) + exportation_links.allow_tags = True + exportation_links.short_description = _('Export') + + def exportion_vcard(self, request, mailinglist_id): + """Export subscribers in the mailing in VCard""" + mailinglist = get_object_or_404(MailingList, pk=mailinglist_id) + name = 'contacts_%s' % smart_str(mailinglist.name) + return vcard_contacts_export_response(mailinglist.subscribers.all(), name) + + def exportion_excel(self, request, mailinglist_id): + """Export subscribers in the mailing in Excel""" + mailinglist = get_object_or_404(MailingList, pk=mailinglist_id) + name = 'contacts_%s' % smart_str(mailinglist.name) + return ExcelResponse(mailinglist.subscribers.all(), name) + + def get_urls(self): + urls = super(MailingListAdmin, self).get_urls() + my_urls = patterns('', + url(r'^export/vcard/(?P\d+)/$', + self.admin_site.admin_view(self.exportion_vcard), + name='newsletter_mailinglist_export_vcard'), + url(r'^export/excel/(?P\d+)/$', + self.admin_site.admin_view(self.exportion_excel), + name='newsletter_mailinglist_export_excel')) + return my_urls + urls diff --git a/emencia/django/newsletter/admin/newsletter.py b/emencia/django/newsletter/admin/newsletter.py new file mode 100644 index 00000000..2574015e --- /dev/null +++ b/emencia/django/newsletter/admin/newsletter.py @@ -0,0 +1,185 @@ +"""ModelAdmin for Newsletter""" +from HTMLParser import HTMLParseError + +from django import forms +from django.db.models import Q +from django.contrib import admin +from django.utils.translation import ugettext_lazy as _ + +from emencia.django.newsletter.models import Contact +from emencia.django.newsletter.models import Newsletter +from emencia.django.newsletter.models import Attachment +from emencia.django.newsletter.models import MailingList +from emencia.django.newsletter.mailer import Mailer +from emencia.django.newsletter.settings import USE_TINYMCE +from emencia.django.newsletter.settings import USE_WORKGROUPS +try: + CAN_USE_PREMAILER = True + from emencia.django.newsletter.utils.premailer import Premailer + from emencia.django.newsletter.utils.premailer import PremailerError +except ImportError: + CAN_USE_PREMAILER = False +from emencia.django.newsletter.utils.workgroups import request_workgroups +from emencia.django.newsletter.utils.workgroups import request_workgroups_contacts_pk +from emencia.django.newsletter.utils.workgroups import request_workgroups_newsletters_pk +from emencia.django.newsletter.utils.workgroups import request_workgroups_mailinglists_pk + + +class AttachmentAdminInline(admin.TabularInline): + model = Attachment + extra = 1 + fieldsets = ((None, {'fields': (('title', 'file_attachment'))}),) + + +class BaseNewsletterAdmin(admin.ModelAdmin): + date_hierarchy = 'creation_date' + list_display = ('title', 'mailing_list', 'server', 'status', + 'sending_date', 'creation_date', 'modification_date', + 'historic_link', 'statistics_link') + list_filter = ('status', 'sending_date', 'creation_date', 'modification_date') + search_fields = ('title', 'content', 'header_sender', 'header_reply') + filter_horizontal = ['test_contacts'] + fieldsets = ((None, {'fields': ('title', 'content',)}), + (_('Receivers'), {'fields': ('mailing_list', 'test_contacts',)}), + (_('Sending'), {'fields': ('sending_date', 'status',)}), + (_('Miscellaneous'), {'fields': ('server', 'header_sender', + 'header_reply', 'slug'), + 'classes': ('collapse',)}), + ) + prepopulated_fields = {'slug': ('title',)} + inlines = (AttachmentAdminInline,) + actions = ['send_mail_test', 'make_ready_to_send', 'make_cancel_sending'] + actions_on_top = False + actions_on_bottom = True + + def get_actions(self, request): + actions = super(BaseNewsletterAdmin, self).get_actions(request) + if not request.user.has_perm('newsletter.can_change_status'): + del actions['make_ready_to_send'] + del actions['make_cancel_sending'] + return actions + + def queryset(self, request): + queryset = super(BaseNewsletterAdmin, self).queryset(request) + if not request.user.is_superuser and USE_WORKGROUPS: + newsletters_pk = request_workgroups_newsletters_pk(request) + queryset = queryset.filter(pk__in=newsletters_pk) + return queryset + + def formfield_for_foreignkey(self, db_field, request, **kwargs): + if db_field.name == 'mailing_list' and \ + not request.user.is_superuser and USE_WORKGROUPS: + mailinglists_pk = request_workgroups_mailinglists_pk(request) + kwargs['queryset'] = MailingList.objects.filter(pk__in=mailinglists_pk) + return db_field.formfield(**kwargs) + return super(BaseNewsletterAdmin, self).formfield_for_foreignkey( + db_field, request, **kwargs) + + def formfield_for_choice_field(self, db_field, request, **kwargs): + if db_field.name == 'status' and \ + not request.user.has_perm('newsletter.can_change_status'): + kwargs['choices'] = ((Newsletter.DRAFT, _('Default')),) + return db_field.formfield(**kwargs) + return super(BaseNewsletterAdmin, self).formfield_for_choice_field( + db_field, request, **kwargs) + + def formfield_for_manytomany(self, db_field, request, **kwargs): + if db_field.name == 'test_contacts': + queryset = Contact.objects.filter(tester=True) + if not request.user.is_superuser and USE_WORKGROUPS: + contacts_pk = request_workgroups_contacts_pk(request) + queryset = queryset.filter(pk__in=contacts_pk) + kwargs['queryset'] = queryset + return super(BaseNewsletterAdmin, self).formfield_for_manytomany( + db_field, request, **kwargs) + + def save_model(self, request, newsletter, form, change): + workgroups = [] + if not newsletter.pk and not request.user.is_superuser \ + and USE_WORKGROUPS: + workgroups = request_workgroups(request) + + if newsletter.content.startswith('http://'): + if CAN_USE_PREMAILER: + try: + premailer = Premailer(newsletter.content.strip()) + newsletter.content = premailer.transform() + except PremailerError: + self.message_user(request, _('Unable to download HTML, due to errors within.')) + else: + self.message_user(request, _('Please install lxml for parsing an URL.')) + if not request.user.has_perm('newsletter.can_change_status'): + newsletter.status = form.initial.get('status', Newsletter.DRAFT) + + newsletter.save() + + for workgroup in workgroups: + workgroup.newsletters.add(newsletter) + + def historic_link(self, newsletter): + """Display link for historic""" + if newsletter.contactmailingstatus_set.count(): + return u'%s' % (newsletter.get_historic_url(), _('View historic')) + return _('Not available') + historic_link.allow_tags = True + historic_link.short_description = _('Historic') + + def statistics_link(self, newsletter): + """Display link for statistics""" + if newsletter.status == Newsletter.SENDING or \ + newsletter.status == Newsletter.SENT: + return u'%s' % (newsletter.get_statistics_url(), _('View statistics')) + return _('Not available') + statistics_link.allow_tags = True + statistics_link.short_description = _('Statistics') + + def send_mail_test(self, request, queryset): + """Send newsletter in test""" + for newsletter in queryset: + if newsletter.test_contacts.count(): + mailer = Mailer(newsletter, test=True) + try: + mailer.run() + except HTMLParseError: + self.message_user(request, _('Unable send newsletter, due to errors within HTML.')) + continue + self.message_user(request, _('%s succesfully sent.') % newsletter) + else: + self.message_user(request, _('No test contacts assigned for %s.') % newsletter) + send_mail_test.short_description = _('Send test email') + + def make_ready_to_send(self, request, queryset): + """Make newsletter ready to send""" + queryset = queryset.filter(status=Newsletter.DRAFT) + for newsletter in queryset: + newsletter.status = Newsletter.WAITING + newsletter.save() + self.message_user(request, _('%s newletters are ready to send') % queryset.count()) + make_ready_to_send.short_description = _('Make ready to send') + + def make_cancel_sending(self, request, queryset): + """Cancel the sending of newsletters""" + queryset = queryset.filter(Q(status=Newsletter.WAITING) | + Q(status=Newsletter.SENDING)) + for newsletter in queryset: + newsletter.status = Newsletter.CANCELED + newsletter.save() + self.message_user(request, _('%s newletters are cancelled') % queryset.count()) + make_cancel_sending.short_description = _('Cancel the sending') + + +if USE_TINYMCE: + from tinymce.widgets import TinyMCE + + class NewsletterTinyMCEForm(forms.ModelForm): + content = forms.CharField( + widget=TinyMCE(attrs={'cols': 150, 'rows': 80})) + + class Meta: + model = Newsletter + + class NewsletterAdmin(BaseNewsletterAdmin): + form = NewsletterTinyMCEForm +else: + class NewsletterAdmin(BaseNewsletterAdmin): + pass diff --git a/emencia/django/newsletter/admin/smtpserver.py b/emencia/django/newsletter/admin/smtpserver.py new file mode 100644 index 00000000..a69687c2 --- /dev/null +++ b/emencia/django/newsletter/admin/smtpserver.py @@ -0,0 +1,57 @@ +"""ModelAdmin for SMTPServer""" +from django import forms +from django.contrib import admin +from django.core.exceptions import ValidationError +from django.utils.translation import ugettext_lazy as _ + +from emencia.django.newsletter.models import SMTPServer + + +class SMTPServerAdminForm(forms.ModelForm): + """Form ofr SMTPServer with custom validation""" + + def clean_headers(self): + """Check if the headers are well formated""" + for line in self.cleaned_data['headers'].splitlines(): + elems = line.split(':') + if len(elems) < 2: + raise ValidationError(_('Invalid syntax, do not forget the ":".')) + if len(elems) > 2: + raise ValidationError(_('Invalid syntax, several assignments by line.')) + + return self.cleaned_data['headers'] + + class Meta: + model = SMTPServer + + +class SMTPServerAdmin(admin.ModelAdmin): + form = SMTPServerAdminForm + list_display = ('name', 'host', 'port', 'user', 'tls', 'mails_hour',) + list_filter = ('tls',) + search_fields = ('name', 'host', 'user') + fieldsets = ((None, {'fields': ('name', )}), + (_('Configuration'), {'fields': ('host', 'port', + 'user', 'password', 'tls')}), + (_('Miscellaneous'), {'fields': ('mails_hour', 'headers'), + 'classes': ('collapse', )}), + ) + actions = ['check_connections'] + actions_on_top = False + actions_on_bottom = True + + def check_connections(self, request, queryset): + """Check the SMTP connection""" + message = '%s connection %s' + for server in queryset: + try: + smtp = server.connect() + if smtp: + status = 'OK' + smtp.quit() + else: + status = 'KO' + except: + status = 'KO' + self.message_user(request, message % (server.__unicode__(), status)) + check_connections.short_description = _('Check connection') diff --git a/emencia/django/newsletter/admin/workgroup.py b/emencia/django/newsletter/admin/workgroup.py new file mode 100644 index 00000000..bb9a1dd0 --- /dev/null +++ b/emencia/django/newsletter/admin/workgroup.py @@ -0,0 +1,26 @@ +"""ModelAdmin for WorkGroup""" +from django.contrib import admin +from django.utils.translation import ugettext_lazy as _ + + +class WorkGroupAdmin(admin.ModelAdmin): + list_display = ('name', 'group', 'contacts_length', + 'mailinglists_length', 'newsletters_length') + fieldsets = ((None, {'fields': ('name', 'group')}), + (None, {'fields': ('contacts', 'mailinglists', 'newsletters')}), + ) + filter_horizontal = ['contacts', 'mailinglists', 'newsletters'] + actions_on_top = False + actions_on_bottom = True + + def contacts_length(self, workgroup): + return workgroup.contacts.count() + contacts_length.short_description = _('Contacts length') + + def mailinglists_length(self, workgroup): + return workgroup.mailinglists.count() + mailinglists_length.short_description = _('Mailing List length') + + def newsletters_length(self, workgroup): + return workgroup.newsletters.count() + newsletters_length.short_description = _('Newsletter length') diff --git a/newsletter/__init__.py b/emencia/django/newsletter/cmsplugin_newsletter/__init__.py similarity index 100% rename from newsletter/__init__.py rename to emencia/django/newsletter/cmsplugin_newsletter/__init__.py diff --git a/emencia/django/newsletter/cmsplugin_newsletter/cms_plugins.py b/emencia/django/newsletter/cmsplugin_newsletter/cms_plugins.py new file mode 100644 index 00000000..790199ee --- /dev/null +++ b/emencia/django/newsletter/cmsplugin_newsletter/cms_plugins.py @@ -0,0 +1,38 @@ +"""Plugins for CMS""" +from django.utils.translation import ugettext_lazy as _ + +from cms.plugin_base import CMSPluginBase +from cms.plugin_pool import plugin_pool + +from emencia.django.newsletter.cmsplugin_newsletter import settings +from emencia.django.newsletter.cmsplugin_newsletter.models import SubscriptionFormPlugin +from emencia.django.newsletter.forms import MailingListSubscriptionForm + + +class CMSSubscriptionFormPlugin(CMSPluginBase): + module = _('newsletter') + model = SubscriptionFormPlugin + name = _('Subscription Form') + render_template = 'newsletter/cms/subscription_form.html' + text_enabled = False + admin_preview = False + + def render(self, context, instance, placeholder): + request = context['request'] + if request.method == "POST" and (settings.FORM_NAME in request.POST.keys()): + form = MailingListSubscriptionForm(data=request.POST) + if form.is_valid(): + form.save(instance.mailing_list) + form.saved = True + else: + form = MailingListSubscriptionForm() + context.update({ + 'object': instance, + 'form': form, + 'form_name': settings.FORM_NAME, + 'placeholder': placeholder, + }) + return context + + +plugin_pool.register_plugin(CMSSubscriptionFormPlugin) diff --git a/emencia/django/newsletter/cmsplugin_newsletter/models.py b/emencia/django/newsletter/cmsplugin_newsletter/models.py new file mode 100644 index 00000000..37e88252 --- /dev/null +++ b/emencia/django/newsletter/cmsplugin_newsletter/models.py @@ -0,0 +1,19 @@ +"""Models of Emencia CMS Plugins""" +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from cms.models import CMSPlugin + +from emencia.django.newsletter.models import MailingList + + +class SubscriptionFormPlugin(CMSPlugin): + """CMS Plugin for susbcribing to a mailing list""" + title = models.CharField(_('title'), max_length=100, blank=True) + show_description = models.BooleanField(_('show description'), default=True, + help_text=_('Show the mailing list\'s description.')) + mailing_list = models.ForeignKey(MailingList, verbose_name=_('mailing list'), + help_text=_('Mailing List to subscribe to.')) + + def __unicode__(self): + return self.mailing_list.name diff --git a/emencia/django/newsletter/cmsplugin_newsletter/settings.py b/emencia/django/newsletter/cmsplugin_newsletter/settings.py new file mode 100644 index 00000000..a19b5bd8 --- /dev/null +++ b/emencia/django/newsletter/cmsplugin_newsletter/settings.py @@ -0,0 +1,4 @@ +"""Settings for emencia.django.newsletter.cmsplugin_newsletter""" +from django.conf import settings + +FORM_NAME = getattr(settings, 'SUBSCRIPTION_FORM_NAME', 'cms_subscription_form_plugin') diff --git a/emencia/django/newsletter/forms.py b/emencia/django/newsletter/forms.py new file mode 100644 index 00000000..72647116 --- /dev/null +++ b/emencia/django/newsletter/forms.py @@ -0,0 +1,56 @@ + +"""Forms for emencia.django.newsletter""" +from django import forms +from django.utils.translation import ugettext_lazy as _ + +from emencia.django.newsletter.models import Contact +from emencia.django.newsletter.models import MailingList + + +class MailingListSubscriptionForm(forms.ModelForm): + """Form for subscribing to a mailing list""" + # Notes : This form will not check the uniquess of + # the 'email' field, by defining it explictly and setting + # it the Meta.exclude list, for allowing registration + # to a mailing list even if the contact already exists. + # Then the contact is always added to the subscribers field + # of the mailing list because it will be cleaned with no + # double. + + email = forms.EmailField(label=_('Email'), max_length=75) + + def save(self, mailing_list): + data = self.cleaned_data + contact, created = Contact.objects.get_or_create( + email=data['email'], + defaults={'first_name': data['first_name'], + 'last_name': data['last_name']}) + + mailing_list.subscribers.add(contact) + mailing_list.unsubscribers.remove(contact) + + class Meta: + model = Contact + fields = ('first_name', 'last_name') + exclude = ('email',) + + +class AllMailingListSubscriptionForm(MailingListSubscriptionForm): + """Form for subscribing to all mailing list""" + + mailing_lists = forms.ModelMultipleChoiceField( + queryset=MailingList.objects.all(), + initial=[obj.id for obj in MailingList.objects.all()], + label=_('Mailing lists'), + widget=forms.CheckboxSelectMultiple()) + + def save(self, mailing_list): + data = self.cleaned_data + contact, created = Contact.objects.get_or_create( + email=data['email'], + defaults={'first_name': data['first_name'], + 'last_name': data['last_name']}) + + for mailing_list in data['mailing_lists']: + mailing_list.subscribers.add(contact) + mailing_list.unsubscribers.remove(contact) diff --git a/newsletter/locale/cs/LC_MESSAGES/django.mo b/emencia/django/newsletter/locale/cs/LC_MESSAGES/django.mo similarity index 100% rename from newsletter/locale/cs/LC_MESSAGES/django.mo rename to emencia/django/newsletter/locale/cs/LC_MESSAGES/django.mo diff --git a/newsletter/locale/cs/LC_MESSAGES/django.po b/emencia/django/newsletter/locale/cs/LC_MESSAGES/django.po similarity index 100% rename from newsletter/locale/cs/LC_MESSAGES/django.po rename to emencia/django/newsletter/locale/cs/LC_MESSAGES/django.po diff --git a/newsletter/locale/de/LC_MESSAGES/django.mo b/emencia/django/newsletter/locale/de/LC_MESSAGES/django.mo similarity index 100% rename from newsletter/locale/de/LC_MESSAGES/django.mo rename to emencia/django/newsletter/locale/de/LC_MESSAGES/django.mo diff --git a/newsletter/locale/de/LC_MESSAGES/django.po b/emencia/django/newsletter/locale/de/LC_MESSAGES/django.po similarity index 100% rename from newsletter/locale/de/LC_MESSAGES/django.po rename to emencia/django/newsletter/locale/de/LC_MESSAGES/django.po diff --git a/newsletter/locale/en/LC_MESSAGES/django.mo b/emencia/django/newsletter/locale/en/LC_MESSAGES/django.mo similarity index 100% rename from newsletter/locale/en/LC_MESSAGES/django.mo rename to emencia/django/newsletter/locale/en/LC_MESSAGES/django.mo diff --git a/newsletter/locale/en/LC_MESSAGES/django.po b/emencia/django/newsletter/locale/en/LC_MESSAGES/django.po similarity index 100% rename from newsletter/locale/en/LC_MESSAGES/django.po rename to emencia/django/newsletter/locale/en/LC_MESSAGES/django.po diff --git a/newsletter/locale/es/LC_MESSAGES/django.mo b/emencia/django/newsletter/locale/es/LC_MESSAGES/django.mo similarity index 100% rename from newsletter/locale/es/LC_MESSAGES/django.mo rename to emencia/django/newsletter/locale/es/LC_MESSAGES/django.mo diff --git a/newsletter/locale/es/LC_MESSAGES/django.po b/emencia/django/newsletter/locale/es/LC_MESSAGES/django.po similarity index 100% rename from newsletter/locale/es/LC_MESSAGES/django.po rename to emencia/django/newsletter/locale/es/LC_MESSAGES/django.po diff --git a/newsletter/locale/es_DO/LC_MESSAGES/django.mo b/emencia/django/newsletter/locale/es_DO/LC_MESSAGES/django.mo similarity index 100% rename from newsletter/locale/es_DO/LC_MESSAGES/django.mo rename to emencia/django/newsletter/locale/es_DO/LC_MESSAGES/django.mo diff --git a/newsletter/locale/es_DO/LC_MESSAGES/django.po b/emencia/django/newsletter/locale/es_DO/LC_MESSAGES/django.po similarity index 100% rename from newsletter/locale/es_DO/LC_MESSAGES/django.po rename to emencia/django/newsletter/locale/es_DO/LC_MESSAGES/django.po diff --git a/newsletter/locale/fo/LC_MESSAGES/django.mo b/emencia/django/newsletter/locale/fo/LC_MESSAGES/django.mo similarity index 100% rename from newsletter/locale/fo/LC_MESSAGES/django.mo rename to emencia/django/newsletter/locale/fo/LC_MESSAGES/django.mo diff --git a/newsletter/locale/fo/LC_MESSAGES/django.po b/emencia/django/newsletter/locale/fo/LC_MESSAGES/django.po similarity index 100% rename from newsletter/locale/fo/LC_MESSAGES/django.po rename to emencia/django/newsletter/locale/fo/LC_MESSAGES/django.po diff --git a/newsletter/locale/fr/LC_MESSAGES/django.mo b/emencia/django/newsletter/locale/fr/LC_MESSAGES/django.mo similarity index 100% rename from newsletter/locale/fr/LC_MESSAGES/django.mo rename to emencia/django/newsletter/locale/fr/LC_MESSAGES/django.mo diff --git a/newsletter/locale/fr/LC_MESSAGES/django.po b/emencia/django/newsletter/locale/fr/LC_MESSAGES/django.po similarity index 100% rename from newsletter/locale/fr/LC_MESSAGES/django.po rename to emencia/django/newsletter/locale/fr/LC_MESSAGES/django.po diff --git a/newsletter/locale/it/LC_MESSAGES/django.mo b/emencia/django/newsletter/locale/it/LC_MESSAGES/django.mo similarity index 100% rename from newsletter/locale/it/LC_MESSAGES/django.mo rename to emencia/django/newsletter/locale/it/LC_MESSAGES/django.mo diff --git a/newsletter/locale/it/LC_MESSAGES/django.po b/emencia/django/newsletter/locale/it/LC_MESSAGES/django.po similarity index 100% rename from newsletter/locale/it/LC_MESSAGES/django.po rename to emencia/django/newsletter/locale/it/LC_MESSAGES/django.po diff --git a/newsletter/locale/ja/LC_MESSAGES/django.mo b/emencia/django/newsletter/locale/ja/LC_MESSAGES/django.mo similarity index 100% rename from newsletter/locale/ja/LC_MESSAGES/django.mo rename to emencia/django/newsletter/locale/ja/LC_MESSAGES/django.mo diff --git a/newsletter/locale/ja/LC_MESSAGES/django.po b/emencia/django/newsletter/locale/ja/LC_MESSAGES/django.po similarity index 100% rename from newsletter/locale/ja/LC_MESSAGES/django.po rename to emencia/django/newsletter/locale/ja/LC_MESSAGES/django.po diff --git a/newsletter/locale/nl/LC_MESSAGES/django.mo b/emencia/django/newsletter/locale/nl/LC_MESSAGES/django.mo similarity index 100% rename from newsletter/locale/nl/LC_MESSAGES/django.mo rename to emencia/django/newsletter/locale/nl/LC_MESSAGES/django.mo diff --git a/newsletter/locale/nl/LC_MESSAGES/django.po b/emencia/django/newsletter/locale/nl/LC_MESSAGES/django.po similarity index 100% rename from newsletter/locale/nl/LC_MESSAGES/django.po rename to emencia/django/newsletter/locale/nl/LC_MESSAGES/django.po diff --git a/newsletter/locale/pt/LC_MESSAGES/django.mo b/emencia/django/newsletter/locale/pt/LC_MESSAGES/django.mo similarity index 100% rename from newsletter/locale/pt/LC_MESSAGES/django.mo rename to emencia/django/newsletter/locale/pt/LC_MESSAGES/django.mo diff --git a/newsletter/locale/pt/LC_MESSAGES/django.po b/emencia/django/newsletter/locale/pt/LC_MESSAGES/django.po similarity index 100% rename from newsletter/locale/pt/LC_MESSAGES/django.po rename to emencia/django/newsletter/locale/pt/LC_MESSAGES/django.po diff --git a/newsletter/locale/pt_BR/LC_MESSAGES/django.mo b/emencia/django/newsletter/locale/pt_BR/LC_MESSAGES/django.mo similarity index 100% rename from newsletter/locale/pt_BR/LC_MESSAGES/django.mo rename to emencia/django/newsletter/locale/pt_BR/LC_MESSAGES/django.mo diff --git a/newsletter/locale/pt_BR/LC_MESSAGES/django.po b/emencia/django/newsletter/locale/pt_BR/LC_MESSAGES/django.po similarity index 100% rename from newsletter/locale/pt_BR/LC_MESSAGES/django.po rename to emencia/django/newsletter/locale/pt_BR/LC_MESSAGES/django.po diff --git a/newsletter/locale/ro/LC_MESSAGES/django.mo b/emencia/django/newsletter/locale/ro/LC_MESSAGES/django.mo similarity index 100% rename from newsletter/locale/ro/LC_MESSAGES/django.mo rename to emencia/django/newsletter/locale/ro/LC_MESSAGES/django.mo diff --git a/newsletter/locale/ro/LC_MESSAGES/django.po b/emencia/django/newsletter/locale/ro/LC_MESSAGES/django.po similarity index 100% rename from newsletter/locale/ro/LC_MESSAGES/django.po rename to emencia/django/newsletter/locale/ro/LC_MESSAGES/django.po diff --git a/newsletter/locale/ru/LC_MESSAGES/django.mo b/emencia/django/newsletter/locale/ru/LC_MESSAGES/django.mo similarity index 100% rename from newsletter/locale/ru/LC_MESSAGES/django.mo rename to emencia/django/newsletter/locale/ru/LC_MESSAGES/django.mo diff --git a/newsletter/locale/ru/LC_MESSAGES/django.po b/emencia/django/newsletter/locale/ru/LC_MESSAGES/django.po similarity index 100% rename from newsletter/locale/ru/LC_MESSAGES/django.po rename to emencia/django/newsletter/locale/ru/LC_MESSAGES/django.po diff --git a/newsletter/locale/sk_SK/LC_MESSAGES/django.mo b/emencia/django/newsletter/locale/sk_SK/LC_MESSAGES/django.mo similarity index 100% rename from newsletter/locale/sk_SK/LC_MESSAGES/django.mo rename to emencia/django/newsletter/locale/sk_SK/LC_MESSAGES/django.mo diff --git a/newsletter/locale/sk_SK/LC_MESSAGES/django.po b/emencia/django/newsletter/locale/sk_SK/LC_MESSAGES/django.po similarity index 100% rename from newsletter/locale/sk_SK/LC_MESSAGES/django.po rename to emencia/django/newsletter/locale/sk_SK/LC_MESSAGES/django.po diff --git a/newsletter/locale/sl/LC_MESSAGES/django.mo b/emencia/django/newsletter/locale/sl/LC_MESSAGES/django.mo similarity index 100% rename from newsletter/locale/sl/LC_MESSAGES/django.mo rename to emencia/django/newsletter/locale/sl/LC_MESSAGES/django.mo diff --git a/newsletter/locale/sl/LC_MESSAGES/django.po b/emencia/django/newsletter/locale/sl/LC_MESSAGES/django.po similarity index 100% rename from newsletter/locale/sl/LC_MESSAGES/django.po rename to emencia/django/newsletter/locale/sl/LC_MESSAGES/django.po diff --git a/newsletter/locale/zh_CN/LC_MESSAGES/django.mo b/emencia/django/newsletter/locale/zh_CN/LC_MESSAGES/django.mo similarity index 100% rename from newsletter/locale/zh_CN/LC_MESSAGES/django.mo rename to emencia/django/newsletter/locale/zh_CN/LC_MESSAGES/django.mo diff --git a/newsletter/locale/zh_CN/LC_MESSAGES/django.po b/emencia/django/newsletter/locale/zh_CN/LC_MESSAGES/django.po similarity index 100% rename from newsletter/locale/zh_CN/LC_MESSAGES/django.po rename to emencia/django/newsletter/locale/zh_CN/LC_MESSAGES/django.po diff --git a/newsletter/mailer.py b/emencia/django/newsletter/mailer.py similarity index 94% rename from newsletter/mailer.py rename to emencia/django/newsletter/mailer.py index 86b60e1e..9891a751 100644 --- a/newsletter/mailer.py +++ b/emencia/django/newsletter/mailer.py @@ -1,4 +1,4 @@ -"""Mailer for django.newsletter""" +"""Mailer for emencia.django.newsletter""" import re import sys import time @@ -35,19 +35,20 @@ from django.utils.encoding import smart_str from django.utils.encoding import smart_unicode from django.core.urlresolvers import reverse -from newsletter.models import Newsletter -from newsletter.models import ContactMailingStatus -from newsletter.utils.tokens import tokenize -from newsletter.utils.newsletter import track_links -from newsletter.utils.newsletter import body_insertion -from newsletter.settings import TRACKING_LINKS -from newsletter.settings import TRACKING_IMAGE -from newsletter.settings import TRACKING_IMAGE_FORMAT -from newsletter.settings import UNIQUE_KEY_LENGTH -from newsletter.settings import UNIQUE_KEY_CHAR_SET -from newsletter.settings import INCLUDE_UNSUBSCRIPTION -from newsletter.settings import SLEEP_BETWEEN_SENDING -from newsletter.settings import RESTART_CONNECTION_BETWEEN_SENDING +from emencia.django.newsletter.models import Newsletter +from emencia.django.newsletter.models import ContactMailingStatus +from emencia.django.newsletter.utils.tokens import tokenize +from emencia.django.newsletter.utils.newsletter import track_links +from emencia.django.newsletter.utils.newsletter import body_insertion +from emencia.django.newsletter.settings import TRACKING_LINKS +from emencia.django.newsletter.settings import TRACKING_IMAGE +from emencia.django.newsletter.settings import TRACKING_IMAGE_FORMAT +from emencia.django.newsletter.settings import UNIQUE_KEY_LENGTH +from emencia.django.newsletter.settings import UNIQUE_KEY_CHAR_SET +from emencia.django.newsletter.settings import INCLUDE_UNSUBSCRIPTION +from emencia.django.newsletter.settings import SLEEP_BETWEEN_SENDING +from emencia.django.newsletter.settings import \ + RESTART_CONNECTION_BETWEEN_SENDING import HTMLParser import chardet diff --git a/newsletter/management/__init__.py b/emencia/django/newsletter/management/__init__.py similarity index 100% rename from newsletter/management/__init__.py rename to emencia/django/newsletter/management/__init__.py diff --git a/newsletter/management/commands/__init__.py b/emencia/django/newsletter/management/commands/__init__.py similarity index 100% rename from newsletter/management/commands/__init__.py rename to emencia/django/newsletter/management/commands/__init__.py diff --git a/newsletter/management/commands/send_newsletter.py b/emencia/django/newsletter/management/commands/send_newsletter.py similarity index 89% rename from newsletter/management/commands/send_newsletter.py rename to emencia/django/newsletter/management/commands/send_newsletter.py index 3fcc15cf..77bb17c5 100644 --- a/newsletter/management/commands/send_newsletter.py +++ b/emencia/django/newsletter/management/commands/send_newsletter.py @@ -3,8 +3,8 @@ from django.conf import settings from django.utils.translation import activate from django.core.management.base import NoArgsCommand -from newsletter.mailer import Mailer -from newsletter.models import Newsletter +from emencia.django.newsletter.mailer import Mailer +from emencia.django.newsletter.models import Newsletter class Command(NoArgsCommand): diff --git a/newsletter/management/commands/send_newsletter_continuous.py b/emencia/django/newsletter/management/commands/send_newsletter_continuous.py similarity index 92% rename from newsletter/management/commands/send_newsletter_continuous.py rename to emencia/django/newsletter/management/commands/send_newsletter_continuous.py index ac71c9d2..eddbab5f 100644 --- a/newsletter/management/commands/send_newsletter_continuous.py +++ b/emencia/django/newsletter/management/commands/send_newsletter_continuous.py @@ -8,8 +8,8 @@ from django.utils.translation import activate from django.core import signals from django.core.management.base import NoArgsCommand -from newsletter.mailer import SMTPMailer -from newsletter.models import SMTPServer +from emencia.django.newsletter.mailer import SMTPMailer +from emencia.django.newsletter.models import SMTPServer class Command(NoArgsCommand): diff --git a/newsletter/managers.py b/emencia/django/newsletter/managers.py similarity index 93% rename from newsletter/managers.py rename to emencia/django/newsletter/managers.py index 3dbd8137..3dbbb877 100644 --- a/newsletter/managers.py +++ b/emencia/django/newsletter/managers.py @@ -1,3 +1,4 @@ +"""Managers for emencia.django.newsletter""" from django.db import models diff --git a/emencia/django/newsletter/migrations/0001_initial.py b/emencia/django/newsletter/migrations/0001_initial.py new file mode 100644 index 00000000..d5b2e548 --- /dev/null +++ b/emencia/django/newsletter/migrations/0001_initial.py @@ -0,0 +1,272 @@ +from south.db import db +from django.db import models +from emencia.django.newsletter.models import * + + +class Migration: + + def forwards(self, orm): + + # Adding model 'MailingList' + db.create_table('newsletter_mailinglist', ( + ('id', orm['newsletter.MailingList:id']), + ('name', orm['newsletter.MailingList:name']), + ('description', orm['newsletter.MailingList:description']), + ('creation_date', orm['newsletter.MailingList:creation_date']), + ('modification_date', orm['newsletter.MailingList:modification_date']), + )) + db.send_create_signal('newsletter', ['MailingList']) + + # Adding model 'ContactMailingStatus' + db.create_table('newsletter_contactmailingstatus', ( + ('id', orm['newsletter.ContactMailingStatus:id']), + ('newsletter', orm['newsletter.ContactMailingStatus:newsletter']), + ('contact', orm['newsletter.ContactMailingStatus:contact']), + ('status', orm['newsletter.ContactMailingStatus:status']), + ('link', orm['newsletter.ContactMailingStatus:link']), + ('creation_date', orm['newsletter.ContactMailingStatus:creation_date']), + )) + db.send_create_signal('newsletter', ['ContactMailingStatus']) + + # Adding model 'WorkGroup' + db.create_table('newsletter_workgroup', ( + ('id', orm['newsletter.WorkGroup:id']), + ('name', orm['newsletter.WorkGroup:name']), + ('group', orm['newsletter.WorkGroup:group']), + )) + db.send_create_signal('newsletter', ['WorkGroup']) + + # Adding model 'Link' + db.create_table('newsletter_link', ( + ('id', orm['newsletter.Link:id']), + ('title', orm['newsletter.Link:title']), + ('url', orm['newsletter.Link:url']), + ('creation_date', orm['newsletter.Link:creation_date']), + )) + db.send_create_signal('newsletter', ['Link']) + + # Adding model 'Newsletter' + db.create_table('newsletter_newsletter', ( + ('id', orm['newsletter.Newsletter:id']), + ('title', orm['newsletter.Newsletter:title']), + ('content', orm['newsletter.Newsletter:content']), + ('mailing_list', orm['newsletter.Newsletter:mailing_list']), + ('server', orm['newsletter.Newsletter:server']), + ('header_sender', orm['newsletter.Newsletter:header_sender']), + ('header_reply', orm['newsletter.Newsletter:header_reply']), + ('status', orm['newsletter.Newsletter:status']), + ('sending_date', orm['newsletter.Newsletter:sending_date']), + ('slug', orm['newsletter.Newsletter:slug']), + ('creation_date', orm['newsletter.Newsletter:creation_date']), + ('modification_date', orm['newsletter.Newsletter:modification_date']), + )) + db.send_create_signal('newsletter', ['Newsletter']) + + # Adding model 'SMTPServer' + db.create_table('newsletter_smtpserver', ( + ('id', orm['newsletter.SMTPServer:id']), + ('name', orm['newsletter.SMTPServer:name']), + ('host', orm['newsletter.SMTPServer:host']), + ('user', orm['newsletter.SMTPServer:user']), + ('password', orm['newsletter.SMTPServer:password']), + ('port', orm['newsletter.SMTPServer:port']), + ('tls', orm['newsletter.SMTPServer:tls']), + ('headers', orm['newsletter.SMTPServer:headers']), + ('mails_hour', orm['newsletter.SMTPServer:mails_hour']), + )) + db.send_create_signal('newsletter', ['SMTPServer']) + + # Adding model 'Contact' + db.create_table('newsletter_contact', ( + ('id', orm['newsletter.Contact:id']), + ('email', orm['newsletter.Contact:email']), + ('first_name', orm['newsletter.Contact:first_name']), + ('last_name', orm['newsletter.Contact:last_name']), + ('subscriber', orm['newsletter.Contact:subscriber']), + ('valid', orm['newsletter.Contact:valid']), + ('tester', orm['newsletter.Contact:tester']), + ('tags', orm['newsletter.Contact:tags']), + ('content_type', orm['newsletter.Contact:content_type']), + ('object_id', orm['newsletter.Contact:object_id']), + ('creation_date', orm['newsletter.Contact:creation_date']), + ('modification_date', orm['newsletter.Contact:modification_date']), + )) + db.send_create_signal('newsletter', ['Contact']) + + # Adding ManyToManyField 'WorkGroup.mailinglists' + db.create_table('newsletter_workgroup_mailinglists', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('workgroup', models.ForeignKey(orm.WorkGroup, null=False)), + ('mailinglist', models.ForeignKey(orm.MailingList, null=False)) + )) + + # Adding ManyToManyField 'MailingList.subscribers' + db.create_table('newsletter_mailinglist_subscribers', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('mailinglist', models.ForeignKey(orm.MailingList, null=False)), + ('contact', models.ForeignKey(orm.Contact, null=False)) + )) + + # Adding ManyToManyField 'WorkGroup.contacts' + db.create_table('newsletter_workgroup_contacts', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('workgroup', models.ForeignKey(orm.WorkGroup, null=False)), + ('contact', models.ForeignKey(orm.Contact, null=False)) + )) + + # Adding ManyToManyField 'WorkGroup.newsletters' + db.create_table('newsletter_workgroup_newsletters', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('workgroup', models.ForeignKey(orm.WorkGroup, null=False)), + ('newsletter', models.ForeignKey(orm.Newsletter, null=False)) + )) + + # Adding ManyToManyField 'MailingList.unsubscribers' + db.create_table('newsletter_mailinglist_unsubscribers', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('mailinglist', models.ForeignKey(orm.MailingList, null=False)), + ('contact', models.ForeignKey(orm.Contact, null=False)) + )) + + # Adding ManyToManyField 'Newsletter.test_contacts' + db.create_table('newsletter_newsletter_test_contacts', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('newsletter', models.ForeignKey(orm.Newsletter, null=False)), + ('contact', models.ForeignKey(orm.Contact, null=False)) + )) + + def backwards(self, orm): + + # Deleting model 'MailingList' + db.delete_table('newsletter_mailinglist') + + # Deleting model 'ContactMailingStatus' + db.delete_table('newsletter_contactmailingstatus') + + # Deleting model 'WorkGroup' + db.delete_table('newsletter_workgroup') + + # Deleting model 'Link' + db.delete_table('newsletter_link') + + # Deleting model 'Newsletter' + db.delete_table('newsletter_newsletter') + + # Deleting model 'SMTPServer' + db.delete_table('newsletter_smtpserver') + + # Deleting model 'Contact' + db.delete_table('newsletter_contact') + + # Dropping ManyToManyField 'WorkGroup.mailinglists' + db.delete_table('newsletter_workgroup_mailinglists') + + # Dropping ManyToManyField 'MailingList.subscribers' + db.delete_table('newsletter_mailinglist_subscribers') + + # Dropping ManyToManyField 'WorkGroup.contacts' + db.delete_table('newsletter_workgroup_contacts') + + # Dropping ManyToManyField 'WorkGroup.newsletters' + db.delete_table('newsletter_workgroup_newsletters') + + # Dropping ManyToManyField 'MailingList.unsubscribers' + db.delete_table('newsletter_mailinglist_unsubscribers') + + # Dropping ManyToManyField 'Newsletter.test_contacts' + db.delete_table('newsletter_newsletter_test_contacts') + + models = { + 'auth.group': { + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'unique_together': "(('content_type', 'codename'),)"}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'newsletter.contact': { + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'modification_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'subscriber': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'tags': ('tagging.fields.TagField', [], {'default': "''"}), + 'tester': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'valid': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}) + }, + 'newsletter.contactmailingstatus': { + 'contact': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.Contact']"}), + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'link': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.Link']", 'null': 'True', 'blank': 'True'}), + 'newsletter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.Newsletter']"}), + 'status': ('django.db.models.fields.IntegerField', [], {}) + }, + 'newsletter.link': { + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'url': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + 'newsletter.mailinglist': { + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modification_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'subscribers': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['newsletter.Contact']"}), + 'unsubscribers': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['newsletter.Contact']", 'null': 'True', 'blank': 'True'}) + }, + 'newsletter.newsletter': { + 'content': ('django.db.models.fields.TextField', [], {'default': "u'\\n\\n'"}), + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'header_reply': ('django.db.models.fields.CharField', [], {'default': "'Emencia Newsletter'", 'max_length': '255'}), + 'header_sender': ('django.db.models.fields.CharField', [], {'default': "'Emencia Newsletter'", 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mailing_list': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.MailingList']"}), + 'modification_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'sending_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'server': ('django.db.models.fields.related.ForeignKey', [], {'default': '1', 'to': "orm['newsletter.SMTPServer']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}), + 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'test_contacts': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['newsletter.Contact']", 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + 'newsletter.smtpserver': { + 'headers': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'host': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mails_hour': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), + 'port': ('django.db.models.fields.IntegerField', [], {'default': '25'}), + 'tls': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'user': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}) + }, + 'newsletter.workgroup': { + 'contacts': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['newsletter.Contact']", 'null': 'True', 'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mailinglists': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['newsletter.MailingList']", 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'newsletters': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['newsletter.Newsletter']", 'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['newsletter'] diff --git a/emencia/django/newsletter/migrations/0002_auto__add_attachment.py b/emencia/django/newsletter/migrations/0002_auto__add_attachment.py new file mode 100644 index 00000000..d070a5ed --- /dev/null +++ b/emencia/django/newsletter/migrations/0002_auto__add_attachment.py @@ -0,0 +1,130 @@ +from south.db import db +from south.v2 import SchemaMigration + + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding model 'Attachment' + db.create_table('newsletter_attachment', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('newsletter', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['newsletter.Newsletter'])), + ('title', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('file_attachment', self.gf('django.db.models.fields.files.FileField')(max_length=100)), + )) + db.send_create_signal('newsletter', ['Attachment']) + + def backwards(self, orm): + # Deleting model 'Attachment' + db.delete_table('newsletter_attachment') + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'newsletter.attachment': { + 'Meta': {'object_name': 'Attachment'}, + 'file_attachment': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'newsletter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.Newsletter']"}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + 'newsletter.contact': { + 'Meta': {'object_name': 'Contact'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'modification_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'subscriber': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'tags': ('tagging.fields.TagField', [], {'default': "''"}), + 'tester': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'valid': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}) + }, + 'newsletter.contactmailingstatus': { + 'Meta': {'object_name': 'ContactMailingStatus'}, + 'contact': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.Contact']"}), + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'link': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.Link']", 'null': 'True', 'blank': 'True'}), + 'newsletter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.Newsletter']"}), + 'status': ('django.db.models.fields.IntegerField', [], {}) + }, + 'newsletter.link': { + 'Meta': {'object_name': 'Link'}, + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'url': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + 'newsletter.mailinglist': { + 'Meta': {'object_name': 'MailingList'}, + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modification_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'subscribers': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mailinglist_subscriber'", 'symmetrical': 'False', 'to': "orm['newsletter.Contact']"}), + 'unsubscribers': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'mailinglist_unsubscriber'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['newsletter.Contact']"}) + }, + 'newsletter.newsletter': { + 'Meta': {'object_name': 'Newsletter'}, + 'content': ('django.db.models.fields.TextField', [], {'default': "u'\\n\\n'"}), + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'header_reply': ('django.db.models.fields.CharField', [], {'default': "'Giorgio Barbarotta Newsletter'", 'max_length': '255'}), + 'header_sender': ('django.db.models.fields.CharField', [], {'default': "'Giorgio Barbarotta Newsletter'", 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mailing_list': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.MailingList']"}), + 'modification_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'sending_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'server': ('django.db.models.fields.related.ForeignKey', [], {'default': '1', 'to': "orm['newsletter.SMTPServer']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}), + 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'test_contacts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['newsletter.Contact']", 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + 'newsletter.smtpserver': { + 'Meta': {'object_name': 'SMTPServer'}, + 'headers': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'host': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mails_hour': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), + 'port': ('django.db.models.fields.IntegerField', [], {'default': '25'}), + 'tls': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'user': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}) + }, + 'newsletter.workgroup': { + 'Meta': {'object_name': 'WorkGroup'}, + 'contacts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['newsletter.Contact']", 'null': 'True', 'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mailinglists': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['newsletter.MailingList']", 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'newsletters': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['newsletter.Newsletter']", 'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['newsletter'] diff --git a/emencia/django/newsletter/migrations/0003_auto__add_unique_newsletter_slug.py b/emencia/django/newsletter/migrations/0003_auto__add_unique_newsletter_slug.py new file mode 100644 index 00000000..14645df9 --- /dev/null +++ b/emencia/django/newsletter/migrations/0003_auto__add_unique_newsletter_slug.py @@ -0,0 +1,123 @@ +from south.db import db +from south.v2 import SchemaMigration + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding unique constraint on 'Newsletter', fields ['slug'] + db.create_unique('newsletter_newsletter', ['slug']) + + def backwards(self, orm): + # Removing unique constraint on 'Newsletter', fields ['slug'] + db.delete_unique('newsletter_newsletter', ['slug']) + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'newsletter.attachment': { + 'Meta': {'object_name': 'Attachment'}, + 'file_attachment': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'newsletter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.Newsletter']"}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + 'newsletter.contact': { + 'Meta': {'ordering': "('creation_date',)", 'object_name': 'Contact'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'modification_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'subscriber': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'tags': ('tagging.fields.TagField', [], {'default': "''"}), + 'tester': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'valid': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'newsletter.contactmailingstatus': { + 'Meta': {'ordering': "('creation_date',)", 'object_name': 'ContactMailingStatus'}, + 'contact': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.Contact']"}), + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'link': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.Link']", 'null': 'True', 'blank': 'True'}), + 'newsletter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.Newsletter']"}), + 'status': ('django.db.models.fields.IntegerField', [], {}) + }, + 'newsletter.link': { + 'Meta': {'ordering': "('creation_date',)", 'object_name': 'Link'}, + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'url': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + 'newsletter.mailinglist': { + 'Meta': {'ordering': "('creation_date',)", 'object_name': 'MailingList'}, + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modification_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'subscribers': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'mailinglist_subscriber'", 'symmetrical': 'False', 'to': "orm['newsletter.Contact']"}), + 'unsubscribers': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'mailinglist_unsubscriber'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['newsletter.Contact']"}) + }, + 'newsletter.newsletter': { + 'Meta': {'ordering': "('creation_date',)", 'object_name': 'Newsletter'}, + 'content': ('django.db.models.fields.TextField', [], {'default': "u'\\n\\n'"}), + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'header_reply': ('django.db.models.fields.CharField', [], {'default': "'Emencia Newsletter'", 'max_length': '255'}), + 'header_sender': ('django.db.models.fields.CharField', [], {'default': "'Emencia Newsletter'", 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mailing_list': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['newsletter.MailingList']"}), + 'modification_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'sending_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'server': ('django.db.models.fields.related.ForeignKey', [], {'default': '1', 'to': "orm['newsletter.SMTPServer']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'}), + 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'test_contacts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['newsletter.Contact']", 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + 'newsletter.smtpserver': { + 'Meta': {'object_name': 'SMTPServer'}, + 'headers': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'host': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mails_hour': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), + 'port': ('django.db.models.fields.IntegerField', [], {'default': '25'}), + 'tls': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'user': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}) + }, + 'newsletter.workgroup': { + 'Meta': {'object_name': 'WorkGroup'}, + 'contacts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['newsletter.Contact']", 'null': 'True', 'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mailinglists': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['newsletter.MailingList']", 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'newsletters': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['newsletter.Newsletter']", 'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['newsletter'] diff --git a/emencia/django/newsletter/migrations/__init__.py b/emencia/django/newsletter/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/newsletter/models.py b/emencia/django/newsletter/models.py similarity index 96% rename from newsletter/models.py rename to emencia/django/newsletter/models.py index 19890020..9da57ca6 100644 --- a/newsletter/models.py +++ b/emencia/django/newsletter/models.py @@ -1,4 +1,6 @@ +"""Models for emencia.django.newsletter""" from smtplib import SMTP +from smtplib import SMTPHeloError from datetime import datetime from datetime import timedelta @@ -11,13 +13,13 @@ from django.contrib.contenttypes.models import ContentType from django.contrib.auth.models import Group from django.utils.encoding import force_unicode -from newsletter.managers import ContactManager -from newsletter.settings import BASE_PATH -from newsletter.settings import MAILER_HARD_LIMIT -from newsletter.settings import DEFAULT_HEADER_REPLY -from newsletter.settings import DEFAULT_HEADER_SENDER -from newsletter.utils.vcard import vcard_contact_export +from emencia.django.newsletter.managers import ContactManager +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.utils.vcard import vcard_contact_export class SMTPServer(models.Model): """Configuration of a SMTP server""" diff --git a/newsletter/settings.py b/emencia/django/newsletter/settings.py similarity index 96% rename from newsletter/settings.py rename to emencia/django/newsletter/settings.py index d5266ae8..f035ca45 100644 --- a/newsletter/settings.py +++ b/emencia/django/newsletter/settings.py @@ -1,3 +1,4 @@ +"""Settings for emencia.django.newsletter""" import string from django.conf import settings @@ -37,6 +38,3 @@ RESTART_CONNECTION_BETWEEN_SENDING = getattr( settings, 'NEWSLETTER_RESTART_CONNECTION_BETWEEN_SENDING', False) BASE_PATH = getattr(settings, 'NEWSLETTER_BASE_PATH', 'uploads/newsletter') - -# -PERIODIC_FOR_ANNOUNCES = [(1, 'week'), (2, '2 weeks'), (3, 'month')] diff --git a/emencia/django/newsletter/static/edn/js/jquery.form.js b/emencia/django/newsletter/static/edn/js/jquery.form.js new file mode 100644 index 00000000..dde39427 --- /dev/null +++ b/emencia/django/newsletter/static/edn/js/jquery.form.js @@ -0,0 +1,660 @@ +/* + * jQuery Form Plugin + * version: 2.36 (07-NOV-2009) + * @requires jQuery v1.2.6 or later + * + * Examples and documentation at: http://malsup.com/jquery/form/ + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + */ +;(function($) { + +/* + Usage Note: + ----------- + Do not use both ajaxSubmit and ajaxForm on the same form. These + functions are intended to be exclusive. Use ajaxSubmit if you want + to bind your own submit handler to the form. For example, + + $(document).ready(function() { + $('#myForm').bind('submit', function() { + $(this).ajaxSubmit({ + target: '#output' + }); + return false; // <-- important! + }); + }); + + Use ajaxForm when you want the plugin to manage all the event binding + for you. For example, + + $(document).ready(function() { + $('#myForm').ajaxForm({ + target: '#output' + }); + }); + + When using ajaxForm, the ajaxSubmit function will be invoked for you + at the appropriate time. +*/ + +/** + * ajaxSubmit() provides a mechanism for immediately submitting + * an HTML form using AJAX. + */ +$.fn.ajaxSubmit = function(options) { + // fast fail if nothing selected (http://dev.jquery.com/ticket/2752) + if (!this.length) { + log('ajaxSubmit: skipping submit process - no element selected'); + return this; + } + + if (typeof options == 'function') + options = { success: options }; + + var url = $.trim(this.attr('action')); + if (url) { + // clean url (don't include hash vaue) + url = (url.match(/^([^#]+)/)||[])[1]; + } + url = url || window.location.href || ''; + + options = $.extend({ + url: url, + type: this.attr('method') || 'GET', + iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank' + }, options || {}); + + // hook for manipulating the form data before it is extracted; + // convenient for use with rich editors like tinyMCE or FCKEditor + var veto = {}; + this.trigger('form-pre-serialize', [this, options, veto]); + if (veto.veto) { + log('ajaxSubmit: submit vetoed via form-pre-serialize trigger'); + return this; + } + + // provide opportunity to alter form data before it is serialized + if (options.beforeSerialize && options.beforeSerialize(this, options) === false) { + log('ajaxSubmit: submit aborted via beforeSerialize callback'); + return this; + } + + var a = this.formToArray(options.semantic); + if (options.data) { + options.extraData = options.data; + for (var n in options.data) { + if(options.data[n] instanceof Array) { + for (var k in options.data[n]) + a.push( { name: n, value: options.data[n][k] } ); + } + else + a.push( { name: n, value: options.data[n] } ); + } + } + + // give pre-submit callback an opportunity to abort the submit + if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) { + log('ajaxSubmit: submit aborted via beforeSubmit callback'); + return this; + } + + // fire vetoable 'validate' event + this.trigger('form-submit-validate', [a, this, options, veto]); + if (veto.veto) { + log('ajaxSubmit: submit vetoed via form-submit-validate trigger'); + return this; + } + + var q = $.param(a); + + if (options.type.toUpperCase() == 'GET') { + options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q; + options.data = null; // data is null for 'get' + } + else + options.data = q; // data is the query string for 'post' + + var $form = this, callbacks = []; + if (options.resetForm) callbacks.push(function() { $form.resetForm(); }); + if (options.clearForm) callbacks.push(function() { $form.clearForm(); }); + + // perform a load on the target only if dataType is not provided + if (!options.dataType && options.target) { + var oldSuccess = options.success || function(){}; + callbacks.push(function(data) { + $(options.target).html(data).each(oldSuccess, arguments); + }); + } + else if (options.success) + callbacks.push(options.success); + + options.success = function(data, status) { + for (var i=0, max=callbacks.length; i < max; i++) + callbacks[i].apply(options, [data, status, $form]); + }; + + // are there files to upload? + var files = $('input:file', this).fieldValue(); + var found = false; + for (var j=0; j < files.length; j++) + if (files[j]) + found = true; + + var multipart = false; +// var mp = 'multipart/form-data'; +// multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp); + + // options.iframe allows user to force iframe mode + // 06-NOV-09: now defaulting to iframe mode if file input is detected + if ((files.length && options.iframe !== false) || options.iframe || found || multipart) { + // hack to fix Safari hang (thanks to Tim Molendijk for this) + // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d + if (options.closeKeepAlive) + $.get(options.closeKeepAlive, fileUpload); + else + fileUpload(); + } + else + $.ajax(options); + + // fire 'notify' event + this.trigger('form-submit-notify', [this, options]); + return this; + + + // private function for handling file uploads (hat tip to YAHOO!) + function fileUpload() { + var form = $form[0]; + + if ($(':input[name=submit]', form).length) { + alert('Error: Form elements must not be named "submit".'); + return; + } + + var opts = $.extend({}, $.ajaxSettings, options); + var s = $.extend(true, {}, $.extend(true, {}, $.ajaxSettings), opts); + + var id = 'jqFormIO' + (new Date().getTime()); + var $io = $('