diff --git a/bower.json b/bower.json index 1f9d474fb..44c77e312 100644 --- a/bower.json +++ b/bower.json @@ -3,14 +3,24 @@ "private": true, "dependencies": { "lodash": "~3.0.1", - "jquery": "~1.11.2", + "jquery": "~2.1.4", "jquery.cookie": "~1.4.1", - "bootstrap": "~3.3.1", - "datatables-bootstrap3-plugin": "~0.2.0", - "angular": "~1.3.13", + "bootstrap-css-only": "~3.3.4", + "angular": "~1.3.15", + "angular-bootstrap": "~0.13.0", + "angular-messages": "~1.3.15", + "angular-animate": "~1.3.15", + "angular-csv-import": "~0.0.15", + "angular-loading-bar": "~0.7.1", "angular-ui-router": "~0.2.13", + "angular-ui-select": "~0.11.2", + "angular-ui-tree": "~2.2.0", "angular-gettext": "~2.0.2", + "angular-sanitize": "~1.3.15", + "angular-xeditable": "~0.1.9", "js-data-angular": "~2.1.0", + "ng-fab-form": "~1.2.7", + "ngBootbox": "~0.0.5", "sockjs": "~0.3.4", "font-awesome-bower": "4.3.0" } diff --git a/openslides/agenda/main_menu.py b/openslides/agenda/main_menu.py index db50655a6..f97d00804 100644 --- a/openslides/agenda/main_menu.py +++ b/openslides/agenda/main_menu.py @@ -10,5 +10,5 @@ class AgendaMainMenuEntry(MainMenuEntry): verbose_name = ugettext_lazy('Agenda') required_permission = 'agenda.can_see' default_weight = 20 - pattern_name = 'item_overview' + pattern_name = '/agenda' # TODO: use generic solution, see issue #1469 icon_css_class = 'glyphicon-calendar' diff --git a/openslides/agenda/static/img/glyphicons_300_microphone.png b/openslides/agenda/static/img/glyphicons_300_microphone.png deleted file mode 100644 index bde033ca3..000000000 Binary files a/openslides/agenda/static/img/glyphicons_300_microphone.png and /dev/null differ diff --git a/openslides/agenda/static/js/agenda.js b/openslides/agenda/static/js/agenda.js deleted file mode 100644 index bda780b12..000000000 --- a/openslides/agenda/static/js/agenda.js +++ /dev/null @@ -1,142 +0,0 @@ -/* - * JavaScript functions for agenda. - * - */ - -function hideClosedSlides(hide) { - if (hide) { - $('#hidelink').attr('title', 'show'); - $('#hidelink').removeClass('hide').addClass('show'); - $('.agenda_list .icon-checked-new').each(function() { - $(this).parents("li").first().hide(); - }); - var hidden = $(".agenda_list li:hidden").length; - $('#hiddencount').text(interpolate(gettext(', of which %s are hidden.'), [hidden])); - } else { - $('.agenda_list li').show(); - $('#hidelink').attr('title','hide'); - $('#hidelink').removeClass('show').addClass('hide'); - $('#hiddencount').text(''); - } - return false; -} - -$('#coming_speakers_changed_form').submit(function() { - $('#sort_order').val($('#coming_speakers').sortable("toArray")); -}); - -$(function() { - // change participant status (on/off) - $('.close_link').click(function(event) { - event.preventDefault(); - var link = $(this); - $.ajax({ - type: 'GET', - url: $(this).attr('href'), - dataType: 'json', - success: function(data) { - if (data.closed) { - newclass = 'icon-checked-new'; - link.parent().parent().addClass('offline'); - link.addClass('btn-success'); - } else { - newclass = 'icon-unchecked-new'; - link.parent().parent().removeClass('offline'); - link.removeClass('btn-success'); - } - link.children('i').removeClass('icon-checked-new icon-unchecked-new').addClass(newclass); - link.attr('href', data.link); - } - }); - }); - // filter to show/hide closed items - $('#hide_closed_items').click(function(event) { - // show all items - if ($.cookie('Slide.HideClosed') == 1) { - $.cookie('Slide.HideClosed', 0); - hideClosedSlides(false); - $('#hide_closed_items').attr('checked', false); - } - else { // hide closed items - $.cookie('Slide.HideClosed', 1); - hideClosedSlides(true); - $('#hide_closed_items').attr('checked', true); - } - }); - - // TODO: Fix this code and reactivate it again - //# if ($.cookie('Slide.HideClosed') === null) { - //# $('#hide_closed_items').attr('checked', false); - //# $.cookie('Slide.HideClosed', 0); - //# } else if ($.cookie('Slide.HideClosed') == 1) { - //# hideClosedSlides(true); - //# $('#hide_closed_items').attr('checked', true); - //# } - - if ($('#coming_speakers').length > 0) { - $('#coming_speakers').sortable({axis: "y", containment: "parent", update: function(event, ui) { - $('#coming_speakers_changed_form').show(); - }}); - $('#coming_speakers').disableSelection(); - } - - - /* - * function for sorting agenda items - * - */ - 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"); - }); -}); diff --git a/openslides/agenda/static/js/agenda/agenda.js b/openslides/agenda/static/js/agenda/agenda.js index bf7c5387c..763adcdce 100644 --- a/openslides/agenda/static/js/agenda/agenda.js +++ b/openslides/agenda/static/js/agenda/agenda.js @@ -15,21 +15,56 @@ angular.module('OpenSlidesApp.agenda', []) resolve: { items: function(Agenda) { return Agenda.findAll(); + }, + tree: function($http) { + return $http.get('/rest/agenda/item/tree/'); + } + } + }) + .state('agenda.item.create', { + resolve: { + types: function($http) { + // get all item types + return $http({ 'method': 'OPTIONS', 'url': '/rest/agenda/item/' }); } } }) - .state('agenda.item.create', {}) .state('agenda.item.detail', { resolve: { item: function(Agenda, $stateParams) { return Agenda.find($stateParams.id); + }, + users: function(User) { + return User.findAll(); } } }) .state('agenda.item.detail.update', { views: { '@agenda.item': {} + }, + resolve: { + types: function($http) { + // get all item types + return $http({ 'method': 'OPTIONS', 'url': '/rest/agenda/item/' }); + } } + }) + .state('agenda.item.sort', { + resolve: { + items: function(Agenda) { + return Agenda.findAll(); + }, + tree: function($http) { + return $http.get('/rest/agenda/item/tree/'); + } + }, + url: '/sort', + controller: 'AgendaSortCtrl', + }) + .state('agenda.item.csv-import', { + url: '/csv-import', + controller: 'AgendaCSVImportCtrl', }); }) @@ -40,30 +75,84 @@ angular.module('OpenSlidesApp.agenda', []) }); }) -.controller('ItemListCtrl', function($scope, Agenda, i18n) { +.controller('ItemListCtrl', function($scope, Agenda, tree) { Agenda.bindAll({}, $scope, 'items'); - $scope.test_plural = i18n.ngettext('test', 'tests', 2); - $scope.test_singular = i18n.ngettext('test', 'tests', 1); -}) -.controller('ItemDetailCtrl', function($scope, Agenda, item) { - Agenda.bindOne($scope, 'item', item.id); -}) + // get a 'flat' (ordered) array of agenda tree to display in table + $scope.flattenedTree = buildTree(tree.data); + function buildTree(tree, level = 0) { + var nodes = []; + var defaultlevel = level; + _.each(tree, function(node) { + level = defaultlevel; + if (node.id) { + nodes.push({ id: node.id, level: level }); + } + if (node.children) { + level++; + var child = buildTree(node.children, level); + if (child.length) { + nodes = nodes.concat(child); + } + } + }); + return nodes; + } -.controller('ItemCreateCtrl', function($scope, Agenda) { - $scope.item = {}; - $scope.save = function (item) { - item.weight = 0; // TODO: the rest_api should do this - item.tags = []; // TODO: the rest_api should do this - Agenda.create(item); - // TODO: redirect to list-view - }; -}) - -.controller('ItemUpdateCtrl', function($scope, Agenda, item) { - $scope.item = item; // do not use Agenda.binOne(...) so autoupdate is not activated + // save changed item $scope.save = function (item) { Agenda.save(item); - // TODO: redirect to list-view }; + // delete selected item + $scope.delete = function (id) { + Agenda.destroy(id); + }; +}) + +.controller('ItemDetailCtrl', function($scope, Agenda, User, item) { + Agenda.bindOne(item.id, $scope, 'item'); + User.bindAll({}, $scope, 'users'); +}) + +.controller('ItemCreateCtrl', function($scope, $state, Agenda, types) { + $scope.types = types.data.actions.POST.type.choices; // get all item types + $scope.save = function (item) { + if (!item) + return null; + item.weight = 0; // TODO: the rest_api should do this + item.tags = []; // TODO: the rest_api should do this + Agenda.create(item).then( + function(success) { + $state.go('agenda.item.list'); + } + ); + }; +}) + +.controller('ItemUpdateCtrl', function($scope, $state, Agenda, types, item) { + $scope.types = types.data.actions.POST.type.choices; // get all item types + $scope.item = item; + $scope.save = function (item) { + Agenda.save(item).then( + function(success) { + $state.go('agenda.item.list'); + } + ); + }; +}) + +.controller('AgendaSortCtrl', function($scope, $http, Agenda, tree) { + Agenda.bindAll({}, $scope, 'items'); + $scope.tree = tree.data; + + // set changed agenda tree + $scope.treeOptions = { + dropped: function(e) { + $http.put('/rest/agenda/item/tree/', {tree: $scope.tree}); + } + }; +}) + +.controller('AgendaCSVImportCtrl', function($scope, Agenda) { + // TODO }); diff --git a/openslides/agenda/static/js/agenda_current_list_of_speakers_projector.js b/openslides/agenda/static/js/agenda_current_list_of_speakers_projector.js deleted file mode 100644 index 432c4bb6d..000000000 --- a/openslides/agenda/static/js/agenda_current_list_of_speakers_projector.js +++ /dev/null @@ -1,14 +0,0 @@ -/* - * JavaScript functions for agenda CurrentListOfSpeakersProjectorView - */ - -function reloadListOfSpeakers() { - $.ajax({ - url: '', - success: function (data) { - updater.updateProjector(data); - setTimeout('reloadListOfSpeakers()', 2000); - }, - dataType: 'json' - }); -} diff --git a/openslides/agenda/static/templates/agenda/item-csv-import.html b/openslides/agenda/static/templates/agenda/item-csv-import.html new file mode 100644 index 000000000..18b95ea27 --- /dev/null +++ b/openslides/agenda/static/templates/agenda/item-csv-import.html @@ -0,0 +1,39 @@ +

Import agenda items

+ + + +

Select a CSV file to import agenda items! + +

Please note:

+ + + +
+
+ + +

The file has to be encoded in UTF-8. +

+ + + + +
diff --git a/openslides/agenda/static/templates/agenda/item-detail.html b/openslides/agenda/static/templates/agenda/item-detail.html index 09811fd77..a7267d2f5 100644 --- a/openslides/agenda/static/templates/agenda/item-detail.html +++ b/openslides/agenda/static/templates/agenda/item-detail.html @@ -1,2 +1,56 @@

{{ item.get_title }}

+ + + {{ item.text }} + +
+

Duration

+ {{ item.duration }} +
+ +
+

Comment

+ {{ item.comment }} +
+ + +

List of speakers

+ +
+
+ + + {{ $select.selected.get_short_name() }} + + +
+ +
+
+
+
+ diff --git a/openslides/agenda/static/templates/agenda/item-form.html b/openslides/agenda/static/templates/agenda/item-form.html index f2330908c..c34dc158b 100644 --- a/openslides/agenda/static/templates/agenda/item-form.html +++ b/openslides/agenda/static/templates/agenda/item-form.html @@ -1,8 +1,51 @@ -

{{ item.title }}

-

Neuer Eintrag

+

Edit agenda item

+

New agenda item

-
- Titel:
- Text:

- + + + +
+ + +
+
+ + +
+
+ +
- + + + +
+ + +
+
+ +