1
AUTHORS
@ -7,3 +7,4 @@ Authors of OpenSlides in chronological order of first contribution:
|
|||||||
Andy Kittner <andkit@gmx.net>
|
Andy Kittner <andkit@gmx.net>
|
||||||
Moira Brülisauer <moira.bruelisauer@piratenpartei.ch> (French translation)
|
Moira Brülisauer <moira.bruelisauer@piratenpartei.ch> (French translation)
|
||||||
Alexis Roussel <alexis.roussel@partipirate.ch> (French translation)
|
Alexis Roussel <alexis.roussel@partipirate.ch> (French translation)
|
||||||
|
Stefan Frauenknecht <stefan@frauenknecht.net>
|
||||||
|
4
THANKS
@ -8,9 +8,13 @@ OpenSlides uses parts of the following projects:
|
|||||||
|
|
||||||
* jQuery
|
* jQuery
|
||||||
<http://www.jquery.com/>
|
<http://www.jquery.com/>
|
||||||
|
and some addons: cookie, form, once, templating
|
||||||
|
|
||||||
* jQuery UI
|
* jQuery UI
|
||||||
<http://jqueryui.com/>
|
<http://jqueryui.com/>
|
||||||
|
custom ui components: core, widget, mouse, sortable, datepicker, slider
|
||||||
|
with css theme 'smoothness'
|
||||||
|
and some addons: slider access, timepicker
|
||||||
|
|
||||||
* Twitter Bootstrap
|
* Twitter Bootstrap
|
||||||
<http://twitter.github.com/bootstrap/>
|
<http://twitter.github.com/bootstrap/>
|
||||||
|
@ -10,14 +10,14 @@
|
|||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from mptt.forms import TreeNodeChoiceField
|
from mptt.forms import TreeNodeChoiceField
|
||||||
|
|
||||||
from openslides.utils.forms import CssClassMixin
|
from openslides.utils.forms import CssClassMixin
|
||||||
|
from .models import Item
|
||||||
from openslides.agenda.models import Item
|
|
||||||
|
|
||||||
|
|
||||||
class ItemForm(forms.ModelForm, CssClassMixin):
|
class ItemForm(forms.ModelForm, CssClassMixin):
|
||||||
@ -27,6 +27,13 @@ class ItemForm(forms.ModelForm, CssClassMixin):
|
|||||||
parent = TreeNodeChoiceField(
|
parent = TreeNodeChoiceField(
|
||||||
queryset=Item.objects.all(), label=_("Parent item"), required=False)
|
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:
|
class Meta:
|
||||||
model = Item
|
model = Item
|
||||||
exclude = ('closed', 'weight', 'related_sid')
|
exclude = ('closed', 'weight', 'related_sid')
|
||||||
@ -45,11 +52,15 @@ class ItemOrderForm(forms.Form, CssClassMixin):
|
|||||||
"""
|
"""
|
||||||
weight = forms.ChoiceField(
|
weight = forms.ChoiceField(
|
||||||
choices=gen_weight_choices(),
|
choices=gen_weight_choices(),
|
||||||
widget=forms.Select(attrs={'class': 'menu-weight'}),
|
widget=forms.Select(attrs={'class': 'menu-weight'}))
|
||||||
)
|
|
||||||
self = forms.IntegerField(
|
self = forms.IntegerField(
|
||||||
widget=forms.HiddenInput(attrs={'class': 'menu-mlid'}),
|
widget=forms.HiddenInput(attrs={'class': 'menu-mlid'}))
|
||||||
)
|
|
||||||
parent = forms.IntegerField(
|
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"))
|
||||||
|
@ -20,7 +20,7 @@ from openslides.config.models import config
|
|||||||
from openslides.projector.projector import SlideMixin
|
from openslides.projector.projector import SlideMixin
|
||||||
from openslides.projector.api import (
|
from openslides.projector.api import (
|
||||||
register_slidemodel, get_slide_from_sid, register_slidefunc)
|
register_slidemodel, get_slide_from_sid, register_slidefunc)
|
||||||
from openslides.agenda.slides import agenda_show
|
from .slides import agenda_show
|
||||||
|
|
||||||
|
|
||||||
class Item(MPTTModel, SlideMixin):
|
class Item(MPTTModel, SlideMixin):
|
||||||
@ -31,6 +31,13 @@ class Item(MPTTModel, SlideMixin):
|
|||||||
"""
|
"""
|
||||||
prefix = 'item'
|
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"))
|
title = models.CharField(null=True, max_length=255, verbose_name=_("Title"))
|
||||||
text = models.TextField(null=True, blank=True, verbose_name=_("Text"))
|
text = models.TextField(null=True, blank=True, verbose_name=_("Text"))
|
||||||
comment = models.TextField(null=True, blank=True, verbose_name=_("Comment"))
|
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"))
|
weight = models.IntegerField(default=0, verbose_name=_("Weight"))
|
||||||
parent = TreeForeignKey('self', null=True, blank=True,
|
parent = TreeForeignKey('self', null=True, blank=True,
|
||||||
related_name='children')
|
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)
|
related_sid = models.CharField(null=True, blank=True, max_length=63)
|
||||||
|
|
||||||
def get_related_slide(self):
|
def get_related_slide(self):
|
||||||
@ -170,6 +179,7 @@ class Item(MPTTModel, SlideMixin):
|
|||||||
permissions = (
|
permissions = (
|
||||||
('can_see_agenda', ugettext_noop("Can see agenda")),
|
('can_see_agenda', ugettext_noop("Can see agenda")),
|
||||||
('can_manage_agenda', ugettext_noop("Can manage 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:
|
class MPTTMeta:
|
||||||
|
@ -16,7 +16,7 @@ from django.utils.translation import ugettext as _
|
|||||||
def agenda_show():
|
def agenda_show():
|
||||||
from openslides.agenda.models import Item
|
from openslides.agenda.models import Item
|
||||||
data = {}
|
data = {}
|
||||||
items = Item.objects.filter(parent=None)
|
items = Item.objects.filter(parent=None, type__exact=Item.AGENDA_ITEM)
|
||||||
data['title'] = _("Agenda")
|
data['title'] = _("Agenda")
|
||||||
data['items'] = items
|
data['items'] = items
|
||||||
data['template'] = 'projector/AgendaSummary.html'
|
data['template'] = 'projector/AgendaSummary.html'
|
||||||
|
89
openslides/agenda/static/javascript/jquery-ui-sliderAccess.js
vendored
Normal 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);
|
1919
openslides/agenda/static/javascript/jquery-ui-timepicker-addon.js
vendored
Normal file
@ -9,3 +9,15 @@ tr.topline td {
|
|||||||
border-bottom: 1px solid #333333;
|
border-bottom: 1px solid #333333;
|
||||||
background-color: #CDCDCD;
|
background-color: #CDCDCD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table#agendatime {
|
||||||
|
float: right;
|
||||||
|
width: auto;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table#agendatime td {
|
||||||
|
padding: 3px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
46
openslides/agenda/static/styles/timepicker.css
Normal 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;
|
||||||
|
}
|
@ -1,24 +1,85 @@
|
|||||||
{% extends "config/base_config.html" %}
|
{% extends "config/base_config.html" %}
|
||||||
|
|
||||||
{% load i18n %}
|
{% 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 title %}{{ block.super }} – {% trans "Agenda settings" %}{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>{% trans "Configuration" %}: {% trans "Agenda" %}
|
<h1>
|
||||||
|
{% trans "Configuration" %}
|
||||||
|
<small>{% trans "Agenda" %}</small>
|
||||||
{% block config_submenu %}{{ block.super }}{% endblock %}
|
{% block config_submenu %}{{ block.super }}{% endblock %}
|
||||||
</h1>
|
</h1>
|
||||||
<form action="" method="post">{% csrf_token %}
|
<form action="" method="post">{% csrf_token %}
|
||||||
{{ form.as_p }}
|
{% include "form.html" %}
|
||||||
<p>
|
<p>
|
||||||
<button class="button" type="submit">
|
{% include "formbuttons_save.html" %}
|
||||||
<span class="icon ok">{% trans 'Save' %}</span>
|
<a href="{% url 'config_agenda' %}" class="btn">
|
||||||
</button>
|
{% trans 'Cancel' %}
|
||||||
<a href="{% url 'config_agenda' %}">
|
</a>
|
||||||
<button class="button" type="button" onclick="window.location='{% url 'config_agenda' %}'">
|
</p>
|
||||||
<span class="icon cancel">{% trans 'Cancel' %}</span>
|
<small>* {% trans "required" %}</small>
|
||||||
</button>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -20,7 +20,7 @@
|
|||||||
{% if perms.agenda.can_manage_agenda %}
|
{% if perms.agenda.can_manage_agenda %}
|
||||||
<div class="dragcell"></div>
|
<div class="dragcell"></div>
|
||||||
{% endif %}
|
{% 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 }}
|
{{ item.get_title_supplement|safe }}
|
||||||
</td>
|
</td>
|
||||||
{% if perms.agenda.can_manage_agenda %}
|
{% if perms.agenda.can_manage_agenda %}
|
||||||
@ -28,6 +28,13 @@
|
|||||||
{{ item.comment|first_line }}
|
{{ item.comment|first_line }}
|
||||||
</td>
|
</td>
|
||||||
{% endif %}
|
{% 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 %}
|
{% if perms.agenda.can_manage_agenda or perms.projector.can_manage_projector %}
|
||||||
<td>
|
<td>
|
||||||
<span style="width: 1px; white-space: nowrap;">
|
<span style="width: 1px; white-space: nowrap;">
|
||||||
|
@ -61,6 +61,20 @@
|
|||||||
</small>
|
</small>
|
||||||
</h1>
|
</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">
|
<label class="checkbox">
|
||||||
<input type="checkbox" id="hide_closed_items"> {% trans "Hide closed items" %}
|
<input type="checkbox" id="hide_closed_items"> {% trans "Hide closed items" %}
|
||||||
</label>
|
</label>
|
||||||
@ -75,6 +89,9 @@
|
|||||||
{% if perms.agenda.can_manage_agenda %}
|
{% if perms.agenda.can_manage_agenda %}
|
||||||
<th width="200" class="optional">{% trans "Comment" %}</th>
|
<th width="200" class="optional">{% trans "Comment" %}</th>
|
||||||
{% endif %}
|
{% 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 %}
|
{% if perms.agenda.can_manage_agenda or perms.projector.can_manage_projector %}
|
||||||
<th class="mini_width">{% trans "Actions" %}</th>
|
<th class="mini_width">{% trans "Actions" %}</th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -90,6 +107,9 @@
|
|||||||
{% if perms.agenda.can_manage_agenda %}
|
{% if perms.agenda.can_manage_agenda %}
|
||||||
<td class="optional"></td>
|
<td class="optional"></td>
|
||||||
{% endif %}
|
{% 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 %}
|
{% if perms.agenda.can_manage_agenda or perms.projector.can_manage_projector %}
|
||||||
<td>
|
<td>
|
||||||
{% if perms.projector.can_manage_projector %}
|
{% if perms.projector.can_manage_projector %}
|
||||||
@ -110,7 +130,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="4"><i>{% trans "No items available." %}</i></td>
|
<td colspan="5"><i>{% trans "No items available." %}</i></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</table>
|
</table>
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
{% for p in item.get_ancestors %}
|
{% for p in item.get_ancestors %}
|
||||||
<span class="indentation"></span>
|
<span class="indentation"></span>
|
||||||
{% endfor %}
|
{% 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 }}
|
{{ item.get_title_supplement|safe }}
|
||||||
</li>
|
</li>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
|
@ -11,10 +11,12 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from django.conf.urls import url, patterns
|
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)
|
ItemCreate, ItemDelete, AgendaPDF)
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns(
|
||||||
|
'',
|
||||||
url(r'^$',
|
url(r'^$',
|
||||||
Overview.as_view(),
|
Overview.as_view(),
|
||||||
name='item_overview',
|
name='item_overview',
|
||||||
|
@ -9,7 +9,9 @@
|
|||||||
:copyright: 2011, 2012 by the OpenSlides team, see AUTHORS.
|
:copyright: 2011, 2012 by the OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from reportlab.platypus import Paragraph
|
from reportlab.platypus import Paragraph
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.contrib import messages
|
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.utils.translation import ugettext as _, ugettext_lazy
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
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.pdf import stylesheet
|
||||||
from openslides.utils.views import (
|
from openslides.utils.views import (
|
||||||
TemplateView, RedirectView, UpdateView, CreateView, DeleteView, PDFView,
|
TemplateView, RedirectView, UpdateView, CreateView, DeleteView, PDFView,
|
||||||
DetailView)
|
DetailView, FormView)
|
||||||
from openslides.utils.template import Tab
|
from openslides.utils.template import Tab
|
||||||
from openslides.utils.utils import html_strong
|
from openslides.utils.utils import html_strong
|
||||||
from openslides.projector.api import get_active_slide
|
from openslides.projector.api import get_active_slide
|
||||||
@ -39,10 +43,44 @@ class Overview(TemplateView):
|
|||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(Overview, self).get_context_data(**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({
|
context.update({
|
||||||
'items': Item.objects.all(),
|
'items': items,
|
||||||
'active_sid': get_active_slide(only_sid=True),
|
'active_sid': get_active_slide(only_sid=True),
|
||||||
})
|
'duration': duration,
|
||||||
|
'start': start,
|
||||||
|
'end': end})
|
||||||
return context
|
return context
|
||||||
|
|
||||||
@transaction.commit_manually
|
@transaction.commit_manually
|
||||||
@ -103,8 +141,7 @@ class SetClosed(RedirectView, SingleObjectMixin):
|
|||||||
link = reverse('item_close', args=[self.object.id])
|
link = reverse('item_close', args=[self.object.id])
|
||||||
context.update({
|
context.update({
|
||||||
'closed': kwargs['closed'],
|
'closed': kwargs['closed'],
|
||||||
'link': link,
|
'link': link})
|
||||||
})
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def pre_redirect(self, request, *args, **kwargs):
|
def pre_redirect(self, request, *args, **kwargs):
|
||||||
@ -157,12 +194,14 @@ class ItemDelete(DeleteView):
|
|||||||
if self.get_answer() == 'all':
|
if self.get_answer() == 'all':
|
||||||
self.object.delete(with_children=True)
|
self.object.delete(with_children=True)
|
||||||
messages.success(
|
messages.success(
|
||||||
request, _("Item %s and his children were successfully deleted.")
|
request,
|
||||||
|
_("Item %s and his children were successfully deleted.")
|
||||||
% html_strong(self.object))
|
% html_strong(self.object))
|
||||||
elif self.get_answer() == 'yes':
|
elif self.get_answer() == 'yes':
|
||||||
self.object.delete(with_children=False)
|
self.object.delete(with_children=False)
|
||||||
messages.success(
|
messages.success(
|
||||||
request, _("Item %s was successfully deleted.")
|
request,
|
||||||
|
_("Item %s was successfully deleted.")
|
||||||
% html_strong(self.object))
|
% html_strong(self.object))
|
||||||
|
|
||||||
|
|
||||||
@ -175,7 +214,7 @@ class AgendaPDF(PDFView):
|
|||||||
document_title = ugettext_lazy('Agenda')
|
document_title = ugettext_lazy('Agenda')
|
||||||
|
|
||||||
def append_to_pdf(self, story):
|
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()
|
ancestors = item.get_ancestors()
|
||||||
if ancestors:
|
if ancestors:
|
||||||
space = " " * 6 * ancestors.count()
|
space = " " * 6 * ancestors.count()
|
||||||
@ -186,6 +225,26 @@ class AgendaPDF(PDFView):
|
|||||||
story.append(Paragraph(item.get_title(), stylesheet['Item']))
|
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):
|
def register_tab(request):
|
||||||
"""
|
"""
|
||||||
register the agenda tab.
|
register the agenda tab.
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: OpenSlides 1.3\n"
|
"Project-Id-Version: OpenSlides 1.3\n"
|
||||||
"Report-Msgid-Bugs-To: support@openslides.org\n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2012-12-09 11:12+0100\n"
|
"POT-Creation-Date: 2013-02-05 14:24+0100\n"
|
||||||
"PO-Revision-Date: 2012-07-28 11:07+0200\n"
|
"PO-Revision-Date: 2012-07-28 11:07+0200\n"
|
||||||
"Last-Translator: Oskar Hahn <mail@oshahn.de>\n"
|
"Last-Translator: Oskar Hahn <mail@oshahn.de>\n"
|
||||||
"Language: de\n"
|
"Language: de\n"
|
||||||
|
@ -195,7 +195,7 @@ class VersionPermitView(GetVersionMixin, SingleObjectMixin, QuestionMixin, Redir
|
|||||||
|
|
||||||
def case_yes(self):
|
def case_yes(self):
|
||||||
"""Activate the version, if the user chooses 'yes'."""
|
"""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()
|
self.object.save()
|
||||||
|
|
||||||
version_permit = VersionPermitView.as_view()
|
version_permit = VersionPermitView.as_view()
|
||||||
@ -221,7 +221,7 @@ class VersionRejectView(GetVersionMixin, SingleObjectMixin, QuestionMixin, Redir
|
|||||||
|
|
||||||
def case_yes(self):
|
def case_yes(self):
|
||||||
"""Reject the version, if the user chooses 'yes'."""
|
"""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()
|
self.object.save()
|
||||||
|
|
||||||
version_reject = VersionRejectView.as_view()
|
version_reject = VersionRejectView.as_view()
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<link type="text/css" rel="stylesheet" media="all" href="{% static 'styles/dashboard.css' %}" />
|
<link type="text/css" rel="stylesheet" media="all" href="{% static 'styles/dashboard.css' %}" />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block javascript %}
|
{% 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.cookie.js' %}"></script>
|
||||||
<script type="text/javascript" src="{% static 'javascript/jquery.form.js' %}"></script>
|
<script type="text/javascript" src="{% static 'javascript/jquery.form.js' %}"></script>
|
||||||
<script type="text/javascript" src="{% static 'javascript/dashboard.js' %}"></script>
|
<script type="text/javascript" src="{% static 'javascript/dashboard.js' %}"></script>
|
||||||
|
BIN
openslides/static/img/glyphicons_054_clock.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
6
openslides/static/javascript/jquery-ui.custom.min.js
vendored
Executable file
@ -311,7 +311,10 @@ legend + .control-group {
|
|||||||
background-image: url("../img/glyphicons_006_user_add.png");
|
background-image: url("../img/glyphicons_006_user_add.png");
|
||||||
background-position: 0;
|
background-position: 0;
|
||||||
}
|
}
|
||||||
|
.icon-clock {
|
||||||
|
background-image: url("../img/glyphicons_054_clock.png");
|
||||||
|
background-position: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Responsive **/
|
/** Responsive **/
|
||||||
|
BIN
openslides/static/styles/jquery-ui/images/animated-overlay.gif
Executable file
After Width: | Height: | Size: 1.7 KiB |
BIN
openslides/static/styles/jquery-ui/images/ui-bg_flat_0_aaaaaa_40x100.png
Executable file
After Width: | Height: | Size: 274 B |
BIN
openslides/static/styles/jquery-ui/images/ui-bg_flat_75_ffffff_40x100.png
Executable file
After Width: | Height: | Size: 271 B |
BIN
openslides/static/styles/jquery-ui/images/ui-bg_glass_55_fbf9ee_1x400.png
Executable file
After Width: | Height: | Size: 387 B |
BIN
openslides/static/styles/jquery-ui/images/ui-bg_glass_65_ffffff_1x400.png
Executable file
After Width: | Height: | Size: 272 B |
BIN
openslides/static/styles/jquery-ui/images/ui-bg_glass_75_dadada_1x400.png
Executable file
After Width: | Height: | Size: 375 B |
BIN
openslides/static/styles/jquery-ui/images/ui-bg_glass_75_e6e6e6_1x400.png
Executable file
After Width: | Height: | Size: 368 B |
BIN
openslides/static/styles/jquery-ui/images/ui-bg_glass_95_fef1ec_1x400.png
Executable file
After Width: | Height: | Size: 384 B |
After Width: | Height: | Size: 360 B |
BIN
openslides/static/styles/jquery-ui/images/ui-icons_222222_256x240.png
Executable file
After Width: | Height: | Size: 6.6 KiB |
BIN
openslides/static/styles/jquery-ui/images/ui-icons_2e83ff_256x240.png
Executable file
After Width: | Height: | Size: 4.3 KiB |
BIN
openslides/static/styles/jquery-ui/images/ui-icons_454545_256x240.png
Executable file
After Width: | Height: | Size: 6.7 KiB |
BIN
openslides/static/styles/jquery-ui/images/ui-icons_888888_256x240.png
Executable file
After Width: | Height: | Size: 6.7 KiB |
BIN
openslides/static/styles/jquery-ui/images/ui-icons_cd0a0a_256x240.png
Executable file
After Width: | Height: | Size: 4.3 KiB |
5
openslides/static/styles/jquery-ui/jquery-ui.custom.min.css
vendored
Executable file
@ -179,7 +179,8 @@ class ViewTest(TestCase):
|
|||||||
response = c.get('/agenda/%d/edit/' % 1000)
|
response = c.get('/agenda/%d/edit/' % 1000)
|
||||||
self.assertEqual(response.status_code, 404)
|
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)
|
response = c.post('/agenda/%d/edit/' % self.item1.id, data)
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.refreshItems()
|
self.refreshItems()
|
||||||
|