Merge pull request #535 from ostcar/ticket_433

Ticket 433
This commit is contained in:
Oskar Hahn 2013-02-18 09:59:48 -08:00
commit 07e9f4a82d
41 changed files with 3308 additions and 918 deletions

View File

@ -7,3 +7,4 @@ Authors of OpenSlides in chronological order of first contribution:
Andy Kittner <andkit@gmx.net>
Moira Brülisauer <moira.bruelisauer@piratenpartei.ch> (French translation)
Alexis Roussel <alexis.roussel@partipirate.ch> (French translation)
Stefan Frauenknecht <stefan@frauenknecht.net>

4
THANKS
View File

@ -8,9 +8,13 @@ OpenSlides uses parts of the following projects:
* jQuery
<http://www.jquery.com/>
and some addons: cookie, form, once, templating
* jQuery UI
<http://jqueryui.com/>
custom ui components: core, widget, mouse, sortable, datepicker, slider
with css theme 'smoothness'
and some addons: slider access, timepicker
* Twitter Bootstrap
<http://twitter.github.com/bootstrap/>

View File

@ -10,14 +10,14 @@
:license: GNU GPL, see LICENSE for more details.
"""
import re
from django import forms
from django.utils.translation import ugettext_lazy as _
from mptt.forms import TreeNodeChoiceField
from openslides.utils.forms import CssClassMixin
from openslides.agenda.models import Item
from .models import Item
class ItemForm(forms.ModelForm, CssClassMixin):
@ -27,6 +27,13 @@ class ItemForm(forms.ModelForm, CssClassMixin):
parent = TreeNodeChoiceField(
queryset=Item.objects.all(), label=_("Parent item"), required=False)
duration = forms.RegexField(
regex=re.compile('[0-99]:[0-5][0-9]'),
error_message=_("Invalid format. Hours from 0 to 99 and minutes from 00 to 59"),
max_length=5,
required=False,
label=_("Duration (hh:mm)"))
class Meta:
model = Item
exclude = ('closed', 'weight', 'related_sid')
@ -45,11 +52,15 @@ class ItemOrderForm(forms.Form, CssClassMixin):
"""
weight = forms.ChoiceField(
choices=gen_weight_choices(),
widget=forms.Select(attrs={'class': 'menu-weight'}),
)
widget=forms.Select(attrs={'class': 'menu-weight'}))
self = forms.IntegerField(
widget=forms.HiddenInput(attrs={'class': 'menu-mlid'}),
)
widget=forms.HiddenInput(attrs={'class': 'menu-mlid'}))
parent = forms.IntegerField(
widget=forms.HiddenInput(attrs={'class': 'menu-plid'}),
)
widget=forms.HiddenInput(attrs={'class': 'menu-plid'}))
class ConfigForm(CssClassMixin, forms.Form):
agenda_start_event_date_time = forms.CharField(
widget=forms.DateTimeInput(format='%d.%m.%Y %H:%M'),
required=False,
label=_("Begin of event"))

View File

@ -20,7 +20,7 @@ from openslides.config.models import config
from openslides.projector.projector import SlideMixin
from openslides.projector.api import (
register_slidemodel, get_slide_from_sid, register_slidefunc)
from openslides.agenda.slides import agenda_show
from .slides import agenda_show
class Item(MPTTModel, SlideMixin):
@ -31,6 +31,13 @@ class Item(MPTTModel, SlideMixin):
"""
prefix = 'item'
AGENDA_ITEM = 1
ORGANIZATIONAL_ITEM = 2
ITEM_TYPE = (
(AGENDA_ITEM, _('Agenda item')),
(ORGANIZATIONAL_ITEM, _('Organizational item')))
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"))
@ -38,6 +45,8 @@ class Item(MPTTModel, SlideMixin):
weight = models.IntegerField(default=0, verbose_name=_("Weight"))
parent = TreeForeignKey('self', null=True, blank=True,
related_name='children')
type = models.IntegerField(max_length=1, choices=ITEM_TYPE, default=AGENDA_ITEM, verbose_name=_("Type"))
duration = models.CharField(null=True, blank=True, max_length=5, verbose_name=_("Duration (hh:mm)"))
related_sid = models.CharField(null=True, blank=True, max_length=63)
def get_related_slide(self):
@ -170,6 +179,7 @@ class Item(MPTTModel, SlideMixin):
permissions = (
('can_see_agenda', ugettext_noop("Can see agenda")),
('can_manage_agenda', ugettext_noop("Can manage agenda")),
('can_see_orga_items', ugettext_noop("Can see orga items and time scheduling of agenda")),
)
class MPTTMeta:

View File

@ -16,7 +16,7 @@ from django.utils.translation import ugettext as _
def agenda_show():
from openslides.agenda.models import Item
data = {}
items = Item.objects.filter(parent=None)
items = Item.objects.filter(parent=None, type__exact=Item.AGENDA_ITEM)
data['title'] = _("Agenda")
data['items'] = items
data['template'] = 'projector/AgendaSummary.html'

View File

@ -0,0 +1,89 @@
/*
* jQuery UI Slider Access
* By: Trent Richardson [http://trentrichardson.com]
* Version 0.3
* Last Modified: 10/20/2012
*
* Copyright 2011 Trent Richardson
* Dual licensed under the MIT and GPL licenses.
* http://trentrichardson.com/Impromptu/GPL-LICENSE.txt
* http://trentrichardson.com/Impromptu/MIT-LICENSE.txt
*
*/
(function($){
$.fn.extend({
sliderAccess: function(options){
options = options || {};
options.touchonly = options.touchonly !== undefined? options.touchonly : true; // by default only show it if touch device
if(options.touchonly === true && !("ontouchend" in document))
return $(this);
return $(this).each(function(i,obj){
var $t = $(this),
o = $.extend({},{
where: 'after',
step: $t.slider('option','step'),
upIcon: 'ui-icon-plus',
downIcon: 'ui-icon-minus',
text: false,
upText: '+',
downText: '-',
buttonset: true,
buttonsetTag: 'span',
isRTL: false
}, options),
$buttons = $('<'+ o.buttonsetTag +' class="ui-slider-access">'+
'<button data-icon="'+ o.downIcon +'" data-step="'+ (o.isRTL? o.step : o.step*-1) +'">'+ o.downText +'</button>'+
'<button data-icon="'+ o.upIcon +'" data-step="'+ (o.isRTL? o.step*-1 : o.step) +'">'+ o.upText +'</button>'+
'</'+ o.buttonsetTag +'>');
$buttons.children('button').each(function(j, jobj){
var $jt = $(this);
$jt.button({
text: o.text,
icons: { primary: $jt.data('icon') }
})
.click(function(e){
var step = $jt.data('step'),
curr = $t.slider('value'),
newval = curr += step*1,
minval = $t.slider('option','min'),
maxval = $t.slider('option','max'),
slidee = $t.slider("option", "slide") || function(){},
stope = $t.slider("option", "stop") || function(){};
e.preventDefault();
if(newval < minval || newval > maxval)
return;
$t.slider('value', newval);
slidee.call($t, null, { value: newval });
stope.call($t, null, { value: newval });
});
});
// before or after
$t[o.where]($buttons);
if(o.buttonset){
$buttons.removeClass('ui-corner-right').removeClass('ui-corner-left').buttonset();
$buttons.eq(0).addClass('ui-corner-left');
$buttons.eq(1).addClass('ui-corner-right');
}
// adjust the width so we don't break the original layout
var bOuterWidth = $buttons.css({
marginLeft: ((o.where == 'after' && !o.isRTL) || (o.where == 'before' && o.isRTL)? 10:0),
marginRight: ((o.where == 'before' && !o.isRTL) || (o.where == 'after' && o.isRTL)? 10:0)
}).outerWidth(true) + 5;
var tOuterWidth = $t.outerWidth(true);
$t.css('display','inline-block').width(tOuterWidth-bOuterWidth);
});
}
});
})(jQuery);

File diff suppressed because it is too large Load Diff

View File

@ -9,3 +9,15 @@ tr.topline td {
border-bottom: 1px solid #333333;
background-color: #CDCDCD;
}
table#agendatime {
float: right;
width: auto;
margin-bottom: 1em;
font-size: 12px;
}
table#agendatime td {
padding: 3px;
white-space: nowrap;
}

View File

@ -0,0 +1,46 @@
/**
* OpenSlides timepicker style
*
* :copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
* :license: GNU GPL, see LICENSE for more details.
*/
.ui-timepicker-div .ui-widget-header {
margin-bottom: 8px;
}
.ui-timepicker-div dl {
text-align: left;
}
.ui-timepicker-div dl dt {
height: 25px;
margin-bottom: -25px;
}
.ui-timepicker-div dl dd {
margin: 0 10px 10px 65px;
}
.ui-timepicker-div td {
font-size: 90%;
}
.ui-tpicker-grid-label {
background: none;
border: none;
margin: 0;
padding: 0;
}
.ui-timepicker-rtl{
direction: rtl;
}
.ui-timepicker-rtl dl {
text-align: right;
}
.ui-timepicker-rtl dl dd {
margin: 0 65px 10px 10px;
}

View File

@ -1,24 +1,85 @@
{% extends "config/base_config.html" %}
{% load i18n %}
{% get_current_language as LANGUAGE_CODE %}
{% load staticfiles %}
{% block header %}
<link type="text/css" rel="stylesheet" media="all" href="{% static 'styles/jquery-ui/jquery-ui.custom.min.css' %}" />
<link type="text/css" rel="stylesheet" media="all" href="{% static 'styles/timepicker.css' %}" />
{% endblock %}
{% block javascript %}
<script type="text/javascript" src="{% static 'javascript/jquery-ui.custom.min.js' %}"></script>
<script type="text/javascript" src="{% static 'javascript/jquery-ui-timepicker-addon.js' %}"></script>
<script type="text/javascript" src="{% static 'javascript/jquery-ui-sliderAccess.js' %}"></script>
<script type="text/javascript">
$(function() {
$.datepicker.regional['{{ LANGUAGE_CODE }}'] = {
prevText: 'previous month',
nextText: 'next month',
monthNames: [
'{% trans 'January' %}', '{% trans 'February' %}', '{% trans 'March' %}',
'{% trans 'April' %}', '{% trans 'May' %}', '{% trans 'June' %}',
'{% trans 'July' %}', '{% trans 'August' %}', '{% trans 'September' %}',
'{% trans 'October' %}', '{% trans 'November' %}', '{% trans 'December' %}'
],
monthNamesShort: [
'{% trans 'Jan' %}', '{% trans 'Feb' %}', '{% trans 'Mar' %}',
'{% trans 'Apr' %}', '{% trans 'May' %}', '{% trans 'Jun' %}',
'{% trans 'Jul' %}', '{% trans 'Aug' %}', '{% trans 'Sep' %}',
'{% trans 'Oct' %}', '{% trans 'Nov' %}', '{% trans 'Dec' %}'
],
dayNames: [
'{% trans 'Sunday' %}', '{% trans 'Monday' %}', '{% trans 'Tuesdey' %}', '{% trans 'Wednesday' %}',
'{% trans 'Thursday' %}', '{% trans 'Friday' %}', '{% trans 'Saturday' %}'
],
dayNamesMin: [
'{% trans 'Su' %}', '{% trans 'Mo' %}', '{% trans 'Tu' %}', '{% trans 'We' %}',
'{% trans 'Th' %}', '{% trans 'Fr' %}', '{% trans 'Sa' %}'
],
dayNamesShort: [
'{% trans 'Su' %}', '{% trans 'Mo' %}', '{% trans 'Tu' %}', '{% trans 'We' %}',
'{% trans 'Th' %}', '{% trans 'Fr' %}', '{% trans 'Sa' %}'
],
dateFormat: 'dd.mm.yy', firstDay: 1, isRTL: false
};
$.datepicker.setDefaults($.datepicker.regional['{{ LANGUAGE_CODE }}']);
$("#id_agenda_start_event_date_time").datetimepicker (
{
hour: 12,
timeFormat: "HH:mm",
timeText: '{% trans 'Time' %}',
hourText: '{% trans 'Hour' %}',
minuteText: '{% trans 'Minute' %}',
currentText: '{% trans 'current time' %}',
closeText: '{% trans 'close' %}'
}
);
});
</script>
{% endblock %}
{% block title %}{{ block.super }} {% trans "Agenda settings" %}{% endblock %}
{% block content %}
<h1>{% trans "Configuration" %}: {% trans "Agenda" %}
<h1>
{% trans "Configuration" %}
<small>{% trans "Agenda" %}</small>
{% block config_submenu %}{{ block.super }}{% endblock %}
</h1>
<form action="" method="post">{% csrf_token %}
{{ form.as_p }}
<p>
<button class="button" type="submit">
<span class="icon ok">{% trans 'Save' %}</span>
</button>
<a href="{% url 'config_agenda' %}">
<button class="button" type="button" onclick="window.location='{% url 'config_agenda' %}'">
<span class="icon cancel">{% trans 'Cancel' %}</span>
</button>
</a>
</p>
{% include "form.html" %}
<p>
{% include "formbuttons_save.html" %}
<a href="{% url 'config_agenda' %}" class="btn">
{% trans 'Cancel' %}
</a>
</p>
<small>* {% trans "required" %}</small>
</form>
{% endblock %}

View File

@ -20,7 +20,7 @@
{% if perms.agenda.can_manage_agenda %}
<div class="dragcell"></div>
{% endif %}
<a href="{% model_url item 'view' %}">{{ item }}</a>
<a href="{% model_url item 'view' %}">{% if item.type == item.ORGANIZATIONAL_ITEM %}<i>[{% endif %}{{ item }}{% if item.type == item.ORGANIZATIONAL_ITEM %}]</i>{% endif %}</a>
{{ item.get_title_supplement|safe }}
</td>
{% if perms.agenda.can_manage_agenda %}
@ -28,6 +28,13 @@
{{ item.comment|first_line }}
</td>
{% endif %}
{% if perms.agenda.can_see_orga_items %}
<td>
{% if item.duration %}
{{ item.duration }}h <a {% if item.tooltip %}rel="tooltip" data-original-title="{% trans 'End' %}: {{ item.tooltip|date:"DATETIME_FORMAT" }}"{% endif %}><i class="icon-clock"></i></a>
{% endif %}
</td>
{% endif %}
{% if perms.agenda.can_manage_agenda or perms.projector.can_manage_projector %}
<td>
<span style="width: 1px; white-space: nowrap;">

View File

@ -61,6 +61,20 @@
</small>
</h1>
{% if perms.agenda.can_see_orga_items %}
{% if start and end %}
<table id="agendatime" class="table table-bordered">
<tr>
<td>{% trans "Start of event" %}:</td>
<td>{{ start|date:"DATETIME_FORMAT" }}</td>
</tr>
<tr>
<td>{% trans "Estimated end" %}:</td>
<td>{{ end|date:"DATETIME_FORMAT" }}</td>
</tr>
</table>
{% endif %}
{% endif %}
<label class="checkbox">
<input type="checkbox" id="hide_closed_items"> {% trans "Hide closed items" %}
</label>
@ -75,6 +89,9 @@
{% if perms.agenda.can_manage_agenda %}
<th width="200" class="optional">{% trans "Comment" %}</th>
{% endif %}
{% if perms.agenda.can_see_orga_items %}
<th width="50">{% trans "Duration" %}</th>
{% endif %}
{% if perms.agenda.can_manage_agenda or perms.projector.can_manage_projector %}
<th class="mini_width">{% trans "Actions" %}</th>
{% endif %}
@ -90,6 +107,9 @@
{% if perms.agenda.can_manage_agenda %}
<td class="optional"></td>
{% endif %}
{% if perms.agenda.can_see_orga_items %}
<td>{{ duration }}h</td>
{% endif %}
{% if perms.agenda.can_manage_agenda or perms.projector.can_manage_projector %}
<td>
{% if perms.projector.can_manage_projector %}
@ -110,7 +130,7 @@
{% endfor %}
{% else %}
<tr>
<td colspan="4"><i>{% trans "No items available." %}</i></td>
<td colspan="5"><i>{% trans "No items available." %}</i></td>
</tr>
{% endif %}
</table>

View File

@ -33,7 +33,7 @@
{% for p in item.get_ancestors %}
<span class="indentation"></span>
{% endfor %}
<a href="{% model_url item 'view' %}">{{ item }}</a>
<a href="{% model_url item 'view' %}">{% if item.type == item.ORGANIZATIONAL_ITEM %}<i>[{% endif %}{{ item }}{% if item.type == item.ORGANIZATIONAL_ITEM %}]</i>{% endif %}</a>
{{ item.get_title_supplement|safe }}
</li>
{% empty %}

View File

@ -11,10 +11,12 @@
"""
from django.conf.urls import url, patterns
from openslides.agenda.views import (Overview, View, SetClosed, ItemUpdate,
from openslides.agenda.views import (
Overview, View, SetClosed, ItemUpdate,
ItemCreate, ItemDelete, AgendaPDF)
urlpatterns = patterns('',
urlpatterns = patterns(
'',
url(r'^$',
Overview.as_view(),
name='item_overview',

View File

@ -9,7 +9,9 @@
:copyright: 2011, 2012 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from reportlab.platypus import Paragraph
from datetime import datetime, timedelta
from django.core.urlresolvers import reverse
from django.contrib import messages
@ -18,10 +20,12 @@ from django.db.models import Model
from django.utils.translation import ugettext as _, ugettext_lazy
from django.views.generic.detail import SingleObjectMixin
from openslides.config.models import config
from openslides.agenda.forms import ConfigForm
from openslides.utils.pdf import stylesheet
from openslides.utils.views import (
TemplateView, RedirectView, UpdateView, CreateView, DeleteView, PDFView,
DetailView)
DetailView, FormView)
from openslides.utils.template import Tab
from openslides.utils.utils import html_strong
from openslides.projector.api import get_active_slide
@ -39,10 +43,44 @@ class Overview(TemplateView):
def get_context_data(self, **kwargs):
context = super(Overview, self).get_context_data(**kwargs)
if self.request.user.has_perm('agenda.can_see_orga_items'):
items = Item.objects.all()
else:
items = Item.objects.filter(type__exact=Item.AGENDA_ITEM)
start = config['agenda_start_event_date_time']
if start is None or len(start) == 0:
start = None
else:
start = datetime.strptime(start, '%d.%m.%Y %H:%M')
duration = timedelta()
for item in items:
if not item.closed and (item.duration is not None
and len(item.duration) > 0):
duration_list = item.duration.split(':')
duration += timedelta(hours=int(duration_list[0]),
minutes=int(duration_list[1]))
if not start is None:
item.tooltip = start + duration
if start is None:
end = None
else:
end = start + duration
duration = u'%d:%02d' % (
(duration.days * 24 + duration.seconds / 3600.0),
(duration.seconds / 60.0 % 60))
context.update({
'items': Item.objects.all(),
'items': items,
'active_sid': get_active_slide(only_sid=True),
})
'duration': duration,
'start': start,
'end': end})
return context
@transaction.commit_manually
@ -103,8 +141,7 @@ class SetClosed(RedirectView, SingleObjectMixin):
link = reverse('item_close', args=[self.object.id])
context.update({
'closed': kwargs['closed'],
'link': link,
})
'link': link})
return context
def pre_redirect(self, request, *args, **kwargs):
@ -157,12 +194,14 @@ class ItemDelete(DeleteView):
if self.get_answer() == 'all':
self.object.delete(with_children=True)
messages.success(
request, _("Item %s and his children were successfully deleted.")
request,
_("Item %s and his children were successfully deleted.")
% html_strong(self.object))
elif self.get_answer() == 'yes':
self.object.delete(with_children=False)
messages.success(
request, _("Item %s was successfully deleted.")
request,
_("Item %s was successfully deleted.")
% html_strong(self.object))
@ -175,7 +214,7 @@ class AgendaPDF(PDFView):
document_title = ugettext_lazy('Agenda')
def append_to_pdf(self, story):
for item in Item.objects.all():
for item in Item.objects.filter(type__exact=Item.AGENDA_ITEM):
ancestors = item.get_ancestors()
if ancestors:
space = "&nbsp;" * 6 * ancestors.count()
@ -186,6 +225,26 @@ class AgendaPDF(PDFView):
story.append(Paragraph(item.get_title(), stylesheet['Item']))
class Config(FormView):
"""
Config page for the agenda app.
"""
permission_required = 'config.can_manage_config'
form_class = ConfigForm
template_name = 'agenda/config.html'
success_url_name = 'config_agenda'
def get_initial(self):
return {
'agenda_start_event_date_time': config['agenda_start_event_date_time'],
}
def form_valid(self, form):
config['agenda_start_event_date_time'] = form.cleaned_data['agenda_start_event_date_time']
messages.success(self.request, _('Agenda settings successfully saved.'))
return super(Config, self).form_valid(form)
def register_tab(request):
"""
register the agenda tab.

File diff suppressed because it is too large Load Diff

View File

@ -6,8 +6,8 @@
msgid ""
msgstr ""
"Project-Id-Version: OpenSlides 1.3\n"
"Report-Msgid-Bugs-To: support@openslides.org\n"
"POT-Creation-Date: 2012-12-09 11:12+0100\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-02-05 14:24+0100\n"
"PO-Revision-Date: 2012-07-28 11:07+0200\n"
"Last-Translator: Oskar Hahn <mail@oshahn.de>\n"
"Language: de\n"

View File

@ -195,7 +195,7 @@ class VersionPermitView(GetVersionMixin, SingleObjectMixin, QuestionMixin, Redir
def case_yes(self):
"""Activate the version, if the user chooses 'yes'."""
self.object.activate_version(self.object.version)
self.object.activate_version(self.object.version) # TODO: Write log message
self.object.save()
version_permit = VersionPermitView.as_view()
@ -221,7 +221,7 @@ class VersionRejectView(GetVersionMixin, SingleObjectMixin, QuestionMixin, Redir
def case_yes(self):
"""Reject the version, if the user chooses 'yes'."""
self.object.reject_version(self.object.version)
self.object.reject_version(self.object.version) # TODO: Write log message
self.object.save()
version_reject = VersionRejectView.as_view()

File diff suppressed because one or more lines are too long

View File

@ -8,7 +8,7 @@
<link type="text/css" rel="stylesheet" media="all" href="{% static 'styles/dashboard.css' %}" />
{% endblock %}
{% block javascript %}
<script type="text/javascript" src="{% static 'javascript/jquery-ui.min.js' %}"></script>
<script type="text/javascript" src="{% static 'javascript/jquery-ui.custom.min.js' %}"></script>
<script type="text/javascript" src="{% static 'javascript/jquery.cookie.js' %}"></script>
<script type="text/javascript" src="{% static 'javascript/jquery.form.js' %}"></script>
<script type="text/javascript" src="{% static 'javascript/dashboard.js' %}"></script>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

File diff suppressed because one or more lines are too long

View File

@ -311,7 +311,10 @@ legend + .control-group {
background-image: url("../img/glyphicons_006_user_add.png");
background-position: 0;
}
.icon-clock {
background-image: url("../img/glyphicons_054_clock.png");
background-position: 0;
}
/** Responsive **/

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

File diff suppressed because one or more lines are too long

View File

@ -179,7 +179,8 @@ class ViewTest(TestCase):
response = c.get('/agenda/%d/edit/' % 1000)
self.assertEqual(response.status_code, 404)
data = {'title': 'newitem1', 'text': 'item1-text', 'weight': '0'}
data = {'title': 'newitem1', 'text': 'item1-text', 'weight': '0',
'type': 1}
response = c.post('/agenda/%d/edit/' % self.item1.id, data)
self.assertEqual(response.status_code, 302)
self.refreshItems()