From 219d16c947ec5c483e1fbf6845334c20486df1fe Mon Sep 17 00:00:00 2001 From: Serghei Mihai Date: Mon, 30 Jan 2017 14:38:00 +0100 Subject: [PATCH] import subscribers from csv (#14010) --- corbo/forms.py | 22 ++++++++++++ corbo/manage_urls.py | 4 ++- corbo/static/css/corbo.css | 39 ++++++++++++++++++++++ corbo/static/js/corbo.manager.js | 6 ++++ corbo/templates/corbo/base.html | 2 ++ corbo/templates/corbo/category_detail.html | 5 +++ .../templates/corbo/subscriptions_import_form.html | 23 +++++++++++++ corbo/views.py | 31 +++++++++++++++-- 8 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 corbo/static/js/corbo.manager.js create mode 100644 corbo/templates/corbo/subscriptions_import_form.html diff --git a/corbo/forms.py b/corbo/forms.py index 2fe8010..6621b45 100644 --- a/corbo/forms.py +++ b/corbo/forms.py @@ -1,7 +1,11 @@ +import csv + from django import forms from django.utils.translation import ugettext_lazy as _ from django.utils.text import slugify from django.core.exceptions import ObjectDoesNotExist +from django.core import validators +from django.core.exceptions import ValidationError from .models import Announce, Category, Broadcast, channel_choices @@ -45,3 +49,21 @@ class CategoryForm(forms.ModelForm): slug = '%s-%s' % (base_slug, i) self.instance.slug = slug return super(CategoryForm, self).save(commit=commit) + + +class SubscriptionsImportForm(forms.Form): + subscribers = forms.FileField(_('Subscribers'), + help_text=_('CSV file with email addresses on first column')) + + def clean_subscribers(self, *args, **kwargs): + subscribers = [] + reader = csv.reader(self.cleaned_data['subscribers']) + for idx, row in enumerate(reader, 1): + if not row[0]: + continue + try: + validators.validate_email(row[0]) + subscribers.append(row[0]) + except ValidationError: + raise ValidationError(_('Invalid email address at line %s' % idx)) + return subscribers diff --git a/corbo/manage_urls.py b/corbo/manage_urls.py index f4c519b..08cc40f 100644 --- a/corbo/manage_urls.py +++ b/corbo/manage_urls.py @@ -2,7 +2,7 @@ from django.conf.urls import patterns, include, url from .views import add_announce, edit_announce, delete_announce, \ add_category, edit_category, view_category, delete_category, manage, \ - menu_json + subscriptions_import, menu_json urlpatterns = patterns('', url(r'^$', manage, name='manage'), @@ -20,5 +20,7 @@ urlpatterns = patterns('', name='edit_category'), url(r'^category/delete/(?P[\w-]+)$', delete_category, name='delete_category'), + url(r'^category/(?P[\w-]+)/import-subscriptions$', subscriptions_import, + name='subscriptions-import'), url(r'^menu.json$', menu_json), ) diff --git a/corbo/static/css/corbo.css b/corbo/static/css/corbo.css index dbc7e5b..56f05d5 100644 --- a/corbo/static/css/corbo.css +++ b/corbo/static/css/corbo.css @@ -165,3 +165,42 @@ form ul li label { border-radius: 0.3em; text-transform: uppercase; } + +#appbar .menu-opener { + border: none; + cursor: pointer; +} + +#appbar .menu { + background: white; + padding: 10px; + list-style: none; + position: absolute; + right: 0px; + margin-top: -24px; + border: 1px solid #d0d0d0; + background: #fafafa; + box-shadow: 0px 1px 1px 2px rgba(0, 0, 0, 0.04); + z-index: 100; +} + +#appbar .menu a { + float: none; + top: 0; + right: 0; + margin: 0; + display: block; + min-width: 12em; +} + +#appbar .menu li { + margin: 1ex 0; +} + +#appbar .menu li a { + border: none; +} + +#appbar .menu li a:hover { + background: white; +} diff --git a/corbo/static/js/corbo.manager.js b/corbo/static/js/corbo.manager.js new file mode 100644 index 0000000..4fd2d3a --- /dev/null +++ b/corbo/static/js/corbo.manager.js @@ -0,0 +1,6 @@ +$(function() { + $('a.menu-opener').on('click', function() { + $('#appbar .menu').toggle(); + }); +}); + diff --git a/corbo/templates/corbo/base.html b/corbo/templates/corbo/base.html index 7b87625..c2805c4 100644 --- a/corbo/templates/corbo/base.html +++ b/corbo/templates/corbo/base.html @@ -19,6 +19,8 @@ + + {% endblock %} {% block logout-url %} diff --git a/corbo/templates/corbo/category_detail.html b/corbo/templates/corbo/category_detail.html index df31de9..eed069e 100644 --- a/corbo/templates/corbo/category_detail.html +++ b/corbo/templates/corbo/category_detail.html @@ -10,6 +10,11 @@ {% block appbar %}

{{ object.name }}

+ + + {% trans 'Delete' %} {% trans 'Edit' %} {% trans 'New announce' %} diff --git a/corbo/templates/corbo/subscriptions_import_form.html b/corbo/templates/corbo/subscriptions_import_form.html new file mode 100644 index 0000000..bed8249 --- /dev/null +++ b/corbo/templates/corbo/subscriptions_import_form.html @@ -0,0 +1,23 @@ +{% extends "corbo/category_detail.html" %} +{% load i18n static %} + +{% block breadcrumb %} +{{ block.super }} +{{ category }} +{% endblock %} + +{% block appbar %} +

{% trans "Import subscriptions" %}

+{% endblock %} + + +{% block content %} +
+ {% csrf_token %} + {{ form }} +
+ + {% trans "Cancel" %} +
+
+{% endblock %} diff --git a/corbo/views.py b/corbo/views.py index 6acd03d..654195f 100644 --- a/corbo/views.py +++ b/corbo/views.py @@ -6,7 +6,7 @@ from django.core import signing from django.utils import timezone from django.core.urlresolvers import reverse from django.views.generic import CreateView, UpdateView, DeleteView, \ - ListView, TemplateView, RedirectView, DetailView + ListView, TemplateView, RedirectView, DetailView, FormView from django.contrib.syndication.views import Feed from django.shortcuts import resolve_url from django.utils.encoding import force_text @@ -14,10 +14,11 @@ from django.utils.feedgenerator import Atom1Feed as DjangoAtom1Feed from django.http import HttpResponseRedirect, HttpResponse, Http404 from django.contrib.auth import logout as auth_logout from django.contrib.auth import views as auth_views +from django.contrib import messages from django.utils.translation import ugettext_lazy as _ import models -from .forms import AnnounceForm, CategoryForm +from .forms import AnnounceForm, CategoryForm, SubscriptionsImportForm try: from mellon.utils import get_idps @@ -222,6 +223,32 @@ class AtomView(Feed): atom = AtomView() +class SubscriptionsImportView(FormView): + form_class = SubscriptionsImportForm + template_name = 'corbo/subscriptions_import_form.html' + + def get_context_data(self, **kwargs): + context = super(SubscriptionsImportView, self).get_context_data(**kwargs) + context['category'] = models.Category.objects.get(slug=self.kwargs['slug']) + return context + + def get_success_url(self): + category = models.Category.objects.get(slug=self.kwargs['slug']) + return reverse('view_category', kwargs={'slug': category.slug}) + + def form_valid(self, form): + new = 0 + c = models.Category.objects.get(slug=self.kwargs['slug']) + for email in form.cleaned_data['subscribers']: + obj, created = models.Subscription.objects.get_or_create(category=c, identifier='mailto:%s' % email) + if created: + new += 1 + messages.info(self.request, _('%s subscriptions added') % new) + return super(SubscriptionsImportView, self).form_valid(form) + +subscriptions_import = SubscriptionsImportView.as_view() + + def menu_json(request): label = _('Announces') json_str = json.dumps([{'label': force_text(label), -- 2.11.0