cleanup the agenda app
This commit is contained in:
parent
a72832842b
commit
29d25d30d6
@ -10,39 +10,46 @@
|
||||
:license: GNU GPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from django.forms import Form, ModelForm, IntegerField, ChoiceField, \
|
||||
ModelChoiceField, HiddenInput, Select, TextInput
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from mptt.forms import TreeNodeChoiceField
|
||||
|
||||
from utils.forms import CssClassMixin
|
||||
from utils.translation_ext import ugettext as _
|
||||
|
||||
from agenda.models import Item
|
||||
|
||||
|
||||
class ItemForm(ModelForm, CssClassMixin):
|
||||
parent = TreeNodeChoiceField(queryset=Item.objects.all(), label=_("Parent item"), required=False)
|
||||
class ItemForm(forms.ModelForm, CssClassMixin):
|
||||
"""
|
||||
Form to create of update an item.
|
||||
"""
|
||||
parent = TreeNodeChoiceField(queryset=Item.objects.all(),
|
||||
label=_("Parent item"), required=False)
|
||||
|
||||
class Meta:
|
||||
model = Item
|
||||
exclude = ('closed', 'weight', 'related_sid')
|
||||
|
||||
|
||||
def genweightchoices():
|
||||
l = []
|
||||
for i in range(-50, 51):
|
||||
l.append(('%d' % i, i))
|
||||
return l
|
||||
def gen_weight_choices():
|
||||
"""
|
||||
Creates a list of tuples (n, n) for n from -49 to 50.
|
||||
"""
|
||||
return zip(*(range(-50, 51), range(-50, 51)))
|
||||
|
||||
|
||||
class ItemOrderForm(Form, CssClassMixin):
|
||||
weight = ChoiceField(choices=genweightchoices(),
|
||||
widget=Select(attrs={'class': 'menu-weight'}),
|
||||
label="")
|
||||
self = IntegerField(widget=HiddenInput(attrs={'class': 'menu-mlid'}))
|
||||
parent = IntegerField(widget=HiddenInput(attrs={'class': 'menu-plid'}))
|
||||
|
||||
|
||||
class ConfigForm(Form, CssClassMixin):
|
||||
pass
|
||||
|
||||
class ItemOrderForm(forms.Form, CssClassMixin):
|
||||
"""
|
||||
Form to change the order of the items.
|
||||
"""
|
||||
weight = forms.ChoiceField(
|
||||
choices=gen_weight_choices(),
|
||||
widget=forms.Select(attrs={'class': 'menu-weight'}),
|
||||
)
|
||||
self = forms.IntegerField(
|
||||
widget=forms.HiddenInput(attrs={'class': 'menu-mlid'}),
|
||||
)
|
||||
parent = forms.IntegerField(
|
||||
widget=forms.HiddenInput(attrs={'class': 'menu-plid'}),
|
||||
)
|
||||
|
@ -13,19 +13,22 @@
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
# for python 2.5 support
|
||||
import simplejson as json
|
||||
|
||||
from django.db import models
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext as _, ugettext_noop
|
||||
|
||||
from mptt.models import MPTTModel, TreeForeignKey
|
||||
|
||||
from config.models import config
|
||||
from openslides.config.models import config
|
||||
|
||||
from projector.projector import SlideMixin
|
||||
from projector.api import register_slidemodel, get_slide_from_sid
|
||||
from openslides.projector.projector import SlideMixin
|
||||
from openslides.projector.api import (register_slidemodel, get_slide_from_sid,
|
||||
register_slidefunc, split_sid)
|
||||
|
||||
from utils.translation_ext import ugettext as _
|
||||
from openslides.agenda.slides import agenda_show
|
||||
|
||||
|
||||
class Item(MPTTModel, SlideMixin):
|
||||
@ -36,18 +39,25 @@ class Item(MPTTModel, SlideMixin):
|
||||
"""
|
||||
prefix = 'item'
|
||||
|
||||
title = models.CharField(null=True, max_length=256, verbose_name=_("Title"))
|
||||
title = models.CharField(null=True, max_length=255, verbose_name=_("Title"))
|
||||
text = models.TextField(null=True, blank=True, verbose_name=_("Text"))
|
||||
comment = models.TextField(null=True, blank=True, verbose_name=_("Comment"))
|
||||
closed = models.BooleanField(default=False, verbose_name=_("Closed"))
|
||||
weight = models.IntegerField(default=0, verbose_name=_("Weight"))
|
||||
parent = TreeForeignKey('self', null=True, blank=True, related_name='children')
|
||||
related_sid = models.CharField(null=True, blank=True, max_length=64)
|
||||
parent = TreeForeignKey('self', null=True, blank=True,
|
||||
related_name='children')
|
||||
related_sid = models.CharField(null=True, blank=True, max_length=63)
|
||||
|
||||
def get_related_slide(self):
|
||||
"""
|
||||
return the object, of which the item points.
|
||||
"""
|
||||
return get_slide_from_sid(self.related_sid, True)
|
||||
|
||||
def get_related_type(self):
|
||||
"""
|
||||
return the type of the releated slide.
|
||||
"""
|
||||
return self.get_related_slide().prefix
|
||||
|
||||
def print_related_type(self):
|
||||
@ -60,6 +70,9 @@ class Item(MPTTModel, SlideMixin):
|
||||
return _(self.get_related_type().capitalize())
|
||||
|
||||
def get_title(self):
|
||||
"""
|
||||
return the title of this item.
|
||||
"""
|
||||
if self.related_sid is None:
|
||||
return self.title
|
||||
return self.get_related_slide().get_agenda_title()
|
||||
@ -69,7 +82,6 @@ class Item(MPTTModel, SlideMixin):
|
||||
Return a map with all Data for the Slide
|
||||
"""
|
||||
if config['presentation_argument'] == 'summary':
|
||||
print 'soweit schonmal'
|
||||
data = {
|
||||
'title': self.get_title(),
|
||||
'items': self.get_children(),
|
||||
@ -92,16 +104,16 @@ class Item(MPTTModel, SlideMixin):
|
||||
self.closed = closed
|
||||
self.save()
|
||||
|
||||
@property
|
||||
def active_parent(self):
|
||||
"""
|
||||
Return True if the item has a active parent
|
||||
"""
|
||||
sid = get_active_slide(only_sid=True).split()
|
||||
if len(sid) == 2 and sid[0] == self.prefix:
|
||||
if self.get_ancestors().filter(pk=sid[0]).exists():
|
||||
return True
|
||||
return False
|
||||
## @property
|
||||
## def active_parent(self):
|
||||
## """
|
||||
## Return True if the item has an active parent.
|
||||
## """
|
||||
## sid = get_active_slide(only_sid=True).split()
|
||||
## if len(sid) == 2 and sid[0] == self.prefix:
|
||||
## if self.get_ancestors().filter(pk=sid[0]).exists():
|
||||
## return True
|
||||
## return False
|
||||
|
||||
@property
|
||||
def weight_form(self):
|
||||
@ -157,8 +169,8 @@ class Item(MPTTModel, SlideMixin):
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
('can_see_agenda', _("Can see agenda", fixstr=True)),
|
||||
('can_manage_agenda', _("Can manage agenda", fixstr=True)),
|
||||
('can_see_agenda', ugettext_noop("Can see agenda")),
|
||||
('can_manage_agenda', ugettext_noop("Can manage agenda")),
|
||||
)
|
||||
|
||||
class MPTTMeta:
|
||||
@ -166,21 +178,4 @@ class Item(MPTTModel, SlideMixin):
|
||||
|
||||
|
||||
register_slidemodel(Item, control_template='agenda/control_item.html')
|
||||
|
||||
# TODO: put this in another file
|
||||
|
||||
from projector.api import register_slidefunc
|
||||
from agenda.slides import agenda_show
|
||||
|
||||
register_slidefunc('agenda', agenda_show, weight=-1, name=_('Agenda'))
|
||||
|
||||
|
||||
from django.dispatch import receiver
|
||||
from openslides.config.signals import default_config_value
|
||||
|
||||
|
||||
@receiver(default_config_value, dispatch_uid="agenda_default_config")
|
||||
def default_config(sender, key, **kwargs):
|
||||
return {
|
||||
'agenda_countdown_time': 60,
|
||||
}.get(key)
|
||||
|
@ -71,20 +71,27 @@
|
||||
<th class="tabledrag-hide">{% trans "Weight" %}</th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% if items %}
|
||||
<tr class="topline {% if overview %}activeline{% endif %}">
|
||||
<tr class="topline{% if active_sid == 'agenda' %} activeline{% endif %}">
|
||||
<td></td>
|
||||
<td>
|
||||
<b>{% trans "Agenda" %}</b>
|
||||
<strong>{% trans "Agenda" %}</strong>
|
||||
</td>
|
||||
{% if perms.agenda.can_manage_agenda %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
{% if perms.agenda.can_manage_agenda or perms.projector.can_manage_projector %}
|
||||
<td></td>
|
||||
<td>
|
||||
{% if perms.projector.can_manage_projector %}
|
||||
<span>
|
||||
<a class="activate_link{% if active_sid == 'agenda' %} active{% endif %}" href="{% url projector_activate_slide 'agenda' %}" title="{% trans 'Activate item' %}">
|
||||
<span></span>
|
||||
</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
|
||||
{% if items %}
|
||||
{% for item in items %}
|
||||
<tr class="draggable{% cycle ' odd' '' %}
|
||||
{% if item.active %}activeline{% endif %}">
|
||||
|
@ -47,7 +47,8 @@ class ItemTest(TestCase):
|
||||
self.assertFalse(self.item4 in self.item1.get_children())
|
||||
|
||||
l = Item.objects.all()
|
||||
self.assertEqual(str(l), "[<Item: item1>, <Item: item1A>, <Item: item1Aa>, <Item: item2>]")
|
||||
self.assertEqual(str(l),
|
||||
"[<Item: item1>, <Item: item1A>, <Item: item1Aa>, <Item: item2>]")
|
||||
|
||||
def testForms(self):
|
||||
for item in Item.objects.all():
|
||||
|
@ -10,8 +10,9 @@
|
||||
:license: GNU GPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from django.conf.urls.defaults import *
|
||||
from agenda.views import Overview, View, SetClosed, ItemUpdate, ItemCreate, ItemDelete, AgendaPDF
|
||||
from django.conf.urls.defaults import url, patterns
|
||||
from agenda.views import (Overview, View, SetClosed, ItemUpdate, ItemCreate,
|
||||
ItemDelete, AgendaPDF)
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^$',
|
||||
|
@ -11,28 +11,33 @@
|
||||
"""
|
||||
from reportlab.platypus import Paragraph
|
||||
|
||||
from django.db.models import Model
|
||||
from django.core.context_processors import csrf
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib import messages
|
||||
from django.db import transaction
|
||||
from django.db.models import Model
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.core.context_processors import csrf
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
|
||||
from openslides.utils.pdf import stylesheet
|
||||
from openslides.utils.views import (TemplateView, RedirectView, UpdateView, CreateView,
|
||||
DeleteView, PDFView, FormView, DetailView)
|
||||
from openslides.utils.views import (TemplateView, RedirectView, UpdateView,
|
||||
CreateView, DeleteView, PDFView, DetailView)
|
||||
from openslides.utils.template import Tab
|
||||
from openslides.utils.utils import html_strong
|
||||
|
||||
from openslides.config.models import config
|
||||
|
||||
from openslides.projector.api import get_active_slide, set_active_slide
|
||||
from openslides.projector.api import get_active_slide
|
||||
from openslides.projector.projector import Widget, SLIDE
|
||||
|
||||
from openslides.agenda.models import Item
|
||||
from openslides.agenda.forms import ItemOrderForm, ItemForm, ConfigForm
|
||||
from openslides.agenda.forms import ItemOrderForm, ItemForm
|
||||
|
||||
|
||||
class Overview(TemplateView):
|
||||
"""
|
||||
Show all agenda items, and update there range via post.
|
||||
"""
|
||||
permission_required = 'agenda.can_see_agenda'
|
||||
template_name = 'agenda/overview.html'
|
||||
|
||||
@ -40,14 +45,18 @@ class Overview(TemplateView):
|
||||
context = super(Overview, self).get_context_data(**kwargs)
|
||||
context.update({
|
||||
'items': Item.objects.all(),
|
||||
'overview': get_active_slide(only_sid=True) == 'agenda_show',
|
||||
'active_sid': get_active_slide(only_sid=True),
|
||||
})
|
||||
return context
|
||||
|
||||
@transaction.commit_manually
|
||||
def post(self, request, *args, **kwargs):
|
||||
#todo: check for permission
|
||||
context = self.get_context_data(**kwargs)
|
||||
#todo: check for any erros in the forms befor saving the data
|
||||
if not request.user.has_perm('agenda.can_manage_agenda'):
|
||||
messages.error(request,
|
||||
_('You are not permitted to manage the agenda.'))
|
||||
return self.render_to_response(context)
|
||||
transaction.commit()
|
||||
for item in Item.objects.all():
|
||||
form = ItemOrderForm(request.POST, prefix="i%d" % item.id)
|
||||
if form.is_valid():
|
||||
@ -58,12 +67,21 @@ class Overview(TemplateView):
|
||||
item.weight = form.cleaned_data['weight']
|
||||
item.parent = parent
|
||||
Model.save(item)
|
||||
|
||||
else:
|
||||
transaction.rollback()
|
||||
messages.error(request,
|
||||
_('Errors when reordering of the agenda'))
|
||||
return self.render_to_response(context)
|
||||
Item.objects.rebuild()
|
||||
# TODO: assure, that it is a valid tree
|
||||
transaction.commit()
|
||||
return self.render_to_response(context)
|
||||
|
||||
|
||||
class View(DetailView):
|
||||
"""
|
||||
Show an agenda item.
|
||||
"""
|
||||
permission_required = 'agenda.can_see_agenda'
|
||||
template_name = 'agenda/view.html'
|
||||
model = Item
|
||||
@ -72,7 +90,7 @@ class View(DetailView):
|
||||
|
||||
class SetClosed(RedirectView, SingleObjectMixin):
|
||||
"""
|
||||
Close or open an Item.
|
||||
Close or open an item.
|
||||
"""
|
||||
permission_required = 'agenda.can_manage_agenda'
|
||||
allow_ajax = True
|
||||
@ -100,6 +118,9 @@ class SetClosed(RedirectView, SingleObjectMixin):
|
||||
|
||||
|
||||
class ItemUpdate(UpdateView):
|
||||
"""
|
||||
Update an existing item.
|
||||
"""
|
||||
permission_required = 'agenda.can_manage_agenda'
|
||||
template_name = 'agenda/edit.html'
|
||||
model = Item
|
||||
@ -109,13 +130,18 @@ class ItemUpdate(UpdateView):
|
||||
apply_url = 'item_edit'
|
||||
|
||||
def get_success_url(self):
|
||||
messages.success(self.request, _("Item <b>%s</b> was successfully modified.") % self.request.POST['title'])
|
||||
messages.success(self.request,
|
||||
_("Item %s was successfully modified.") \
|
||||
% html_strong(self.request.POST['title']))
|
||||
if 'apply' in self.request.POST:
|
||||
return ''
|
||||
return reverse(super(UpdateView, self).get_success_url())
|
||||
|
||||
|
||||
class ItemCreate(CreateView):
|
||||
"""
|
||||
Create a new item.
|
||||
"""
|
||||
permission_required = 'agenda.can_manage_agenda'
|
||||
template_name = 'agenda/edit.html'
|
||||
model = Item
|
||||
@ -125,7 +151,9 @@ class ItemCreate(CreateView):
|
||||
apply_url = 'item_edit'
|
||||
|
||||
def get_success_url(self):
|
||||
messages.success(self.request, _("Item <b>%s</b> was successfully created.") % self.request.POST['title'])
|
||||
messages.success(self.request,
|
||||
_("Item %s was successfully created.") \
|
||||
% html_strong(self.request.POST['title']))
|
||||
if 'apply' in self.request.POST:
|
||||
return reverse(self.get_apply_url(), args=[self.object.id])
|
||||
return reverse(super(CreateView, self).get_success_url())
|
||||
@ -133,7 +161,7 @@ class ItemCreate(CreateView):
|
||||
|
||||
class ItemDelete(DeleteView):
|
||||
"""
|
||||
Delete an Item.
|
||||
Delete an item.
|
||||
"""
|
||||
permission_required = 'agenda.can_manage_agenda'
|
||||
model = Item
|
||||
@ -144,27 +172,69 @@ class ItemDelete(DeleteView):
|
||||
|
||||
if 'all' in request.POST:
|
||||
self.object.delete(with_children=True)
|
||||
messages.success(request, _("Item <b>%s</b> and his children were successfully deleted.") % self.object)
|
||||
messages.success(request,
|
||||
_("Item %s and his children were successfully deleted.") \
|
||||
% html_strong(self.object))
|
||||
else:
|
||||
self.object.delete(with_children=False)
|
||||
messages.success(request, _("Item <b>%s</b> was successfully deleted.") % self.object)
|
||||
messages.success(request,
|
||||
_("Item %s was successfully deleted.") \
|
||||
% html_strong(self.object))
|
||||
|
||||
def gen_confirm_form(self, request, message, url, singleitem=False):
|
||||
if singleitem:
|
||||
messages.warning(request, '%s<form action="%s" method="post"><input type="hidden" value="%s" name="csrfmiddlewaretoken"><input type="submit" value="%s" /> <input type="button" value="%s"></form>' % (message, url, csrf(request)['csrf_token'], _("Yes"), _("No")))
|
||||
messages.warning(
|
||||
request,
|
||||
"""
|
||||
%s
|
||||
<form action="%s" method="post">
|
||||
<input type="hidden" value="%s" name="csrfmiddlewaretoken">
|
||||
<input type="submit" value="%s">
|
||||
<input type="button" value="%s">
|
||||
</form>
|
||||
"""
|
||||
% (message, url, csrf(request)['csrf_token'], _("Yes"),
|
||||
_("No"))
|
||||
)
|
||||
else:
|
||||
messages.warning(request, '%s<form action="%s" method="post"><input type="hidden" value="%s" name="csrfmiddlewaretoken"><input type="submit" value="%s" /> <input type="submit" name="all" value="%s" /> <input type="button" value="%s"></form>' % (message, url, csrf(request)['csrf_token'], _("Yes"), _("Yes, with all child items."), _("No")))
|
||||
messages.warning(
|
||||
request,
|
||||
"""
|
||||
%s
|
||||
<form action="%s" method="post">
|
||||
<input type="hidden" value="%s" name="csrfmiddlewaretoken">
|
||||
<input type="submit" value="%s">
|
||||
<input type="submit" name="all" value="%s">
|
||||
<input type="button" value="%s">
|
||||
</form>
|
||||
"""
|
||||
% (message, url, csrf(request)['csrf_token'], _("Yes"),
|
||||
_("Yes, with all child items."), _("No"))
|
||||
)
|
||||
|
||||
def confirm_form(self, request, object, item=None):
|
||||
if item is None:
|
||||
item = object
|
||||
if item.get_children():
|
||||
self.gen_confirm_form(request, _('Do you really want to delete <b>%s</b>?') % item, item.get_absolute_url('delete'), False)
|
||||
self.gen_confirm_form(
|
||||
request,
|
||||
_('Do you really want to delete %s?') % html_strong(item),
|
||||
item.get_absolute_url('delete'),
|
||||
False,
|
||||
)
|
||||
else:
|
||||
self.gen_confirm_form(request, _('Do you really want to delete <b>%s</b>?') % item, item.get_absolute_url('delete'), True)
|
||||
self.gen_confirm_form(
|
||||
request,
|
||||
_('Do you really want to delete %s?') % html_strong(item),
|
||||
item.get_absolute_url('delete'),
|
||||
True,
|
||||
)
|
||||
|
||||
|
||||
class AgendaPDF(PDFView):
|
||||
"""
|
||||
Create a full agenda-PDF.
|
||||
"""
|
||||
permission_required = 'agenda.can_see_agenda'
|
||||
filename = _('Agenda')
|
||||
document_title = _('Agenda')
|
||||
@ -173,39 +243,31 @@ class AgendaPDF(PDFView):
|
||||
for item in Item.objects.all():
|
||||
ancestors = item.get_ancestors()
|
||||
if ancestors:
|
||||
space = " " * ancestors.count()
|
||||
story.append(Paragraph("%s%s" % (space, item.get_title()), stylesheet['Subitem']))
|
||||
space = " " * 6 * ancestors.count()
|
||||
story.append(Paragraph("%s%s" % (space, item.get_title()),
|
||||
stylesheet['Subitem']))
|
||||
else:
|
||||
story.append(Paragraph(item.get_title(), stylesheet['Item']))
|
||||
|
||||
|
||||
#
|
||||
# rene: empty for now so comment it out to keep it from appearing in the settings
|
||||
#
|
||||
#class Config(FormView):
|
||||
# permission_required = 'config.can_manage_config'
|
||||
# form_class = ConfigForm
|
||||
# template_name = 'agenda/config.html'
|
||||
#
|
||||
# def get_initial(self):
|
||||
# return {}
|
||||
#
|
||||
# def form_valid(self, form):
|
||||
# messages.success(self.request, _('Agenda settings successfully saved.'))
|
||||
# return super(Config, self).form_valid(form)
|
||||
|
||||
|
||||
def register_tab(request):
|
||||
"""
|
||||
register the agenda tab.
|
||||
"""
|
||||
selected = True if request.path.startswith('/agenda/') else False
|
||||
return Tab(
|
||||
title=_('Agenda'),
|
||||
url=reverse('item_overview'),
|
||||
permission=request.user.has_perm('agenda.can_see_agenda') or request.user.has_perm('agenda.can_manage_agenda'),
|
||||
permission=request.user.has_perm('agenda.can_see_agenda')
|
||||
or request.user.has_perm('agenda.can_manage_agenda'),
|
||||
selected=selected,
|
||||
)
|
||||
|
||||
|
||||
def get_widgets(request):
|
||||
"""
|
||||
return the agenda widget for the projector-tab.
|
||||
"""
|
||||
return [
|
||||
Widget(
|
||||
name='agenda',
|
||||
|
@ -31,9 +31,6 @@ $(function () {
|
||||
$('tr').removeClass('activeline');
|
||||
link.parent().parent().parent().addClass('activeline');
|
||||
link.addClass('active');
|
||||
},
|
||||
error: function () {
|
||||
alert("Ajax Error");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -126,3 +126,7 @@ def encodedict(dict):
|
||||
for key in dict:
|
||||
newdict[key] = [unicode(dict[key][0].decode('utf-8'))]
|
||||
return newdict
|
||||
|
||||
|
||||
def html_strong(string):
|
||||
return "<strong>%s</strong>" % string
|
||||
|
Loading…
Reference in New Issue
Block a user