finish views and cms plugins
This commit is contained in:
parent
f62aa82eee
commit
ecbfad9bf5
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
{% include "portail_citoyen_announces/announce_list.html" %}
|
||||
{% endblock %}
|
|
@ -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>
|
|
@ -0,0 +1 @@
|
|||
{% include "portail_citoyen_announces/announce_list.html" %}
|
|
@ -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 %}
|
|
@ -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 %}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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'),
|
||||
)
|
|
@ -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()
|
||||
|
|
|
@ -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_
|
Reference in New Issue