Merge pull request #611 from emanuelschuetze/Issue543

Sorting agenda items with nestedSortable.
This commit is contained in:
Oskar Hahn 2013-04-23 22:52:17 -07:00
commit e755712403
13 changed files with 664 additions and 1601 deletions

View File

@ -8,3 +8,4 @@ Authors of OpenSlides in chronological order of first contribution:
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> Stefan Frauenknecht <stefan@frauenknecht.net>
Tobias Hößl <tobias@hoessl.eu>

6
THANKS
View File

@ -22,8 +22,8 @@ OpenSlides uses parts of the following projects:
* ReportLab * ReportLab
<http://www.reportlab.com/software/opensource/rl-toolkit/> <http://www.reportlab.com/software/opensource/rl-toolkit/>
* Drupal (tabledrag function)
<http://www.drupal.org/>
* Ubuntu TrueType Font * Ubuntu TrueType Font
<http://font.ubuntu.com/> <http://font.ubuntu.com/>
* nestedSortable jQuery Plugin
<http://mjsarfatti.com/sandbox/nestedSortable/>

View File

@ -9,7 +9,7 @@ Dieses Tutorial ist noch nicht fertiggestellt. Wenn Sie Interesse haben, uns zu
.. TODO, ggf. nach oben verschieben .. TODO, ggf. nach oben verschieben
.. Das Aussehen von Openslides wird durch die folgenden Dateien beeinflusst: base.css, tabledrag.css, agenda.css, beamer.css .. Das Aussehen von Openslides wird durch die folgenden Dateien beeinflusst: base.css, agenda.css, projector.css
.. Die Dateien liegen im Unterverzeichnis "/static/styles/" des Installationsverzeichnisses. .. Die Dateien liegen im Unterverzeichnis "/static/styles/" des Installationsverzeichnisses.

View File

@ -37,7 +37,6 @@ URL_SETS = {
"agenda": [ "agenda": [
"/agenda/", "/agenda/",
"/static/styles/base.css", "/static/styles/base.css",
"/static/styles/tabledrag.css",
"/static/javascript/utils.js", "/static/javascript/utils.js",
"/static/styles/agenda.css", "/static/styles/agenda.css",
"/static/javascript/jquery.min.js", "/static/javascript/jquery.min.js",

View File

@ -40,20 +40,12 @@ class ItemForm(forms.ModelForm, CssClassMixin):
exclude = ('closed', 'weight', 'related_sid') exclude = ('closed', 'weight', 'related_sid')
def gen_weight_choices(): class ItemOrderForm(forms.Form, CssClassMixin):
"""
Creates a list of tuples (n, n) for n from -49 to 50.
"""
return zip(*(range(-50, 51), range(-50, 51)))
class ItemOrderForm(CssClassMixin, forms.Form):
""" """
Form to change the order of the items. Form to change the order of the items.
""" """
weight = forms.ChoiceField( weight = forms.IntegerField(
choices=gen_weight_choices(), widget=forms.HiddenInput(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(

View File

@ -5,28 +5,18 @@
* :license: GNU GPL, see LICENSE for more details. * :license: GNU GPL, see LICENSE for more details.
*/ */
function hideLine(object) {
if (object == []) {
return;
}
object.hide();
id = object.children('td.tabledrag-hide').children('input.menu-mlid').attr('value');
$('.menu-plid[value=\'' + id + '\']').parent().parent().each(function() {
hideLine($(this));
});
}
function hideClosedSlides(hide) { function hideClosedSlides(hide) {
if (hide) { if (hide) {
$('#hidelink').attr('title', 'show'); $('#hidelink').attr('title', 'show');
$('#hidelink').removeClass('hide').addClass('show'); $('#hidelink').removeClass('hide').addClass('show');
$('.close_link .icon-checked-new').parent().parent().parent().each(function() { $('.close_link .icon-checked-new').each(function() {
hideLine($(this)); $(this).parents("li").first().hide();
}); });
hidden = $('#menu-overview tr:hidden').size(); var hidden = $(".agenda_list li:hidden").length;
$('#hiddencount').text(interpolate(gettext(', of which %s are hidden.'), [hidden])); $('#hiddencount').text(interpolate(gettext(', of which %s are hidden.'), [hidden]));
} else { } else {
$('#menu-overview tr').show(); $('.agenda_list li').show();
$('#hidelink').attr('title','hide'); $('#hidelink').attr('title','hide');
$('#hidelink').removeClass('show').addClass('hide'); $('#hidelink').removeClass('show').addClass('hide');
$('#hiddencount').text(''); $('#hiddencount').text('');

View File

@ -0,0 +1,66 @@
$(function() {
var $agenda_list = $('ol.agenda_list');
var rebuildOpenersClosers = function ( ) {
$agenda_list.find("li").each(function() {
var $li = $(this);
if ($li.find("> ol").length > 0) $li.find("> div .opener_closer").show();
else $li.find("> div .opener_closer").hide();
});
}
var rebuildNesting = function( $root, level, parent_id ) {
var $children = $root.find('> li'),
curr_weight = -50;
$children.each(function() {
var $child = $(this),
$curr_element = $child.find('> div'),
my_id = $curr_element.find('.menu-mlid').val();
$curr_element.find('.menu-plid').val(parent_id);
$curr_element.find('.menu-weight').val(curr_weight);
curr_weight++;
$child.find('> ol').each(function() {
rebuildNesting( $(this), level + 1, my_id );
});
});
};
if ($agenda_list.hasClass("sortable")) $agenda_list.nestedSortable({
forcePlaceholderSize: true,
handle: 'div',
helper: 'clone',
items: 'li',
opacity: .6,
placeholder: 'placeholder',
revert: 250,
tabSize: 25,
tolerance: 'pointer',
toleranceElement: '> div',
isTree: true,
expandOnHover: 700,
startCollapsed: true,
update: function (event, ui) {
var $this = $(this);
rebuildNesting($this, 0, 0);
$('#changed-order-message').show();
rebuildOpenersClosers();
}
});
rebuildOpenersClosers();
$agenda_list.find(".opener_closer .opener").click(function(ev) {
ev.preventDefault();
$(this).parents("li").first().removeClass("closed");
});
$agenda_list.find(".opener_closer .closer").click(function(ev) {
ev.preventDefault();
$(this).parents("li").first().addClass("closed");
});
});

View File

@ -0,0 +1,429 @@
/*
* jQuery UI Nested Sortable
* v 1.3.5 / 21 jun 2012
* http://mjsarfatti.com/code/nestedSortable
*
* Depends on:
* jquery.ui.sortable.js 1.8+
*
* Copyright (c) 2010-2012 Manuele J Sarfatti
* Licensed under the MIT License
* http://www.opensource.org/licenses/mit-license.php
*/
(function($) {
$.widget("mjs.nestedSortable", $.extend({}, $.ui.sortable.prototype, {
options: {
tabSize: 20,
disableNesting: 'mjs-nestedSortable-no-nesting',
errorClass: 'mjs-nestedSortable-error',
doNotClear: false,
listType: 'ol',
maxLevels: 0,
protectRoot: false,
rootID: null,
rtl: false,
isAllowed: function(item, parent) { return true; }
},
_create: function() {
this.element.data('sortable', this.element.data('nestedSortable'));
if (!this.element.is(this.options.listType))
throw new Error('nestedSortable: Please check the listType option is set to your actual list type');
return $.ui.sortable.prototype._create.apply(this, arguments);
},
destroy: function() {
this.element
.removeData("nestedSortable")
.unbind(".nestedSortable");
return $.ui.sortable.prototype.destroy.apply(this, arguments);
},
_mouseDrag: function(event) {
//Compute the helpers position
this.position = this._generatePosition(event);
this.positionAbs = this._convertPositionTo("absolute");
if (!this.lastPositionAbs) {
this.lastPositionAbs = this.positionAbs;
}
var o = this.options;
//Do scrolling
if(this.options.scroll) {
var scrolled = false;
if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') {
if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity)
this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity)
this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
} else {
if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
}
if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
$.ui.ddmanager.prepareOffsets(this, event);
}
//Regenerate the absolute position used for position checks
this.positionAbs = this._convertPositionTo("absolute");
// Find the top offset before rearrangement,
var previousTopOffset = this.placeholder.offset().top;
//Set the helper position
if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
//Rearrange
for (var i = this.items.length - 1; i >= 0; i--) {
//Cache variables and intersection, continue if no intersection
var item = this.items[i], itemElement = item.item[0], intersection = this._intersectsWithPointer(item);
if (!intersection) continue;
if(itemElement != this.currentItem[0] //cannot intersect with itself
&& this.placeholder[intersection == 1 ? "next" : "prev"]()[0] != itemElement //no useless actions that have been done before
&& !$.contains(this.placeholder[0], itemElement) //no action if the item moved is the parent of the item checked
&& (this.options.type == 'semi-dynamic' ? !$.contains(this.element[0], itemElement) : true)
//&& itemElement.parentNode == this.placeholder[0].parentNode // only rearrange items within the same container
) {
$(itemElement).mouseenter();
this.direction = intersection == 1 ? "down" : "up";
if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) {
$(itemElement).mouseleave();
this._rearrange(event, item);
} else {
break;
}
// Clear emtpy ul's/ol's
this._clearEmpty(itemElement);
this._trigger("change", event, this._uiHash());
break;
}
}
var parentItem = (this.placeholder[0].parentNode.parentNode &&
$(this.placeholder[0].parentNode.parentNode).closest('.ui-sortable').length)
? $(this.placeholder[0].parentNode.parentNode)
: null,
level = this._getLevel(this.placeholder),
childLevels = this._getChildLevels(this.helper);
// To find the previous sibling in the list, keep backtracking until we hit a valid list item.
var previousItem = this.placeholder[0].previousSibling ? $(this.placeholder[0].previousSibling) : null;
if (previousItem != null) {
while (previousItem[0].nodeName.toLowerCase() != 'li' || previousItem[0] == this.currentItem[0] || previousItem[0] == this.helper[0]) {
if (previousItem[0].previousSibling) {
previousItem = $(previousItem[0].previousSibling);
} else {
previousItem = null;
break;
}
}
}
// To find the next sibling in the list, keep stepping forward until we hit a valid list item.
var nextItem = this.placeholder[0].nextSibling ? $(this.placeholder[0].nextSibling) : null;
if (nextItem != null) {
while (nextItem[0].nodeName.toLowerCase() != 'li' || nextItem[0] == this.currentItem[0] || nextItem[0] == this.helper[0]) {
if (nextItem[0].nextSibling) {
nextItem = $(nextItem[0].nextSibling);
} else {
nextItem = null;
break;
}
}
}
var newList = document.createElement(o.listType);
this.beyondMaxLevels = 0;
// If the item is moved to the left, send it to its parent's level unless there are siblings below it.
if (parentItem != null && nextItem == null &&
(o.rtl && (this.positionAbs.left + this.helper.outerWidth() > parentItem.offset().left + parentItem.outerWidth()) ||
!o.rtl && (this.positionAbs.left < parentItem.offset().left))) {
parentItem.after(this.placeholder[0]);
this._clearEmpty(parentItem[0]);
this._trigger("change", event, this._uiHash());
}
// If the item is below a sibling and is moved to the right, make it a child of that sibling.
else if (previousItem != null &&
(o.rtl && (this.positionAbs.left + this.helper.outerWidth() < previousItem.offset().left + previousItem.outerWidth() - o.tabSize) ||
!o.rtl && (this.positionAbs.left > previousItem.offset().left + o.tabSize))) {
this._isAllowed(previousItem, level, level+childLevels+1);
if (!previousItem.children(o.listType).length) {
previousItem[0].appendChild(newList);
}
// If this item is being moved from the top, add it to the top of the list.
if (previousTopOffset && (previousTopOffset <= previousItem.offset().top)) {
previousItem.children(o.listType).prepend(this.placeholder);
}
// Otherwise, add it to the bottom of the list.
else {
previousItem.children(o.listType)[0].appendChild(this.placeholder[0]);
}
this._trigger("change", event, this._uiHash());
}
else {
this._isAllowed(parentItem, level, level+childLevels);
}
//Post events to containers
this._contactContainers(event);
//Interconnect with droppables
if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
//Call callbacks
this._trigger('sort', event, this._uiHash());
this.lastPositionAbs = this.positionAbs;
return false;
},
_mouseStop: function(event, noPropagation) {
// If the item is in a position not allowed, send it back
if (this.beyondMaxLevels) {
this.placeholder.removeClass(this.options.errorClass);
if (this.domPosition.prev) {
$(this.domPosition.prev).after(this.placeholder);
} else {
$(this.domPosition.parent).prepend(this.placeholder);
}
this._trigger("revert", event, this._uiHash());
}
// Clean last empty ul/ol
for (var i = this.items.length - 1; i >= 0; i--) {
var item = this.items[i].item[0];
this._clearEmpty(item);
}
$.ui.sortable.prototype._mouseStop.apply(this, arguments);
},
serialize: function(options) {
var o = $.extend({}, this.options, options),
items = this._getItemsAsjQuery(o && o.connected),
str = [];
$(items).each(function() {
var res = ($(o.item || this).attr(o.attribute || 'id') || '')
.match(o.expression || (/(.+)[-=_](.+)/)),
pid = ($(o.item || this).parent(o.listType)
.parent(o.items)
.attr(o.attribute || 'id') || '')
.match(o.expression || (/(.+)[-=_](.+)/));
if (res) {
str.push(((o.key || res[1]) + '[' + (o.key && o.expression ? res[1] : res[2]) + ']')
+ '='
+ (pid ? (o.key && o.expression ? pid[1] : pid[2]) : o.rootID));
}
});
if(!str.length && o.key) {
str.push(o.key + '=');
}
return str.join('&');
},
toHierarchy: function(options) {
var o = $.extend({}, this.options, options),
sDepth = o.startDepthCount || 0,
ret = [];
$(this.element).children(o.items).each(function () {
var level = _recursiveItems(this);
ret.push(level);
});
return ret;
function _recursiveItems(item) {
var id = ($(item).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
if (id) {
var currentItem = {"id" : id[2]};
if ($(item).children(o.listType).children(o.items).length > 0) {
currentItem.children = [];
$(item).children(o.listType).children(o.items).each(function() {
var level = _recursiveItems(this);
currentItem.children.push(level);
});
}
return currentItem;
}
}
},
toArray: function(options) {
var o = $.extend({}, this.options, options),
sDepth = o.startDepthCount || 0,
ret = [],
left = 2;
ret.push({
"item_id": o.rootID,
"parent_id": 'none',
"depth": sDepth,
"left": '1',
"right": ($(o.items, this.element).length + 1) * 2
});
$(this.element).children(o.items).each(function () {
left = _recursiveArray(this, sDepth + 1, left);
});
ret = ret.sort(function(a,b){ return (a.left - b.left); });
return ret;
function _recursiveArray(item, depth, left) {
var right = left + 1,
id,
pid;
if ($(item).children(o.listType).children(o.items).length > 0) {
depth ++;
$(item).children(o.listType).children(o.items).each(function () {
right = _recursiveArray($(this), depth, right);
});
depth --;
}
id = ($(item).attr(o.attribute || 'id')).match(o.expression || (/(.+)[-=_](.+)/));
if (depth === sDepth + 1) {
pid = o.rootID;
} else {
var parentItem = ($(item).parent(o.listType)
.parent(o.items)
.attr(o.attribute || 'id'))
.match(o.expression || (/(.+)[-=_](.+)/));
pid = parentItem[2];
}
if (id) {
ret.push({"item_id": id[2], "parent_id": pid, "depth": depth, "left": left, "right": right});
}
left = right + 1;
return left;
}
},
_clearEmpty: function(item) {
var emptyList = $(item).children(this.options.listType);
if (emptyList.length && !emptyList.children().length && !this.options.doNotClear) {
emptyList.remove();
}
},
_getLevel: function(item) {
var level = 1;
if (this.options.listType) {
var list = item.closest(this.options.listType);
while (list && list.length > 0 &&
!list.is('.ui-sortable')) {
level++;
list = list.parent().closest(this.options.listType);
}
}
return level;
},
_getChildLevels: function(parent, depth) {
var self = this,
o = this.options,
result = 0;
depth = depth || 0;
$(parent).children(o.listType).children(o.items).each(function (index, child) {
result = Math.max(self._getChildLevels(child, depth + 1), result);
});
return depth ? result + 1 : result;
},
_isAllowed: function(parentItem, level, levels) {
var o = this.options,
isRoot = $(this.domPosition.parent).hasClass('ui-sortable') ? true : false,
maxLevels = this.placeholder.closest('.ui-sortable').nestedSortable('option', 'maxLevels'); // this takes into account the maxLevels set to the recipient list
// Is the root protected?
// Are we trying to nest under a no-nest?
// Are we nesting too deep?
if (!o.isAllowed(this.currentItem, parentItem) ||
parentItem && parentItem.hasClass(o.disableNesting) ||
o.protectRoot && (parentItem == null && !isRoot || isRoot && level > 1)) {
this.placeholder.addClass(o.errorClass);
if (maxLevels < levels && maxLevels != 0) {
this.beyondMaxLevels = levels - maxLevels;
} else {
this.beyondMaxLevels = 1;
}
} else {
if (maxLevels < levels && maxLevels != 0) {
this.placeholder.addClass(o.errorClass);
this.beyondMaxLevels = levels - maxLevels;
} else {
this.placeholder.removeClass(o.errorClass);
this.beyondMaxLevels = 0;
}
}
}
}));
$.mjs.nestedSortable.prototype.options = $.extend({}, $.ui.sortable.prototype.options, $.mjs.nestedSortable.prototype.options);
})(jQuery);

View File

@ -0,0 +1,78 @@
ol {
margin: 0;
padding: 0;
padding-left: 30px;
}
ol.agenda_list, ol.agenda_list ol {
margin: 0 0 0 25px;
padding: 0;
list-style-type: none;
}
ol.agenda_list {
margin: 0;
}
.agenda_list li {
margin: 5px 0 0 0;
padding: 0;
}
.agenda_list li > div {
border: 1px solid #d4d4d4;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
border-color: #D4D4D4 #D4D4D4 #BCBCBC;
padding: 6px;
margin: 0;
background: #f6f6f6;
background: -moz-linear-gradient(top, #ffffff 0%, #f6f6f6 47%, #ededed 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(47%,#f6f6f6), color-stop(100%,#ededed));
background: -webkit-linear-gradient(top, #ffffff 0%,#f6f6f6 47%,#ededed 100%);
background: -o-linear-gradient(top, #ffffff 0%,#f6f6f6 47%,#ededed 100%);
background: -ms-linear-gradient(top, #ffffff 0%,#f6f6f6 47%,#ededed 100%);
background: linear-gradient(to bottom, #ffffff 0%,#f6f6f6 47%,#ededed 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#ededed',GradientType=0 );
overflow: auto;
}
.agenda_list.sortable li > div {
cursor: move;
}
.disclose {
cursor: pointer;
width: 10px;
display: none;
}
.placeholder {
outline: 1px dashed #4183C4;
}
.agenda_list .openclose { width: 40px; float: left; min-height: 1px; }
.agenda_list .duration { width: 92px; float: right; min-height: 1px; padding-left: 5px; }
.agenda_list .manage { width: 150px; float: right; min-height: 1px; padding-left: 5px; }
.agenda_list .title { float: left; }
.agenda_list .optional { float: left; }
.agenda_list .opener_closer { float: left; margin-right: 10px; }
.agenda_list .optional { float: right; width: 208px; padding-left: 8px;}
#menu-overview .manage { width: 150px; }
#menu-overview .duration { width: 75px; }
#menu-overview .optional { width: 200px; }
@media screen and (max-width: 1000px) {
#menu-overview .optional { width: 150px; }
.agenda_list .optional { float: right; width: 158px; padding-left: 8px;}
}
@media screen and (max-width: 800px) {
#menu-overview .optional { width: 100px; }
.agenda_list .optional { float: right; width: 108px; padding-left: 8px;}
}
.agenda_list .opener, .agenda_list .closer { }
.agenda_list .opener { display: none; }
.agenda_list li.closed ol { display: none; }
.agenda_list li.closed .closer { display: none; }
.agenda_list li.closed > div .opener { display: inline-block; }

View File

@ -1,70 +1,71 @@
{% load i18n %} {% load i18n %}
{% load tags %} {% load tags %}
<td> <div>
{% if perms.agenda.can_manage_agenda %}
<a href="{% if item.closed %}{% url 'item_open' item.id %}{% else %}{% url 'item_close' item.id %}{% endif %}"
class="close_link btn btn-mini {% if item.closed %}btn-success{% endif %}" title="{% trans 'Change status (open/closed)' %}">
<i class="{% if item.closed %}icon-checked-new{% else %}icon-unchecked-new{% endif %}"></i>
</a>
{% else %}
<span class="close_link">
<i class="{% if item.closed %}icon-checked-new{% else %}icon-unchecked-new{% endif %}"></i>
</span>
{% endif %}
</td>
<td>
{% for p in item.get_ancestors %}
<div class="indentation">&nbsp;</div>
{% endfor %}
{% if perms.agenda.can_manage_agenda %}
<div class="dragcell"></div>
{% endif %}
<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 %}
<td class="optional">
{{ item.comment|first_line }}
</td>
{% endif %}
{% if perms.agenda.can_see_orga_items %}
<td>
{% if item.duration %}
{{ item.duration }}h {% if start and end %}<a {% if item.tooltip %}rel="tooltip" data-original-title="{% trans 'End' %}: {{ item.tooltip|date:"DATETIME_FORMAT" }}"{% endif %}><i class="icon-clock"></i></a>{% endif %}
{% 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> <div class="manage">
<span style="width: 1px; white-space: nowrap;"> <span style="width: 1px; white-space: nowrap;">
{% if perms.projector.can_manage_projector %} {% if perms.projector.can_manage_projector %}
<a href="{% url 'projector_activate_slide' item.sid %}" class="activate_link btn {% if item.active %}btn-primary{% endif %} btn-mini" title="{% trans 'Show' %}"> <a href="{% url 'projector_activate_slide' node.sid %}" class="activate_link btn {% if node.active %}btn-primary{% endif %} btn-mini" title="{% trans 'Show' %}">
<i class="icon-facetime-video {% if item.active %}icon-white{% endif %}"></i> <i class="icon-facetime-video {% if node.active %}icon-white{% endif %}"></i>
</a> </a>
{% endif %} {% endif %}
{% if perms.agenda.can_manage_agenda %} {% if perms.agenda.can_manage_agenda %}
<a href="{% model_url item 'edit' %}" title="{% trans 'Edit' %}" class="btn btn-mini"> <a href="{% model_url node 'edit' %}" title="{% trans 'Edit' %}" class="btn btn-mini">
<i class="icon-pencil"></i> <i class="icon-pencil"></i>
</a> </a>
<a href="{% model_url item 'delete' %}" title="{% trans 'Delete' %}" class="btn btn-mini"> <a href="{% model_url node 'delete' %}" title="{% trans 'Delete' %}" class="btn btn-mini">
<i class="icon-remove"></i> <i class="icon-remove"></i>
</a> </a>
{% if not item.is_leaf_node %} {% if not node.is_leaf_node %}
<a href="{% url 'projector_activate_slide' item.sid 'summary' %}" class="activate_link btn btn-mini" title="{% trans 'Show summary for this item' %}"> <a href="{% url 'projector_activate_slide' node.sid 'summary' %}" class="activate_link btn btn-mini" title="{% trans 'Show summary for this item' %}">
<i class="icon-summary"></i> <i class="icon-summary"></i>
</a> </a>
{% endif %} {% endif %}
{% endif %} {% endif %}
</span> </span>
</td> </div>
{% endif %} {% endif %}
<td class="tabledrag-hide" style="display: none;">
{% with form=item.weight_form %} {% if perms.agenda.can_see_orga_items %}
<div class="duration">
{% if node.duration %}
{{ node.duration }}h {% if start and end %}<a {% if node.tooltip %}rel="tooltip" data-original-title="{% trans 'End' %}: {{ node.tooltip|date:"DATETIME_FORMAT" }}"{% endif %}><i class="icon-clock"></i></a>{% endif %}
{% endif %}
</div>
{% endif %}
{% if perms.agenda.can_manage_agenda %}
<div class="optional">
{{ node.comment|first_line }}
</div>
{% endif %}
<div class="opener_closer">
<a href="#" class="opener btn btn-mini"><span class="icon-plus"></span></a>
<a href="#" class="closer btn btn-mini"><span class="icon-minus"></span></a>
</div>
<div class="openclose">
{% if perms.agenda.can_manage_agenda %}
<a href="{% if node.closed %}{% url 'item_open' node.id %}{% else %}{% url 'item_close' node.id %}{% endif %}"
class="close_link btn btn-mini {% if node.closed %}btn-success{% endif %}" title="{% trans 'Change status (open/closed)' %}">
<i class="{% if node.closed %}icon-checked-new{% else %}icon-unchecked-new{% endif %}"></i>
</a>
{% else %}
<span class="close_link">
<i class="{% if node.closed %}icon-checked-new{% else %}icon-unchecked-new{% endif %}"></i>
</span>
{% endif %}
</div>
<div class="title">
{% with form=node.weight_form %}
{{ form.weight }} {{ form.weight }}
{{ form.self }} {{ form.self }}
{{ form.parent }} {{ form.parent }}
{% endwith %} {% endwith %}
</td> <a href="{% model_url node 'view' %}">{% if node.type == node.ORGANIZATIONAL_ITEM %}<i>[{% endif %}{{ node }}{% if node.type == node.ORGANIZATIONAL_ITEM %}]</i>{% endif %}</a>
{{ node.get_title_supplement|safe }}
</div>
</div>

View File

@ -8,8 +8,8 @@
{% block title %}{{ block.super }} {% trans "Agenda" %}{% endblock %} {% block title %}{{ block.super }} {% trans "Agenda" %}{% endblock %}
{% block header %} {% block header %}
<link type="text/css" rel="stylesheet" media="all" href="{% static 'styles/tabledrag.css' %}" />
<link type="text/css" rel="stylesheet" media="all" href="{% static 'styles/agenda.css' %}" /> <link type="text/css" rel="stylesheet" media="all" href="{% static 'styles/agenda.css' %}" />
<link type="text/css" rel="stylesheet" media="all" href="{% static 'styles/agenda_sort.css' %}" />
{% endblock %} {% endblock %}
{% block javascript %} {% block javascript %}
@ -17,26 +17,11 @@
<script type="text/javascript" src="{% static 'javascript/jquery.cookie.js' %}"></script> <script type="text/javascript" src="{% static 'javascript/jquery.cookie.js' %}"></script>
{% if perms.agenda.can_manage_agenda %} {% if perms.agenda.can_manage_agenda %}
<script type="text/javascript" src="{% static 'javascript/jquery.once.js' %}"></script> <script type="text/javascript" src="{% static 'javascript/jquery.once.js' %}"></script>
<script type="text/javascript" src="{% static 'javascript/jquery-ui.custom.min.js' %}"></script>
<script type="text/javascript" src="{% static 'javascript/jquery.tmpl.js' %}"></script> <script type="text/javascript" src="{% static 'javascript/jquery.tmpl.js' %}"></script>
<script type="text/javascript" src="{% static 'javascript/tabledrag.js' %}"></script> <script type="text/javascript" src="{% static 'javascript/jquery.mjs.nestedSortable.js' %}"></script>
<script type="text/javascript">
<!--//--><![CDATA[//><!--
//This is Drupal Code
jQuery.extend(
Drupal.settings,
{
"tableDrag":
{ "menu-overview":
{ "menu-plid":
[ { "target": "menu-plid", "source": "menu-mlid", "relationship": "parent", "action": "match", "hidden": true, "limit": 8 } ],
"menu-weight":
[ { "target": "menu-weight", "source": "menu-weight", "relationship": "sibling", "action": "order", "hidden": true, "limit": 0 } ]
}
}
});
//--><!]]>
</script>
{% endif %} {% endif %}
<script type="text/javascript" src="{% static 'javascript/agenda_sort.js' %}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@ -84,55 +69,57 @@
</i></small> </i></small>
<table id="menu-overview" class="table table-striped table-bordered"> <table id="menu-overview" class="table table-striped table-bordered">
<tr> <tr>
<th class="mini_width"></th> <th class="openclose"></th>
<th>{% trans "Item" %}</th> <th class="title">{% trans "Item" %}</th>
{% if perms.agenda.can_manage_agenda %} {% if perms.agenda.can_manage_agenda %}
<th width="200" class="optional">{% trans "Comment" %}</th> <th class="optional">{% trans "Comment" %}</th>
{% endif %} {% endif %}
{% if perms.agenda.can_see_orga_items %} {% if perms.agenda.can_see_orga_items %}
<th width="50">{% trans "Duration" %}</th> <th class="duration">{% trans "Duration" %}</th>
{% endif %} {% 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="manage">{% trans "Actions" %}</th>
{% endif %} {% endif %}
<th class="tabledrag-hide" style="display: none;">
{% trans "Weight" %}
</th>
</tr> </tr>
<tr class="topline{% if active_sid == 'agenda' %} activeline{% endif %}"> <tr class="topline{% if active_sid == 'agenda' %} activeline{% endif %}">
<td></td> <td class="openclose"></td>
<td> <td class="title">
{% trans "Agenda" %} {% trans "Agenda" %}
</td> </td>
{% 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 %} {% if perms.agenda.can_see_orga_items %}
<td>{{ duration }}h</td> <td class="duration">{{ duration }}h</td>
{% endif %} {% 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 class="manage">
{% if perms.projector.can_manage_projector %} {% if perms.projector.can_manage_projector %}
<span> <span>
<a href="{% url 'projector_activate_slide' 'agenda' %}" class="activate_link btn {% if active_sid == 'agenda' %}btn-primary{% endif %} btn-mini" title="{% trans 'Show' %}"> <a href="{% url 'projector_activate_slide' 'agenda' %}" class="activate_link btn {% if active_sid == 'agenda' %}btn-primary{% endif %} btn-mini" title="{% trans 'Show' %}">
<i class="icon-facetime-video {% if active_sid == 'agenda' %}icon-white{% endif %}"></i> <i class="icon-facetime-video {% if active_sid == 'agenda' %}icon-white{% endif %}"></i>
</a> </a>
<span> </span>
{% endif %} {% endif %}
</td> </td>
{% endif %} {% endif %}
</tr> </tr>
{% if items %}
{% for item in items %}
<tr class="draggable{% if item.active %} activeline{% endif %}{% if item.closed %} offline{% endif %}">
{% include "agenda/item_row.html" %}
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="5"><i>{% trans "No items available." %}</i></td>
</tr>
{% endif %}
</table> </table>
{% if items %}
<ol class="agenda_list {% if perms.agenda.can_manage_agenda %}sortable{% endif %}">
{% recursetree items %}
<li class="draggable{% if item.active %} activeline{% endif %}{% if item.closed %} offline{% endif %}">
{% include "agenda/item_row.html" %}
{% if not node.is_leaf_node %}
<ol>
{{ children }}
</ol>
{% endif %}
</li>
{% endrecursetree %}
</ol>
{% else %}
<div class="overview_no_items">{% trans "No items available." %}</div>
{% endif %}
</form> </form>
{% endblock %} {% endblock %}

File diff suppressed because it is too large Load Diff

View File

@ -1,49 +0,0 @@
/**
* TableDrag behavior.
*
* @see tabledrag.js
*/
body.drag {
cursor: move;
}
.draggable a.tabledrag-handle {
cursor: move;
float: left; /* LTR */
padding-right: 0.5em ; /* LTR */
text-decoration: none;
}
.draggable .tabledrag-changed {
padding-right: 0.2em;
}
a.tabledrag-handle:hover {
text-decoration: none;
}
a.tabledrag-handle .handle {
background: url("../img/draggable.png") no-repeat scroll 0 0 transparent;
height: 13px;
margin-top: 4px;
width: 13px;
}
a.tabledrag-handle-hover .handle {
background-position: 0 -20px;
}
div.indentation {
float: left; /* LTR */
height: 1.7em;
margin: -0.4em 0.2em -0.4em -0.4em; /* LTR */
padding: 0.42em 0 0.42em 0.6em; /* LTR */
width: 10px;
}
div.tree-child {
background: url(../img/tree.png) no-repeat 11px center; /* LTR */
}
div.tree-child-last {
background: url(../img/tree-bottom.png) no-repeat 11px center; /* LTR */
}
div.tree-child-horizontal {
background: url(../img/tree.png) no-repeat -11px center;
}
.tabledrag-toggle-weight-wrapper {
text-align: right; /* LTR */
display: none;
}