Merge pull request #2277 from FinnStutzenstein/FeatureCSV
Improved motions table with CSV export
This commit is contained in:
commit
2753af3585
@ -96,6 +96,21 @@ angular.module('OpenSlidesApp.agenda', ['OpenSlidesApp.users'])
|
|||||||
}
|
}
|
||||||
return title;
|
return title;
|
||||||
},
|
},
|
||||||
|
getItemNumberWithAncestors: function (agendaId) {
|
||||||
|
if (!agendaId) {
|
||||||
|
agendaId = this.id;
|
||||||
|
}
|
||||||
|
var agendaItem = DS.get(name, agendaId);
|
||||||
|
if (!agendaItem) {
|
||||||
|
return '';
|
||||||
|
} else if (agendaItem.item_number) {
|
||||||
|
return agendaItem.item_number;
|
||||||
|
} else if (agendaItem.parent_id) {
|
||||||
|
return this.getItemNumberWithAncestors(agendaItem.parent_id);
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
},
|
||||||
// override project function of jsDataModel factory
|
// override project function of jsDataModel factory
|
||||||
project: function() {
|
project: function() {
|
||||||
return $http.post(
|
return $http.post(
|
||||||
|
@ -670,6 +670,100 @@ img {
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Motion **/
|
||||||
|
#motion-table .row {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#motion-table .data-row:hover {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
#motion-table .data-row > div {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#motion-table .header-row {
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-row > div {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#motion-table .main-header {
|
||||||
|
width: calc(100% - 50px);
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
#motion-table .main-header .form-inline {
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#motion-table .content > div {
|
||||||
|
display: inline-block;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
#motion-table .identifier-col {
|
||||||
|
width: 50px;
|
||||||
|
min-height: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#motion-table .identifier-col > div {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#motion-table .title-col {
|
||||||
|
width: calc(100% - 50px);
|
||||||
|
}
|
||||||
|
|
||||||
|
#motion-table .title-col, #motion-table small {
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
#motion-table .content > div > div {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#motion-table .content > div > div:last-child {
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#motion-table .row .centered {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#motion-table .row .col-xs-1 {
|
||||||
|
width: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#motion-table .row .col-xs-4 {
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#motion-table .dropdown {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#motion-table .dropdown > span, #motion-table .sort-spacer {
|
||||||
|
padding: 5px 10px 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#motion-table .dropdown-entry {
|
||||||
|
padding: 5px 10px 5px 10px;
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#motion-table .title {
|
||||||
|
margin-right: 10px;
|
||||||
|
padding: 0;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
/** Footer **/
|
/** Footer **/
|
||||||
#footer {
|
#footer {
|
||||||
float: left;
|
float: left;
|
||||||
@ -681,11 +775,23 @@ img {
|
|||||||
|
|
||||||
/** General helper classes **/
|
/** General helper classes **/
|
||||||
|
|
||||||
|
.disabled {
|
||||||
|
color: #555;
|
||||||
|
cursor: not-allowed !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
background-color: #317796;
|
background-color: #317796;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dropdown-menu {
|
||||||
|
margin-left: 0px !important;
|
||||||
|
}
|
||||||
|
|
||||||
.dropdown-entries {
|
.dropdown-entries {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
@ -717,6 +823,14 @@ img {
|
|||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.spacer-left {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacer-left-lg {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.lead-div {
|
.lead-div {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
@ -764,6 +878,10 @@ img {
|
|||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.badge-info {
|
||||||
|
background-color: #f0ad4e;
|
||||||
|
}
|
||||||
|
|
||||||
.listOfSpeakers h3 {
|
.listOfSpeakers h3 {
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
}
|
}
|
||||||
@ -1053,7 +1171,7 @@ tr.hiddenrow td {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tr.activeline td, li.activeline, .projected {
|
tr.activeline td, li.activeline, .projected {
|
||||||
background-color: #bed4de;
|
background-color: #bed4de !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
tr.selected td {
|
tr.selected td {
|
||||||
@ -1119,11 +1237,11 @@ tr.selected td {
|
|||||||
|
|
||||||
#chatbox { width: 100%; top: 40px; }
|
#chatbox { width: 100%; top: 40px; }
|
||||||
|
|
||||||
/* hide marked element / column */
|
|
||||||
.optional, .hide-sm { display: none; }
|
|
||||||
|
|
||||||
/* show replacement elements, if any */
|
/* show replacement elements, if any */
|
||||||
.optional-show { display: block !important; }
|
.optional-show { display: block !important; }
|
||||||
|
|
||||||
|
/* hide marked element / column */
|
||||||
|
.optional, .hide-sm { display: none !important; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* display for resolutions smaller that 560px */
|
/* display for resolutions smaller that 560px */
|
||||||
|
@ -460,8 +460,7 @@ angular.module('OpenSlidesApp.core', [
|
|||||||
])
|
])
|
||||||
|
|
||||||
.filter('osFilter', [
|
.filter('osFilter', [
|
||||||
'$filter',
|
function () {
|
||||||
function ($filter) {
|
|
||||||
return function (array, string, getFilterString) {
|
return function (array, string, getFilterString) {
|
||||||
if (!string) {
|
if (!string) {
|
||||||
return array;
|
return array;
|
||||||
@ -473,6 +472,30 @@ angular.module('OpenSlidesApp.core', [
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
// This filter filters all items in array. If the filterArray is empty, the array is passed.
|
||||||
|
// The filterArray contains numbers of the multiselect: [1, 3, 4].
|
||||||
|
// Then, all items in array are passed, if the item_id (get with id_function) matches one of the
|
||||||
|
// ids in filterArray. id_function could also return a list of ids. Example:
|
||||||
|
// Item 1 has two tags with ids [1, 4]. filterArray = [3, 4] --> match
|
||||||
|
.filter('SelectMultipleFilter', [
|
||||||
|
function () {
|
||||||
|
return function (array, filterArray, idFunction) {
|
||||||
|
if (filterArray.length === 0) {
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
return Array.prototype.filter.call(array, function (item) {
|
||||||
|
var id = idFunction(item);
|
||||||
|
if (!id) {
|
||||||
|
return false;
|
||||||
|
} else if (typeof id === 'number') {
|
||||||
|
id = [id];
|
||||||
|
}
|
||||||
|
return _.intersection(id, filterArray).length > 0;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
// mark HTML as "trusted"
|
// mark HTML as "trusted"
|
||||||
.filter('trusted', [
|
.filter('trusted', [
|
||||||
'$sce',
|
'$sce',
|
||||||
|
@ -793,6 +793,7 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
|||||||
.controller('MotionListCtrl', [
|
.controller('MotionListCtrl', [
|
||||||
'$scope',
|
'$scope',
|
||||||
'$state',
|
'$state',
|
||||||
|
'$http',
|
||||||
'ngDialog',
|
'ngDialog',
|
||||||
'MotionForm',
|
'MotionForm',
|
||||||
'Motion',
|
'Motion',
|
||||||
@ -800,7 +801,8 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
|||||||
'Tag',
|
'Tag',
|
||||||
'Workflow',
|
'Workflow',
|
||||||
'User',
|
'User',
|
||||||
function($scope, $state, ngDialog, MotionForm, Motion, Category, Tag, Workflow, User) {
|
'Agenda',
|
||||||
|
function($scope, $state, $http, ngDialog, MotionForm, Motion, Category, Tag, Workflow, User, Agenda) {
|
||||||
Motion.bindAll({}, $scope, 'motions');
|
Motion.bindAll({}, $scope, 'motions');
|
||||||
Category.bindAll({}, $scope, 'categories');
|
Category.bindAll({}, $scope, 'categories');
|
||||||
Tag.bindAll({}, $scope, 'tags');
|
Tag.bindAll({}, $scope, 'tags');
|
||||||
@ -812,6 +814,32 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
|||||||
$scope.sortColumn = 'identifier';
|
$scope.sortColumn = 'identifier';
|
||||||
$scope.filterPresent = '';
|
$scope.filterPresent = '';
|
||||||
$scope.reverse = false;
|
$scope.reverse = false;
|
||||||
|
|
||||||
|
$scope.multiselectFilter = {
|
||||||
|
state: [],
|
||||||
|
category: [],
|
||||||
|
tag: []
|
||||||
|
};
|
||||||
|
$scope.getItemId = {
|
||||||
|
state: function (motion) {return motion.state_id;},
|
||||||
|
category: function (motion) {return motion.category_id;},
|
||||||
|
tag: function (motion) {return motion.tags_id;}
|
||||||
|
};
|
||||||
|
// function to operate the multiselectFilter
|
||||||
|
$scope.operateMultiselectFilter = function (filter, id) {
|
||||||
|
if (!$scope.isDeleteMode) {
|
||||||
|
if (_.indexOf($scope.multiselectFilter[filter], id) > -1) {
|
||||||
|
// remove id
|
||||||
|
$scope.multiselectFilter[filter] = _.filter($scope.multiselectFilter[filter], function (_id) {
|
||||||
|
return _id != id;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// add id
|
||||||
|
$scope.multiselectFilter[filter].push(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
// function to sort by clicked column
|
// function to sort by clicked column
|
||||||
$scope.toggleSort = function (column) {
|
$scope.toggleSort = function (column) {
|
||||||
if ( $scope.sortColumn === column ) {
|
if ( $scope.sortColumn === column ) {
|
||||||
@ -852,6 +880,23 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
|||||||
category,
|
category,
|
||||||
].join(" ");
|
].join(" ");
|
||||||
};
|
};
|
||||||
|
// for reset-button
|
||||||
|
$scope.reset_filters = function () {
|
||||||
|
$scope.multiselectFilter = {
|
||||||
|
state: [],
|
||||||
|
category: [],
|
||||||
|
tag: []
|
||||||
|
};
|
||||||
|
if ($scope.filter) {
|
||||||
|
$scope.filter.search = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
$scope.are_filters_set = function () {
|
||||||
|
return $scope.multiselectFilter.state.length > 0 ||
|
||||||
|
$scope.multiselectFilter.category.length > 0 ||
|
||||||
|
$scope.multiselectFilter.tag.length > 0 ||
|
||||||
|
($scope.filter ? $scope.filter.search : false);
|
||||||
|
};
|
||||||
|
|
||||||
// collect all states of all workflows
|
// collect all states of all workflows
|
||||||
// TODO: regard workflows only which are used by motions
|
// TODO: regard workflows only which are used by motions
|
||||||
@ -861,7 +906,7 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
|||||||
if (workflows.length > 1) {
|
if (workflows.length > 1) {
|
||||||
var wf = {};
|
var wf = {};
|
||||||
wf.name = workflow.name;
|
wf.name = workflow.name;
|
||||||
wf.workflowSeparator = "-";
|
wf.workflowHeader = true;
|
||||||
$scope.states.push(wf);
|
$scope.states.push(wf);
|
||||||
}
|
}
|
||||||
angular.forEach(workflow.states, function (state) {
|
angular.forEach(workflow.states, function (state) {
|
||||||
@ -869,41 +914,83 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// update state
|
||||||
|
$scope.updateState = function (motion, state_id) {
|
||||||
|
$http.put('/rest/motions/motion/' + motion.id + '/set_state/', {'state': state_id});
|
||||||
|
};
|
||||||
|
// reset state
|
||||||
|
$scope.reset_state = function (motion) {
|
||||||
|
$http.put('/rest/motions/motion/' + motion.id + '/set_state/', {});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.has_tag = function (motion, tag) {
|
||||||
|
return _.indexOf(motion.tags_id, tag.id) > -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use this methon instead of Motion.save(), because otherwise
|
||||||
|
// you have to provide always a title and a text
|
||||||
|
var save = function (motion) {
|
||||||
|
motion.title = motion.getTitle(-1);
|
||||||
|
motion.text = motion.getText(-1);
|
||||||
|
motion.reason = motion.getReason(-1);
|
||||||
|
Motion.save(motion);
|
||||||
|
};
|
||||||
|
$scope.toggle_tag = function (motion, tag) {
|
||||||
|
if ($scope.has_tag(motion, tag)) {
|
||||||
|
// remove
|
||||||
|
motion.tags_id = _.filter(motion.tags_id, function (tag_id){
|
||||||
|
return tag_id != tag.id;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
motion.tags_id.push(tag.id);
|
||||||
|
}
|
||||||
|
save(motion);
|
||||||
|
};
|
||||||
|
$scope.toggle_category = function (motion, category) {
|
||||||
|
if (motion.category_id == category.id) {
|
||||||
|
motion.category_id = null;
|
||||||
|
} else {
|
||||||
|
motion.category_id = category.id;
|
||||||
|
}
|
||||||
|
save(motion);
|
||||||
|
};
|
||||||
|
|
||||||
// open new/edit dialog
|
// open new/edit dialog
|
||||||
$scope.openDialog = function (motion) {
|
$scope.openDialog = function (motion) {
|
||||||
ngDialog.open(MotionForm.getDialog(motion));
|
ngDialog.open(MotionForm.getDialog(motion));
|
||||||
};
|
};
|
||||||
// cancel QuickEdit mode
|
|
||||||
$scope.cancelQuickEdit = function (motion) {
|
// Export the given motions as a csv file
|
||||||
// revert all changes by restore (refresh) original motion object from server
|
$scope.csv_export = function () {
|
||||||
Motion.refresh(motion);
|
var element = document.getElementById('downloadLink');
|
||||||
motion.quickEdit = false;
|
var csvRows = [
|
||||||
};
|
['identifier', 'title', 'text', 'reason', 'submitter', 'category', 'origin'],
|
||||||
// save changed motion
|
];
|
||||||
$scope.save = function (motion) {
|
angular.forEach($scope.motionsFiltered, function (motion) {
|
||||||
// get (unchanged) values from latest version for update method
|
var row = [];
|
||||||
motion.title = motion.getTitle(-1);
|
row.push('"' + motion.identifier + '"');
|
||||||
motion.text = motion.getText(-1);
|
row.push('"' + motion.getTitle() + '"');
|
||||||
motion.reason = motion.getReason(-1);
|
row.push('"' + motion.getText() + '"');
|
||||||
Motion.save(motion).then(
|
row.push('"' + motion.getReason() + '"');
|
||||||
function(success) {
|
row.push('"' + motion.submitters[0].get_full_name() + '"');
|
||||||
motion.quickEdit = false;
|
var category = motion.category ? motion.category.name : '';
|
||||||
$scope.alert.show = false;
|
row.push('"' + category + '"');
|
||||||
},
|
row.push('"' + motion.origin + '"');
|
||||||
function(error){
|
csvRows.push(row);
|
||||||
var message = '';
|
|
||||||
for (var e in error.data) {
|
|
||||||
message += e + ': ' + error.data[e] + ' ';
|
|
||||||
}
|
|
||||||
$scope.alert = { type: 'danger', msg: message, show: true };
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var csvString = csvRows.join("%0A");
|
||||||
|
element.href = 'data:text/csv;charset=utf-8,' + csvString;
|
||||||
|
element.download = 'motions-export.csv';
|
||||||
|
element.target = '_blank';
|
||||||
};
|
};
|
||||||
|
|
||||||
// *** delete mode functions ***
|
// *** delete mode functions ***
|
||||||
$scope.isDeleteMode = false;
|
$scope.isDeleteMode = false;
|
||||||
// check all checkboxes
|
// check all checkboxes from filtered motions
|
||||||
$scope.checkAll = function () {
|
$scope.checkAll = function () {
|
||||||
angular.forEach($scope.motions, function (motion) {
|
$scope.selectedAll = !$scope.selectedAll;
|
||||||
|
angular.forEach($scope.motionsFiltered, function (motion) {
|
||||||
motion.selected = $scope.selectedAll;
|
motion.selected = $scope.selectedAll;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -918,7 +1005,7 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
|||||||
};
|
};
|
||||||
// delete selected motions
|
// delete selected motions
|
||||||
$scope.deleteMultiple = function () {
|
$scope.deleteMultiple = function () {
|
||||||
angular.forEach($scope.motions, function (motion) {
|
angular.forEach($scope.motionsFiltered, function (motion) {
|
||||||
if (motion.selected)
|
if (motion.selected)
|
||||||
Motion.destroy(motion.id);
|
Motion.destroy(motion.id);
|
||||||
});
|
});
|
||||||
|
@ -17,10 +17,6 @@
|
|||||||
<i class="fa fa-download fa-lg"></i>
|
<i class="fa fa-download fa-lg"></i>
|
||||||
<translate>Import</translate>
|
<translate>Import</translate>
|
||||||
</a>
|
</a>
|
||||||
<a ui-sref="motions_pdf" target="_blank" class="btn btn-default btn-sm">
|
|
||||||
<i class="fa fa-file-pdf-o fa-lg"></i>
|
|
||||||
<translate>PDF</translate>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
<h1 translate>Motions</h1>
|
<h1 translate>Motions</h1>
|
||||||
</div>
|
</div>
|
||||||
@ -28,7 +24,7 @@
|
|||||||
|
|
||||||
<div class="details">
|
<div class="details">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-12">
|
||||||
<!-- delete mode -->
|
<!-- delete mode -->
|
||||||
<button os-perms="motions.can_manage" class="btn"
|
<button os-perms="motions.can_manage" class="btn"
|
||||||
ng-class="$parent.isDeleteMode ? 'btn-primary' : 'btn-default'"
|
ng-class="$parent.isDeleteMode ? 'btn-primary' : 'btn-default'"
|
||||||
@ -36,38 +32,39 @@
|
|||||||
<i class="fa fa-check-square-o"></i>
|
<i class="fa fa-check-square-o"></i>
|
||||||
<translate>Select ...</translate>
|
<translate>Select ...</translate>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
<!-- Export dropdown -->
|
||||||
<div class="col-sm-6">
|
<div class="dropdown pull-right" uib-dropdown>
|
||||||
<div class="form-inline text-right">
|
<button type=button" class="btn btn-default" id="dropdownExport" uib-dropdown-toggle>
|
||||||
<div class="form-group">
|
<i class="fa fa-upload"></i>
|
||||||
<div class="input-group">
|
<span ng-if="motionsFiltered.length == motions.length" translate>
|
||||||
<div class="input-group-addon"><i class="fa fa-search"></i></div>
|
Export all
|
||||||
<input type="text" ng-model="filter.search" class="form-control"
|
</span>
|
||||||
placeholder="{{ 'Search' | translate}}">
|
<span ng-if="motionsFiltered.length != motions.length" translate>
|
||||||
</div>
|
Export filtered
|
||||||
</div>
|
</span>
|
||||||
<button class="btn btn-default" ng-click="isFilterOpen = !isFilterOpen"
|
<span class="caret"></span>
|
||||||
ng-class="isFilterOpen ? 'btn-primary' : 'btn-default'">
|
|
||||||
<i class="fa fa-filter"></i>
|
|
||||||
<translate>Filter ...</translate>
|
|
||||||
</button>
|
</button>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownExport">
|
||||||
|
<li>
|
||||||
|
<a ui-sref="motions_pdf" target="_blank">
|
||||||
|
<i class="fa fa-file-pdf-o fa-lg"></i>
|
||||||
|
<translate>PDF</translate>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<!--CSV export -->
|
||||||
|
<li>
|
||||||
|
<a href="" id="downloadLink"
|
||||||
|
os-perms="motions.can_manage"
|
||||||
|
ng-click="csv_export()">
|
||||||
|
<i class="fa fa-file-text-o fa-lg"></i>
|
||||||
|
<translate>CSV</translate>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div uib-collapse="!isFilterOpen" class="row spacer">
|
|
||||||
<div class="col-sm-6 text-right"></div>
|
|
||||||
<div class="col-sm-6 text-right">
|
|
||||||
<!-- state filter -->
|
|
||||||
<select ng-model="stateFilter" class="form-control" id="stateFilter">
|
|
||||||
<option value="" translate>--- Select state ---</option>
|
|
||||||
<option ng-repeat="state in states" value="{{ state.id }}">
|
|
||||||
{{ state.workflowSeparator }}
|
|
||||||
{{ state.name | translate }}
|
|
||||||
{{ state.workflowSeparator }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div uib-collapse="!isDeleteMode" class="row spacer">
|
<div uib-collapse="!isDeleteMode" class="row spacer">
|
||||||
<div class="col-sm-12 text-left">
|
<div class="col-sm-12 text-left">
|
||||||
<!-- delete button -->
|
<!-- delete button -->
|
||||||
@ -85,194 +82,377 @@
|
|||||||
{{ motions.length }} {{ "motions" | translate }}<span ng-if="(motions|filter:{selected:true}).length > 0">,
|
{{ motions.length }} {{ "motions" | translate }}<span ng-if="(motions|filter:{selected:true}).length > 0">,
|
||||||
{{(motions|filter:{selected:true}).length}} {{ "selected" | translate }}</span>
|
{{(motions|filter:{selected:true}).length}} {{ "selected" | translate }}</span>
|
||||||
</div>
|
</div>
|
||||||
<table class="table table-striped table-bordered table-hover">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<!-- projector column -->
|
|
||||||
<th ng-show="!$parent.isDeleteMode" os-perms="core.can_manage_projector" class="minimum">
|
|
||||||
<!-- delete selection column -->
|
|
||||||
<th ng-show="$parent.isDeleteMode" os-perms="motions.can_manage" class="minimum deleteColumn">
|
|
||||||
<input type="checkbox" ng-model="$parent.selectedAll" ng-change="checkAll()">
|
|
||||||
|
|
||||||
<!-- agenda item column -->
|
<div id="motion-table" class="container-fluid">
|
||||||
<th ng-click="toggleSort('agenda_item.item_number')" class="sortable optional">
|
<div class="row header-row">
|
||||||
<translate translate-comment="short form of agenda item">Item</translate>
|
<div class="col-xs-1 centered" ng-show="isDeleteMode">
|
||||||
<i class="pull-right fa" ng-show="sortColumn === 'agenda_item.item_number' && header.sortable != false"
|
<i class="fa text-danger pointer" ng-class=" selectedAll ? 'fa-check-square-o' : 'fa-square-o'"
|
||||||
ng-class="reverse ? 'fa-sort-desc' : 'fa-sort-asc'">
|
ng-click="checkAll()"></i>
|
||||||
</i>
|
</div>
|
||||||
<!-- identifier column -->
|
<div class="col-xs-11 main-header">
|
||||||
<th ng-click="toggleSort('identifier')" class="sortable minimum">
|
|
||||||
<translate>Identifier</translate>
|
|
||||||
<i class="pull-right fa" ng-show="sortColumn === 'identifier' && header.sortable != false"
|
|
||||||
ng-class="reverse ? 'fa-sort-desc' : 'fa-sort-asc'">
|
|
||||||
</i>
|
|
||||||
|
|
||||||
<!-- title column -->
|
<span class="form-inline text-right pull-right">
|
||||||
<th ng-click="toggleSort('getTitle()')" class="sortable">
|
<span class="sort-spacer pointer" ng-click="reset_filters()"
|
||||||
<translate>Title</translate>
|
ng-if="are_filters_set()" ng-disabled="isDeleteMode"
|
||||||
<i class="pull-right fa" ng-show="sortColumn === 'getTitle()' && header.sortable != false"
|
ng-class="{'disabled': isDeleteMode}">
|
||||||
ng-class="reverse ? 'fa-sort-desc' : 'fa-sort-asc'">
|
<i class="fa fa-times-circle"></i>
|
||||||
</i>
|
<translate>Filter</translate>
|
||||||
|
</span>
|
||||||
<!-- submitters column -->
|
<!-- Statefilter -->
|
||||||
<th ng-click="toggleSort('submitters')" class="sortable optional">
|
<span class="dropdown" uib-dropdown>
|
||||||
<translate>Submitters</translate>
|
<span class="pointer" id="dropdownState" uib-dropdown-toggle
|
||||||
<i class="pull-right fa" ng-show="sortColumn === 'submitters' && header.sortable != false"
|
ng-class="{'bold': multiselectFilter.state.length > 0, 'disabled': isDeleteMode}"
|
||||||
ng-class="reverse ? 'fa-sort-desc' : 'fa-sort-asc'">
|
ng-disabled="isDeleteMode">
|
||||||
</i>
|
|
||||||
|
|
||||||
<!-- category column -->
|
|
||||||
<th ng-click="toggleSort('category')" class="sortable optional">
|
|
||||||
<translate>Category</translate>
|
|
||||||
<i class="pull-right fa" ng-show="sortColumn === 'category' && header.sortable != false"
|
|
||||||
ng-class="reverse ? 'fa-sort-desc' : 'fa-sort-asc'">
|
|
||||||
</i>
|
|
||||||
|
|
||||||
<!-- state column -->
|
|
||||||
<th ng-click="toggleSort('state.name')" class="sortable optional">
|
|
||||||
<translate>State</translate>
|
<translate>State</translate>
|
||||||
<i class="pull-right fa" ng-show="sortColumn === 'state.name' && header.sortable != false"
|
<span class="caret"></span>
|
||||||
|
</span>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownState">
|
||||||
|
<li ng-repeat="state in states" ng-class="state.workflowHeader ? 'dropdown-header' : ''">
|
||||||
|
<div class="dropdown-entry pointer" ng-if="state.workflowHeader">
|
||||||
|
{{ state.name | translate }}
|
||||||
|
</div>
|
||||||
|
<div class="dropdown-entry pointer" ng-if="!state.workflowHeader"
|
||||||
|
ng-click="operateMultiselectFilter('state', state.id)">
|
||||||
|
<i class="fa fa-check" ng-if="multiselectFilter.state.indexOf(state.id) > -1"></i>
|
||||||
|
{{ state.name | translate }}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</span>
|
||||||
|
<!-- Categoryfilter -->
|
||||||
|
<span class="dropdown" uib-dropdown ng-if="categories.length > 0">
|
||||||
|
<span class="pointer" id="dropdownCategory" uib-dropdown-toggle
|
||||||
|
ng-class="{'bold': multiselectFilter.category.length > 0, 'disabled': isDeleteMode}"
|
||||||
|
ng-disabled="isDeleteMode">
|
||||||
|
<translate>Category</translate>
|
||||||
|
<span class="caret"></span>
|
||||||
|
</span>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-right"
|
||||||
|
aria-labelledby="dropdownCategory">
|
||||||
|
<li ng-repeat="category in categories">
|
||||||
|
<div class="dropdown-entry pointer"
|
||||||
|
ng-click="operateMultiselectFilter('category', category.id)">
|
||||||
|
<i class="fa fa-check" ng-if="multiselectFilter.category.indexOf(category.id) > -1"></i>
|
||||||
|
{{ category.name }}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</span>
|
||||||
|
<!-- Tagfilter -->
|
||||||
|
<span class="dropdown" uib-dropdown ng-if="tags.length > 0">
|
||||||
|
<span class="pointer" id="dropdownTag" uib-dropdown-toggle
|
||||||
|
ng-class="{'bold': multiselectFilter.tag.length > 0, 'disabled': isDeleteMode}"
|
||||||
|
ng-disabled="isDeleteMode">
|
||||||
|
<translate>Tag</translate>
|
||||||
|
<span class="caret"></span>
|
||||||
|
</span>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-right"
|
||||||
|
aria-labelledby="dropdownTag">
|
||||||
|
<li ng-repeat="tag in tags">
|
||||||
|
<div class="dropdown-entry pointer"
|
||||||
|
ng-click="operateMultiselectFilter('tag', tag.id)">
|
||||||
|
<i class="fa fa-check" ng-if="multiselectFilter.tag.indexOf(tag.id) > -1"></i>
|
||||||
|
{{ tag.name }}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</span>
|
||||||
|
<!-- dropdown sort -->
|
||||||
|
<span class="dropdown" uib-dropdown>
|
||||||
|
<span class="pointer" id="dropdownSort" uib-dropdown-toggle
|
||||||
|
ng-class="{'disabled': isDeleteMode}"
|
||||||
|
ng-disabled="isDeleteMode">
|
||||||
|
<translate>Sort</translate>
|
||||||
|
<span class="caret"></span>
|
||||||
|
</span>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownSort">
|
||||||
|
<li>
|
||||||
|
<!-- item -->
|
||||||
|
<div class="pointer dropdown-entry" ng-click="toggleSort('agenda_item.getItemNumberWithAncestors()')">
|
||||||
|
<translate translate-comment="short form of agenda item">Item</translate>
|
||||||
|
<span class="spacer-right pull-right"></span>
|
||||||
|
<i class="pull-right fa"
|
||||||
|
ng-style="{'visibility': sortColumn === 'agenda_item.getItemNumberWithAncestors()' && header.sortable != false ? 'visible' : 'hidden'}"
|
||||||
ng-class="reverse ? 'fa-sort-desc' : 'fa-sort-asc'">
|
ng-class="reverse ? 'fa-sort-desc' : 'fa-sort-asc'">
|
||||||
</i>
|
</i>
|
||||||
<tbody>
|
</div>
|
||||||
<tr ng-repeat="motion in motionsFiltered = (motions | osFilter: filter.search : getFilterString |
|
</li>
|
||||||
filter: {state_id: stateFilter} | orderBy: sortColumn:reverse)"
|
<li>
|
||||||
class="animate-item"
|
<!-- indentifier -->
|
||||||
ng-class="{ 'activeline': motion.isProjected(), 'selected': motion.selected }">
|
<div class="pointer dropdown-entry" ng-click="toggleSort('identifier')">
|
||||||
|
<translate>Identifier</translate>
|
||||||
|
<span class="spacer-right pull-right"></span>
|
||||||
|
<i class="pull-right fa"
|
||||||
|
ng-style="{'visibility': sortColumn === 'identifier' && header.sortable != false ? 'visible' : 'hidden'}"
|
||||||
|
ng-class="reverse ? 'fa-sort-desc' : 'fa-sort-asc'">
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<!-- title -->
|
||||||
|
<div class="pointer dropdown-entry" ng-click="toggleSort('getTitle()')">
|
||||||
|
<translate>Title</translate>
|
||||||
|
<span class="spacer-right pull-right"></span>
|
||||||
|
<i class="pull-right fa"
|
||||||
|
ng-style="{'visibility': sortColumn === 'getTitle()' && header.sortable != false ? 'visible' : 'hidden'}"
|
||||||
|
ng-class="reverse ? 'fa-sort-desc' : 'fa-sort-asc'">
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<!-- submitters -->
|
||||||
|
<div class="pointer dropdown-entry" ng-click="toggleSort('submitters')">
|
||||||
|
<translate>Submitters</translate>
|
||||||
|
<span class="spacer-right pull-right"></span>
|
||||||
|
<i class="pull-right fa"
|
||||||
|
ng-style="{'visibility': sortColumn === 'submitters' && header.sortable != false ? 'visible' : 'hidden'}"
|
||||||
|
ng-class="reverse ? 'fa-sort-desc' : 'fa-sort-asc'">
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<!-- category -->
|
||||||
|
<div class="pointer dropdown-entry" ng-click="toggleSort('category')">
|
||||||
|
<translate>Category</translate>
|
||||||
|
<span class="spacer-right pull-right"></span>
|
||||||
|
<i class="pull-right fa"
|
||||||
|
ng-style="{'visibility': sortColumn === 'category' && header.sortable != false ? 'visible' : 'hidden'}"
|
||||||
|
ng-class="reverse ? 'fa-sort-desc' : 'fa-sort-asc'">
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<!-- state -->
|
||||||
|
<div class="pointer dropdown-entry" ng-click="toggleSort('state.name')">
|
||||||
|
<translate>State</translate>
|
||||||
|
<span class="spacer-right pull-right"></span>
|
||||||
|
<i class="pull-right fa"
|
||||||
|
ng-style="{'visibility': sortColumn === 'state.name' && header.sortable != false ? 'visible' : 'hidden'}"
|
||||||
|
ng-class="reverse ? 'fa-sort-desc' : 'fa-sort-asc'">
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</span>
|
||||||
|
<!-- search field -->
|
||||||
|
<span class="form-group">
|
||||||
|
<span class="input-group">
|
||||||
|
<span class="input-group-addon"><i class="fa fa-search"></i></span>
|
||||||
|
<input type="text" ng-model="filter.search" class="form-control"
|
||||||
|
placeholder="{{ 'Search' | translate}}" ng-disabled="isDeleteMode">
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<!-- show all selected multiselectoptions -->
|
||||||
|
<span>
|
||||||
|
<span ng-repeat="state in states" class="pointer spacer-left-lg"
|
||||||
|
ng-if="!state.workflowHeader && multiselectFilter.state.indexOf(state.id) > -1"
|
||||||
|
ng-click="operateMultiselectFilter('state', state.id)"
|
||||||
|
ng-class="{'disabled': isDeleteMode}">
|
||||||
|
<span class="nobr">
|
||||||
|
<i class="fa fa-times-circle"></i>
|
||||||
|
{{ state.name | translate }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span ng-repeat="category in categories" class="pointer spacer-left-lg"
|
||||||
|
ng-if="multiselectFilter.category.indexOf(category.id) > -1"
|
||||||
|
ng-click="operateMultiselectFilter('category', category.id)"
|
||||||
|
ng-class="{'disabled': isDeleteMode}">
|
||||||
|
<span class="nobr">
|
||||||
|
<i class="fa fa-times-circle"></i>
|
||||||
|
{{ category.name }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span ng-repeat="tag in tags" class="pointer spacer-left-lg"
|
||||||
|
ng-if="multiselectFilter.tag.indexOf(tag.id) > -1"
|
||||||
|
ng-click="operateMultiselectFilter('tag', tag.id)"
|
||||||
|
ng-class="{'disabled': isDeleteMode}">
|
||||||
|
<span class="nobr">
|
||||||
|
<i class="fa fa-times-circle"></i>
|
||||||
|
{{ tag.name }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- projector -->
|
<!-- main table -->
|
||||||
<td ng-show="!isDeleteMode" os-perms="core.can_manage_projector">
|
<!-- data row -->
|
||||||
|
<div class="row data-row" ng-mouseover="motion.hover=true"
|
||||||
|
ng-mouseleave="motion.hover=false"
|
||||||
|
ng-class="{'projected': motion.isProjected()}"
|
||||||
|
ng-repeat="motion in motionsFiltered = (motions
|
||||||
|
| osFilter: filter.search : getFilterString
|
||||||
|
| SelectMultipleFilter: multiselectFilter.state : getItemId.state
|
||||||
|
| SelectMultipleFilter: multiselectFilter.category : getItemId.category
|
||||||
|
| SelectMultipleFilter: multiselectFilter.tag : getItemId.tag
|
||||||
|
| orderBy: sortColumn : reverse)">
|
||||||
|
|
||||||
|
<!-- select column -->
|
||||||
|
<div ng-show="isDeleteMode" os-perms="motions.can_manage"
|
||||||
|
class="col-xs-1 centered" ng-class="{'deleteColumn' : motion.selected}">
|
||||||
|
<i class="fa text-danger pointer" ng-click="motion.selected=!motion.selected"
|
||||||
|
ng-class="motion.selected ? 'fa-check-square-o' : 'fa-square-o'"></i>
|
||||||
|
</div>
|
||||||
|
<!-- projector column -->
|
||||||
|
<div class="col-xs-1 centered" os-perms="core.can_manage_projector">
|
||||||
<a class="btn btn-default btn-sm"
|
<a class="btn btn-default btn-sm"
|
||||||
ng-class="{ 'btn-primary': motion.isProjected() }"
|
ng-class="{ 'btn-primary': motion.isProjected() }"
|
||||||
ng-click="motion.project()"
|
ng-click="motion.project()"
|
||||||
title="{{ 'Project motion' | translate }}">
|
title="{{ 'Project motion' | translate }}">
|
||||||
<i class="fa fa-video-camera"></i>
|
<i class="fa fa-video-camera"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<!-- delete selection -->
|
|
||||||
<td ng-show="isDeleteMode" os-perms="motions.can_manage" class="deleteColumn">
|
|
||||||
<input type="checkbox" ng-model="motion.selected">
|
|
||||||
|
|
||||||
<!-- agenda item number -->
|
|
||||||
<td ng-if="!motion.quickEdit" class="optional">{{ motion.agenda_item.item_number }}
|
|
||||||
|
|
||||||
<!-- identifier -->
|
|
||||||
<td ng-if="!motion.quickEdit">{{ motion.identifier }}
|
|
||||||
|
|
||||||
<!-- title -->
|
|
||||||
<td ng-if="!motion.quickEdit" ng-mouseover="motion.hover=true" ng-mouseleave="motion.hover=false">
|
|
||||||
<strong><a ui-sref="motions.motion.detail({id: motion.id})">{{ motion.getTitle() }}</a></strong>
|
|
||||||
<span ng-repeat="tag in motion.tags" class="label label-default">
|
|
||||||
{{ tag.name }}
|
|
||||||
</span>
|
|
||||||
<div ng-if="motion.origin">
|
|
||||||
<small>
|
|
||||||
<i class="fa fa-info-circle"></i> <translate>Origin</translate>:
|
|
||||||
{{ motion.origin | limitTo:30 }}{{ motion.origin.length > 30 ? '...' : '' }}
|
|
||||||
</small>
|
|
||||||
</div>
|
</div>
|
||||||
<div ng-if="motion.isAllowed('update')" class="hoverActions" ng-class="{'hiddenDiv': !motion.hover}">
|
<!-- main content column -->
|
||||||
|
<div class="col-xs-6 content">
|
||||||
|
<div class="identifier-col">
|
||||||
|
<div class="nobr" ng-show="motion.identifier">
|
||||||
|
{{ motion.identifier }}:
|
||||||
|
</div>
|
||||||
|
<!-- hover menu -->
|
||||||
|
<div ng-if="motion.isAllowed('update')" ng-class="{'hiddenDiv': !motion.hover}">
|
||||||
<span ng-if="motion.isAllowed('update')">
|
<span ng-if="motion.isAllowed('update')">
|
||||||
<a href="" ng-click="openDialog(motion)" translate>Edit</a>
|
<a href="" ng-click="openDialog(motion)">
|
||||||
</span>
|
<i class="fa fa-pencil"></i></a>
|
||||||
<span ng-if="motion.isAllowed('quickedit')">
|
|
||||||
| <a href="" ng-click="motion.quickEdit=true" translate>QuickEdit</a> |
|
|
||||||
</span>
|
</span>
|
||||||
<span ng-if="motion.isAllowed('delete')">
|
<span ng-if="motion.isAllowed('delete')">
|
||||||
<a href="" class="text-danger"
|
<a href="" ng-bootbox-confirm="{{ 'Are you sure you want to delete this entry?' | translate }}<br>
|
||||||
ng-bootbox-confirm="{{ 'Are you sure you want to delete this entry?' | translate }}<br>
|
|
||||||
<b>{{ motion.getTitle() }}</b>"
|
<b>{{ motion.getTitle() }}</b>"
|
||||||
ng-bootbox-confirm-action="delete(motion)" translate>Delete</a>
|
ng-bootbox-confirm-action="delete(motion)">
|
||||||
|
<i class="fa fa-trash text-danger"></i></a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- submitters -->
|
|
||||||
<td ng-if="!motion.quickEdit" class="optional">
|
|
||||||
<div ng-repeat="submitter in motion.submitters">
|
|
||||||
{{ submitter.get_full_name() }}<br>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="title-col">
|
||||||
<!-- category -->
|
<!-- ID and title -->
|
||||||
<td ng-if="!motion.quickEdit" class="optional">
|
<div>
|
||||||
{{ motion.category.name }}
|
<strong>
|
||||||
|
<a class="title" ui-sref="motions.motion.detail({id: motion.id})">{{ motion.getTitle() }}</a>
|
||||||
<!-- state -->
|
</strong>
|
||||||
<td ng-if="!motion.quickEdit" class="optional">
|
<i class="fa fa-paperclip" ng-if="motion.attachments_id.length > 0"></i>
|
||||||
|
<span style="padding: 5px;" ng-mouseover="motion.stateHover=true" ng-mouseleave="motion.stateHover=false">
|
||||||
<span class="label" ng-class="'label-'+motion.state.css_class">
|
<span class="label" ng-class="'label-'+motion.state.css_class">
|
||||||
{{ motion.state.name | translate }}
|
{{ motion.state.name | translate }}
|
||||||
</span>
|
</span>
|
||||||
|
<span ng-class="{'hiddenDiv': !motion.stateHover}" uib-dropdown>
|
||||||
|
<i class="fa fa-cog pointer" uib-dropdown-toggle id="state-dropdown{{ motion.id }}"></i>
|
||||||
|
<ul uib-dropdown-menu aria-labelledby="state-dropdown{{ motion.id }}">
|
||||||
|
<li ng-repeat="state in motion.state.getNextStates()">
|
||||||
|
<a href ng-click="updateState(motion, state.id)">{{ state.action_word | translate }}</a>
|
||||||
|
</li>
|
||||||
|
<li class="divider" ng-if="motion.state.getNextStates().length"></li>
|
||||||
|
<li>
|
||||||
|
<a href ng-if="motion.isAllowed('reset_state')" ng-click="reset_state(motion)">
|
||||||
|
<i class="fa fa-exclamation-triangle"></i>
|
||||||
|
<translate>Reset state</translate>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<!-- Submitters -->
|
||||||
|
<div>
|
||||||
|
<span>
|
||||||
|
<span class="optional" translate>by</span>
|
||||||
|
<span class="optional" ng-repeat="submitter in motion.submitters | limitTo:3">
|
||||||
|
{{ submitter.get_full_name() }}<span ng-if="!$last">,</span></span><span ng-if="motion.submitters.length > 3">, ...</span>
|
||||||
|
<!-- sorry for merging them together, but otherwise there would be a whitespace because of the new line -->
|
||||||
|
|
||||||
<!-- quickEdit columns -->
|
</span>
|
||||||
<td ng-if="motion.quickEdit && motion.isAllowed('quickedit')" class="quickmode" colspan="6">
|
|
||||||
<h4>{{ motion.getTitle() }} <span class="text-muted">– <translate>QuickEdit</translate></span></h4>
|
|
||||||
<uib-alert ng-show="alert.show" type="{{ alert.type }}" ng-click="alert={}" close="alert={}">
|
|
||||||
{{ alert.msg }}
|
|
||||||
</uib-alert>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-6">
|
|
||||||
<label for="inputIdentifier" translate>Identifier</label>
|
|
||||||
<input type="text" ng-model="motion.identifier" class="form-control input-sm"
|
|
||||||
id="inputIdentifier">
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-6">
|
|
||||||
<label for="selectCategory" translate>Category</label>
|
|
||||||
<select ng-options="category.id as category.name for category in categories"
|
|
||||||
ng-model="motion.category_id" class="form-control" id="selectCategory">
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<!-- additional content column -->
|
||||||
<div class="col-xs-6">
|
<style>
|
||||||
<label for="selectSubmitter" translate>Submitters</label>
|
#motion-table .row .col-xs-4 {
|
||||||
<select multiple chosen
|
width: calc(50% - {{ isDeleteMode ? '100' : '50' }}px);
|
||||||
ng-model="motion.submitters_id"
|
}
|
||||||
ng-options="user.id as user.full_name for user in users"
|
</style>
|
||||||
search-contains="true"
|
<div class="col-xs-4 content">
|
||||||
id="selectSubmitter"
|
<div style="width: 60%;" class="optional">
|
||||||
class="form-control"
|
<small>
|
||||||
data-placeholder-text-multiple="'Select or search a submitter ...' | translate"
|
<div ng-mouseover="motion.categoryHover=true"
|
||||||
no-results-text="'No results match' | translate">
|
ng-mouseleave="motion.categoryHover=false"
|
||||||
</select>
|
ng-show="categories.length > 0">
|
||||||
|
<!-- Category dropdown -->
|
||||||
|
<span uib-dropdown>
|
||||||
|
<span id="dropdown-category{{ motion.id }}" class="pointer"
|
||||||
|
uib-dropdown-toggle uib-tooltip="{{ 'Set a category' | translate }}"
|
||||||
|
tooltip-class="nobr">
|
||||||
|
<span ng-if="motion.category == null" ng-show="motion.hover">
|
||||||
|
<i class="fa fa-sitemap"></i>
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
</span>
|
||||||
|
<span ng-if="motion.category != null">
|
||||||
|
<i class="fa fa-sitemap spacer-right"></i>
|
||||||
|
{{ motion.category.name }}
|
||||||
|
<i class="fa fa-cog fa-lg spacer-left" ng-show="motion.categoryHover"></i>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<ul class="dropdown-menu" aria-labelledby="dropdown-category{{ motion.id }}">
|
||||||
|
<li ng-repeat="category in categories">
|
||||||
|
<div class="dropdown-entry pointer"
|
||||||
|
ng-click="toggle_category(motion, category)">
|
||||||
|
<i class="fa fa-check" ng-if="category.id == motion.category.id"></i>
|
||||||
|
{{ category.name }}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-6">
|
</li>
|
||||||
<label for="selectTags" translate>Tags</label>
|
</ul>
|
||||||
<select multiple chosen
|
</span>
|
||||||
ng-model="motion.tags_id"
|
</div>
|
||||||
ng-options="tag.id as tag.name for tag in tags"
|
<div ng-mouseover="motion.tagHover=true"
|
||||||
search-contains="true"
|
ng-mouseleave="motion.tagHover=false"
|
||||||
id="selectTag"
|
ng-show="tags.length > 0">
|
||||||
class="form-control"
|
<span uib-dropdown>
|
||||||
data-placeholder-text-multiple="'Select or search a tag ...' | translate"
|
<span id="dropdown-tags{{ motion.id }}" class="pointer"
|
||||||
no-results-text="'No results match' | translate">
|
uib-dropdown-toggle uib-tooltip="{{ 'Add a tag' | translate }}"
|
||||||
</select>
|
tooltip-class="nobr">
|
||||||
|
<span ng-if="motion.tags.length == 0" ng-show="motion.hover">
|
||||||
|
<i class="fa fa-tags"></i>
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
</span>
|
||||||
|
<span ng-if="motion.tags.length > 0">
|
||||||
|
<i class="fa fa-tags spacer-right"></i>
|
||||||
|
<span ng-repeat="tag in motion.tags">
|
||||||
|
{{ tag.name }}<span ng-if="!$last">,</span>
|
||||||
|
</span>
|
||||||
|
<i class="fa fa-cog fa-lg spacer-left" ng-show="motion.tagHover"></i>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<ul class="dropdown-menu" aria-labelledby="dropdown-tags{{ motion.id }}">
|
||||||
|
<li ng-repeat="tag in tags">
|
||||||
|
<div class="dropdown-entry pointer" ng-click="toggle_tag(motion, tag)">
|
||||||
|
<i class="fa fa-check" ng-if="has_tag(motion, tag)"></i>
|
||||||
|
{{ tag.name }}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div ng-if="motion.origin">
|
||||||
|
<i class="fa fa-share spacer-right" uib-tooltip="{{ 'Origin' | translate }}"></i>
|
||||||
|
{{ motion.origin | limitTo:25 }}{{ motion.origin.length > 25 ? '...' : '' }}
|
||||||
|
</div>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<div style="width: 10%;" class="pull-right optional">
|
||||||
|
<div class="pull-right" ng-if="config('motions_min_supporters') != 0"
|
||||||
|
uib-tooltip="{{ motion.supporters.length }} {{ 'Supporters' | translate }}
|
||||||
|
{{ (config('motions_min_supporters') - motion.supporters.length) > 0 ? '(' + (config('motions_min_supporters') - motion.supporters.length) + ' ' + ('needed' | translate) + ')': '' }}"
|
||||||
|
tooltip-class="nobr">
|
||||||
|
<span class="badge"
|
||||||
|
ng-class="{'badge-info': motion.supporters.length < config('motions_min_supporters')}">
|
||||||
|
{{ motion.supporters.length }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div style="width: 30%;" class="pull-right">
|
||||||
<div class="col-xs-6">
|
<div class="centered">{{ motion.agenda_item.getItemNumberWithAncestors() }}</div>
|
||||||
<div ng-if="config('motions_min_supporters') > 0">
|
|
||||||
<label for="selectSupporter" translate>Supporters</label>
|
|
||||||
<select multiple chosen
|
|
||||||
ng-model="motion.supporters_id"
|
|
||||||
ng-options="user.id as user.full_name for user in users"
|
|
||||||
search-contains="true"
|
|
||||||
id="selectSupporter"
|
|
||||||
class="form-control"
|
|
||||||
data-placeholder-text-multiple="'Select or search a supporter ...' | translate"
|
|
||||||
no-results-text="'No results match' | translate">
|
|
||||||
<option value=""></option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> <!-- data row -->
|
||||||
<div class="spacer">
|
|
||||||
<button ng-click="cancelQuickEdit(motion)" class="btn btn-default pull-left" translate>
|
</div> <!-- container -->
|
||||||
Cancel
|
</div> <!-- details -->
|
||||||
</button>
|
|
||||||
<button ng-if="motion.isAllowed('update')" ng-click="save(motion)" class="btn btn-primary" translate>
|
|
||||||
Update
|
|
||||||
</button>
|
|
||||||
<a ng-if="motion.isAllowed('update')" ui-sref="motions.motion.detail.update({id: motion.id })"
|
|
||||||
class="pull-right" translate>Edit motion ...</a>
|
|
||||||
</div>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
Loading…
Reference in New Issue
Block a user