finish views and cms plugins

This commit is contained in:
Benjamin Dauvergne 2013-06-04 15:54:40 +02:00
parent f62aa82eee
commit ecbfad9bf5
14 changed files with 418 additions and 40 deletions

View File

@ -5,7 +5,6 @@ from django.contrib.sites.models import get_current_site
import models
import transports
import app_settings
from django.utils.translation import ugettext_lazy as _
@ -31,7 +30,8 @@ class SendingAction(object):
def transport_actions():
return [SendingAction(mode) for mode in app_settings.transport_modes]
return [SendingAction(transport.identifier)
for transport in transports.get_transports() if hasattr(transport, 'send')]
class SentInlineAdmin(admin.TabularInline):

View File

@ -1,10 +1,19 @@
from django.conf import settings
default_transport_modes = {
'email': 'portail_citoyen_announces.transports.EmailTransport',
}
default_transport_modes = (
'portail_citoyen_announces.transports.EmailTransport',
'portail_citoyen_announces.transports.HomepageTransport',
)
transport_modes = getattr(settings, 'ANNOUNCES_TRANSPORTS',
default_transport_modes)
default_from = getattr(settings, 'ANNOUNCES_DEFAULT_FROM_EMAIL', None)
feed_title = getattr(settings, 'ANNOUNCES_FEED_TITLE', u'Announces')
feed_description = getattr(settings, 'ANNOUNCES_FEED_TITLE',
u'')
feed_link = getattr(settings, 'ANNOUNCES_FEED_LINK', '')
feed_item_link_template = getattr(settings,
'ANNOUNCES_FEED_ITEM_LINK_TEMPLATE', '#announce-item-{0}')
feed_homepage_limit = getattr(settings, 'ANNOUNCE_FEED_HOMEPAGE_LIMIT', 10)

View File

@ -0,0 +1,79 @@
from cms.plugin_base import CMSPluginBase
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from django.contrib.sites.models import get_current_site
from cms.plugin_pool import plugin_pool
import forms
import models
import transports
import app_settings
class AnnounceListPlugin(CMSPluginBase):
model = models.AnnounceListPlugin
name = _('Announce List Plugin')
render_template = "portail_citoyen_announces/announce_list_plugin.html"
text_enabled = True
transport_identifier = transports.HomepageTransport.identifier
def get_queryset(self):
qs = models.Announce.objects.all()
qs = qs.filter(category__subscription__transport=self.transport_identifier)
if app_settings.feed_homepage_limit:
qs = qs[:app_settings.feed_homepage_limit]
return qs
def render(self, context, instance, placeholder):
request = context['request']
context['object_list'] = self.get_queryset()
context['id_prefix'] = 'announce-item-'
subscriptions = models.Subscription.objects.filter(
transport=self.transport_identifier,
user=request.user).select_related('category')
context['subscriptions'] = [ sub.category.name for sub in subscriptions ]
return context
def icon_src(self, instance):
return settings.STATIC_URL + u"compte_meyzieu/meyzieu_newsletters_plugin.png"
class FormPluginMixin(object):
form_class = None
success_msg = None
render_template = 'portail_citoyen_announces/form_plugin.html'
def get_form_kwargs(self, context, instance, placeholder):
request = context['request']
return dict(user=request.user, site=get_current_site(request))
def render(self, context, instance, placeholder):
request = context['request']
context['submit'] = submit = 'cms-form-plugin-%s' % instance.id
if request.method == 'POST' and submit in request.POST:
form = self.form_class(data=request.POST,
**self.get_form_kwargs(context, instance, placeholder))
if form.is_valid():
form.save()
context['success'] = self.success_msg
else:
form = self.form_class(**self.get_form_kwargs(context, instance,
placeholder))
context['form'] = form
return context
class AnnounceSubscribePlugin(FormPluginMixin, CMSPluginBase):
model = models.AnnounceSubscribePlugin
form_class = forms.SubscriptionForm
name = _('Announce Subscribe Plugin')
sucess_msg = _(u'Your subscriptions were saved')
text_enabled = True
plugin_pool.register_plugin(AnnounceSubscribePlugin)
plugin_pool.register_plugin(AnnounceListPlugin)

View File

@ -6,6 +6,7 @@ from django import forms
import models
import transports
import widgets
class SubscriptionForm(forms.Form):
@ -16,18 +17,19 @@ class SubscriptionForm(forms.Form):
self.subscriptions = models.Subscription.objects.filter(user=self.user)
sub_by_category = defaultdict(lambda: [])
for sub in self.subscriptions:
sub_by_category[sub].append(sub.transport)
sub_by_category[sub.category].append(sub.transport)
super(SubscriptionForm, self).__init__(*args, **kwargs)
choices = transports.get_transport_choices()
self.choices = list(transports.get_transport_choices())
for category in self.categories:
field = forms.MultipleChoiceField(
label=category.name, choices=choices,
widget=forms.CheckboxSelectMultiple,
initial=sub_by_category[category])
label=category.name, choices=self.choices,
widget=widgets.CheckboxMultipleSelect,
initial=sub_by_category[category],
required=False)
self.fields['category_%s' % category.identifier] = field
def save(self):
cleaned_data = self.cleaned_data()
cleaned_data = self.cleaned_data
wanted_subscriptions = set()
for category in self.categories:
field_id = 'category_%s' % category.identifier
@ -41,7 +43,7 @@ class SubscriptionForm(forms.Form):
subscription.delete()
# create or update new ones
for category, transport in wanted_subscriptions:
models.SubscriptionForm.objects.get_or_create(
models.Subscription.objects.get_or_create(
user=self.user,
category=category,
transport=transport)

View File

@ -2,6 +2,7 @@ from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from django.core.validators import RegexValidator
from django.utils import timezone
from model_utils.models import TimeStampedModel
@ -41,7 +42,7 @@ class Announce(models.Model):
hidden = models.BooleanField(_('hidden'), blank=True, default=False)
publication_time = models.DateTimeField(_('publication time'), blank=True,
null=True)
null=True, default=timezone.now)
expiration_time = models.DateTimeField(_('expiration time'), blank=True,
null=True)
creation_time = models.DateTimeField(_('creation time'), auto_now_add=True)
@ -96,3 +97,14 @@ class Subscription(TimeStampedModel):
class Meta:
verbose_name = _('subscription')
ordering = ('-created',)
try:
from cms.models import CMSPlugin
except:
pass
else:
class AnnounceListPlugin(CMSPlugin):
pass
class AnnounceSubscribePlugin(CMSPlugin):
pass

View File

@ -0,0 +1,5 @@
{% extends "base.html" %}
{% block content %}
{% include "portail_citoyen_announces/announce_list.html" %}
{% endblock %}

View File

@ -0,0 +1,12 @@
{% load i18n %}
<dl class="announce-list">
{% for announce in object_list %}
<dt id="{{id_prefix}}{{ announce.pk}} announce-title">
<a href="#{{id_prefix}}{{announce.pk}}">{{ announce.title }}</a>
</dt>
{% if announce.text %}
<dd class="announce-text">{{ announce.text }}</dd>
{% endif %}
{% endfor %}
</dl>
<p>{% trans "Subscriptions" %}: {{ subscriptions|join:", " }}</p>

View File

@ -0,0 +1 @@
{% include "portail_citoyen_announces/announce_list.html" %}

View File

@ -0,0 +1,33 @@
{% load i18n %}
<form method="post">
{% csrf_token %}
{{ form.non_field_errors }}
<table>
<thead>
<tr>
<td>{% trans "Catégorie" %}</td>
{% for choice_key, choice_name in form.choices %}
<td>{{ choice_name }}</td>
{% endfor %}
</tr>
</thead>
<tbody>
{% for field in form %}
<tr>
<td>{{ field.label }}</td>
{% for subwidget in field %}
<td>{{ subwidget.tag }}</td>
{% endfor %}
</tr>
{% if field.error %}
<tr><td colspan="{{ form.choices|length }}">{{ field.error }}</td></tr>
{% endif %}
{% endfor %}
</tbody>
</table>
<input type="submit" name="{{ submit }}" value="{% trans "Modify" %}"/>
</form>
{% if success_msg %}
<p>{{ success_msg }}</p>
{% endif %}

View File

@ -3,7 +3,31 @@
{% block content %}
<form method="post">
{{ form }}
{% csrf_token %}
{{ form.non_field_errors }}
<table>
<thead>
<tr>
<td>{% trans "Catégorie" %}</td>
{% for choice_key, choice_name in form.choices %}
<td>{{ choice_name }}</td>
{% endfor %}
</tr>
</thead>
<tbody>
{% for field in form %}
<tr>
<td>{{ field.label }}</td>
{% for subwidget in field %}
<td>{{ subwidget.tag }}</td>
{% endfor %}
</tr>
{% if field.error %}
<tr><td colspan="{{ form.choices|length }}">{{ field.error }}</td></tr>
{% endif %}
{% endfor %}
</tbody>
</table>
<button type="submit">{% trans "Modify" %}</button>
</form>
{% endblock %}

View File

@ -15,31 +15,41 @@ import models
logger = logging.getLogger()
__transport_choices = None
def get_transport_choices():
global __transport_choices
if __transport_choices is None:
__transport_choices = []
for mode in app_settings.transport_modes:
transport = get_transport(mode)
identifier = transport.identifier
display_name = getattr(transport, 'display_name', identifier)
__transport_choices.append((identifier, display_name))
return __transport_choices
def get_transport_choices(include=[], exclude=[]):
for transport in get_transports():
if include and transport.identifer not in include:
continue
if exclude and transport.identifer in exclude:
continue
for identifier, display_name in transport.get_choices():
yield (identifier, display_name)
def get_transport(mode):
if mode not in app_settings.transport_modes:
return None
module_path, class_name = app_settings.transport_modes[mode].rsplit('.', 1)
try:
module = import_module(module_path)
return getattr(module, class_name)()
except (ImportError, AttributeError):
logger.error('unable to load transport class %s.%s',
module_path, class_name)
return None
def get_transport(identifier):
transports = get_transports()
for transport in transports:
if identifier == transport.identifier:
return transport
return None
__TRANSPORTS = None
def get_transports():
global __TRANSPORTS
if __TRANSPORTS is None:
transports = []
for class_path in app_settings.transport_modes:
module_path, class_name = class_path.rsplit('.', 1)
try:
module = import_module(module_path)
transports.append(getattr(module, class_name)())
except (ImportError, AttributeError), e:
raise ImportError('Unable to load transport class %s' % class_path, e)
__TRANSPORTS = transports
return __TRANSPORTS
def get_template_list(template_list, category):
@ -53,7 +63,16 @@ def get_template(template_list, category):
return select_template(template_list)
class HomepageTransport(object):
identifier = 'homepage'
def get_choices(self):
return (('homepage', _('homepage')),)
class EmailTransport(object):
identifier = 'email'
subject_template_list = [
'portail_citoyen_announces/email/subject_{category}.txt',
'portail_citoyen_announces/email/subject.txt',
@ -67,8 +86,9 @@ class EmailTransport(object):
'portail_citoyen_announces/body_{category}.txt',
'portail_citoyen_announces/body.txt',
]
identifier = 'email'
display_name = _('email')
def get_choices(self):
return (('email', _('email')),)
def get_subscriptions(self, category):
return models.Subscription.objects.filter(category=category,

View File

@ -0,0 +1,10 @@
from django.conf.urls import patterns, include, url
urlpatterns = patterns('',
url(r'^$', 'portail_citoyen_announces.views.homepage_view',
name='announce-homepage'),
url(r'^subscribe/$', 'portail_citoyen_announces.views.subscription_view',
name='announce-subscribe'),
url(r'^feed/$', 'portail_citoyen_announces.views.feed',
name='announce-feed'),
)

View File

@ -1,7 +1,16 @@
from django.views.generic.edit import FormView
from django.views.generic.list import ListView
from django.contrib.syndication.views import Feed
from django.contrib.sites.models import get_current_site
from django.utils.feedgenerator import Atom1Feed
import forms
import models
import transports
import app_settings
class SubscriptionView(FormView):
@ -9,9 +18,67 @@ class SubscriptionView(FormView):
form_class = forms.SubscriptionForm
success_url = '..'
def get_form_kwargs(self, **kwargs):
kwargs = super(SubscriptionView, self).get_form_kwargs(**kwargs)
kwargs['user'] = self.request.user
kwargs['site'] = get_current_site(self.request)
return kwargs
def form_valid(self, form):
form.save()
return super(SubscriptionView, self).form_valid(self, form)
return super(SubscriptionView, self).form_valid(form)
subscription_view = SubscriptionView.as_view()
class AnnounceHomepageView(ListView):
model = models.Announce
template_name = 'portail_citoyen_announces/announce_homepage.html'
transport_identifier = transports.HomepageTransport.identifier
def get_queryset(self):
qs = models.Announce.objects.all()
qs = qs.filter(category__subscription__transport=self.transport_identifier)
if app_settings.feed_homepage_limit:
qs = qs[:app_settings.feed_homepage_limit]
return qs
def get_context_data(self, **kwargs):
ctx = super(AnnounceHomepageView, self).get_context_data(**kwargs)
ctx['id_prefix'] = 'announce-item-'
subscriptions = models.Subscription.objects.filter(
transport=self.transport_identifier,
user=self.request.user).select_related('category')
ctx['subscriptions'] = [ sub.category.name for sub in subscriptions ]
return ctx
homepage_view = AnnounceHomepageView.as_view()
class AnnounceFeed(Feed):
title = app_settings.feed_title
description = app_settings.feed_description
link = app_settings.feed_link
feed_item_link_template = app_settings.feed_item_link_template
feed_type = Atom1Feed
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def items(self):
return models.Announce.objects.order_by('-publication_time')
def item_title(self, item):
return item.title
def item_description(self, item):
return item.text
def item_link(self, item):
return self.feed_item_link_template.format(item.pk)
def item_pubdate(self, item):
return item.publication_time or item.modification_time
feed = AnnounceFeed()

View File

@ -0,0 +1,104 @@
from itertools import chain
from django.forms.widgets import SubWidget, SelectMultiple
from django.forms.util import flatatt
from django.utils.html import conditional_escape
from django.utils.encoding import StrAndUnicode, force_unicode
from django.utils.safestring import mark_safe
class CheckboxInput(SubWidget):
"""
An object used by CheckboxRenderer that represents a single
<input type='checkbox'>.
"""
def __init__(self, name, value, attrs, choice, index):
self.name, self.value = name, value
self.attrs = attrs
self.choice_value = force_unicode(choice[0])
self.choice_label = force_unicode(choice[1])
self.index = index
def __unicode__(self):
return self.render()
def render(self, name=None, value=None, attrs=None, choices=()):
name = name or self.name
value = value or self.value
attrs = attrs or self.attrs
if 'id' in self.attrs:
label_for = ' for="%s_%s"' % (self.attrs['id'], self.index)
else:
label_for = ''
choice_label = conditional_escape(force_unicode(self.choice_label))
return mark_safe(u'<label%s>%s %s</label>' % (label_for, self.tag(), choice_label))
def is_checked(self):
return self.choice_value in self.value
def tag(self):
if 'id' in self.attrs:
self.attrs['id'] = '%s_%s' % (self.attrs['id'], self.index)
final_attrs = dict(self.attrs, type='checkbox', name=self.name, value=self.choice_value)
if self.is_checked():
final_attrs['checked'] = 'checked'
return mark_safe(u'<input%s />' % flatatt(final_attrs))
class CheckboxRenderer(StrAndUnicode):
def __init__(self, name, value, attrs, choices):
self.name, self.value, self.attrs = name, value, attrs
self.choices = choices
def __iter__(self):
for i, choice in enumerate(self.choices):
yield CheckboxInput(self.name, self.value, self.attrs.copy(), choice, i)
def __getitem__(self, idx):
choice = self.choices[idx] # Let the IndexError propogate
return CheckboxInput(self.name, self.value, self.attrs.copy(), choice, idx)
def __unicode__(self):
return self.render()
def render(self):
"""Outputs a <ul> for this set of checkbox fields."""
return mark_safe(u'<ul>\n%s\n</ul>' % u'\n'.join([u'<li>%s</li>'
% force_unicode(w) for w in self]))
class CheckboxMultipleSelect(SelectMultiple):
"""
Checkbox multi select field that enables iteration of each checkbox
Similar to django.forms.widgets.RadioSelect
"""
renderer = CheckboxRenderer
def __init__(self, *args, **kwargs):
# Override the default renderer if we were passed one.
renderer = kwargs.pop('renderer', None)
if renderer:
self.renderer = renderer
super(CheckboxMultipleSelect, self).__init__(*args, **kwargs)
def subwidgets(self, name, value, attrs=None, choices=()):
for widget in self.get_renderer(name, value, attrs, choices):
yield widget
def get_renderer(self, name, value, attrs=None, choices=()):
"""Returns an instance of the renderer."""
if value is None: value = ''
str_values = set([force_unicode(v) for v in value]) # Normalize to string.
if attrs is None:
attrs = {}
if 'id' not in attrs:
attrs['id'] = name
final_attrs = self.build_attrs(attrs)
choices = list(chain(self.choices, choices))
return self.renderer(name, str_values, final_attrs, choices)
def render(self, name, value, attrs=None, choices=()):
return self.get_renderer(name, value, attrs, choices).render()
def id_for_label(self, id_):
if id_:
id_ += '_0'
return id_