This commit is contained in:
Oskar Hahn 2012-07-04 12:52:09 +02:00
commit 76d2a33fc2
14 changed files with 213 additions and 139 deletions

View File

@ -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', 'releated_sid')
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'}),
)

View File

@ -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,47 +39,56 @@ 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')
releated_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_releated_slide(self):
return get_slide_from_sid(self.releated_sid, True)
def get_releated_type(self):
return self.get_releated_slide().prefix
def print_releated_type(self):
def get_related_slide(self):
"""
Print the type of the releated item.
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):
"""
Print the type of the related item.
For use in Template
??Why does {% trans item.print_releated_type|capfirst %} not work??
??Why does {% trans item.print_related_type|capfirst %} not work??
"""
return _(self.get_releated_type().capitalize())
return _(self.get_related_type().capitalize())
def get_title(self):
if self.releated_sid is None:
"""
return the title of this item.
"""
if self.related_sid is None:
return self.title
return self.get_releated_slide().get_agenda_title()
return self.get_related_slide().get_agenda_title()
def slide(self):
"""
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(),
'template': 'projector/AgendaSummary.html',
}
elif self.releated_sid:
data = self.get_releated_slide().slide()
elif self.related_sid:
data = self.get_related_slide().slide()
else:
data = {
'item': self,
@ -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):
@ -142,12 +154,12 @@ class Item(MPTTModel, SlideMixin):
* delete
"""
if link == 'view':
if self.releated_sid:
return self.get_releated_slide().get_absolute_url(link)
if self.related_sid:
return self.get_related_slide().get_absolute_url(link)
return reverse('item_view', args=[str(self.id)])
if link == 'edit':
if self.releated_sid:
return self.get_releated_slide().get_absolute_url(link)
if self.related_sid:
return self.get_related_slide().get_absolute_url(link)
return reverse('item_edit', args=[str(self.id)])
if link == 'delete':
return reverse('item_delete', args=[str(self.id)])
@ -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)

View File

@ -19,8 +19,8 @@
<div class="dragcell"></div>
{% endif %}
<a href="{{ item.get_absolute_url }}">{{ item }}</a>
{% if item.releated_sid %}
({{ item.print_releated_type }})
{% if item.related_sid %}
({{ item.print_related_type }})
{% endif %}
</td>
{% if perms.agenda.can_manage_agenda %}

View File

@ -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 %}">

View File

@ -32,7 +32,7 @@
{% for p in item.get_ancestors %}
<span class="indentation">&nbsp;</span>
{% endfor %}
<a href="{% model_url item 'view' %}">{{ item }}</a>{% if item.releated_sid %} ({{ item.print_releated_type }}){% endif %}
<a href="{% model_url item 'view' %}">{{ item }}</a>{% if item.related_sid %} ({{ item.print_related_type }}){% endif %}
{% if not item.is_leaf_node %}
<a class="activate_link" href="{% url projector_activate_slide item.sid 'summary' %}"><img src="{% static 'images/icons/view-list-tree.png' %}" title="{% trans 'Activate summary for this item' %}"></a>
{% endif %}

View File

@ -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():

View File

@ -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'^$',

View File

@ -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 = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" * ancestors.count()
story.append(Paragraph("%s%s" % (space, item.get_title()), stylesheet['Subitem']))
space = "&nbsp;" * 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',

View File

@ -415,7 +415,7 @@ class Application(models.Model, SlideMixin):
'You can not delete it.')
for item in Item.objects.filter(releated_sid=self.sid):
for item in Item.objects.filter(related_sid=self.sid):
item.delete()
super(Application, self).delete()

View File

@ -683,7 +683,7 @@ class CreateAgendaItem(RedirectView):
def pre_redirect(self, request, *args, **kwargs):
self.application = Application.objects.get(pk=kwargs['application_id'])
self.item = Item(releated_sid=self.application.sid)
self.item = Item(related_sid=self.application.sid)
self.item.save()
def get_redirect_url(self, **kwargs):

View File

@ -112,12 +112,12 @@ class Assignment(models.Model, SlideMixin):
def vote_results(self):
"""
returns a table represented as a list with all candidates from all
releated polls and their vote results.
related polls and their vote results.
"""
vote_results_dict = {}
# All polls releated to this assigment
# All polls related to this assigment
polls = self.poll_set.all()
# All PollOption-Objects releated to this assignment
# All PollOption-Objects related to this assignment
options = []
for poll in polls:
options += poll.get_options()
@ -130,13 +130,13 @@ class Assignment(models.Model, SlideMixin):
for poll in polls:
try:
polloption = poll.get_options().get(candidate=candidate)
# candidate is releated to this poll
# candidate is related to this poll
votes = {}
for vote in polloption.get_votes():
votes[vote.value] = vote.get_weight()
vote_results_dict[candidate].append(votes)
except AssignmentOption.DoesNotExist:
# candidate not in releated to this poll
# candidate not in related to this poll
vote_results_dict[candidate].append(None)
return vote_results_dict
@ -145,7 +145,7 @@ class Assignment(models.Model, SlideMixin):
return self.name
def delete(self):
for item in Item.objects.filter(releated_sid=self.sid):
for item in Item.objects.filter(related_sid=self.sid):
item.delete()
super(Assignment, self).delete()

View File

@ -463,7 +463,7 @@ class CreateAgendaItem(RedirectView):
def pre_redirect(self, request, *args, **kwargs):
self.assignment = Assignment.objects.get(pk=kwargs['assignment_id'])
self.item = Item(releated_sid=self.assignment.sid)
self.item = Item(related_sid=self.assignment.sid)
self.item.save()
def get_redirect_url(self, **kwargs):

View File

@ -31,9 +31,6 @@ $(function () {
$('tr').removeClass('activeline');
link.parent().parent().parent().addClass('activeline');
link.addClass('active');
},
error: function () {
alert("Ajax Error");
}
});
});

View File

@ -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