New table design for users
This commit is contained in:
parent
f84c381f38
commit
e0f78b16a0
@ -866,23 +866,23 @@ img {
|
||||
}
|
||||
|
||||
/** Motion **/
|
||||
#motion-table .row {
|
||||
border: 1px solid #ddd;
|
||||
border-top: 0px;
|
||||
#motion-os-table .identifier-col {
|
||||
width: 50px;
|
||||
min-height: 1px;
|
||||
}
|
||||
|
||||
#motion-table .data-row:hover {
|
||||
background-color: #f5f5f5;
|
||||
#motion-os-table .identifier-col > div {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#motion-table .data-row > div {
|
||||
padding: 10px 0px 10px 8px;
|
||||
#motion-os-table .title-col {
|
||||
width: calc(100% - 50px);
|
||||
}
|
||||
|
||||
#motion-table .header-row {
|
||||
border-top: 1px solid #ddd;
|
||||
background-color: #f5f5f5;
|
||||
#motion-os-table .title-col, .os-table small {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.motion-toolbar, .speakers-toolbar {
|
||||
background-color: #f5f5f5;
|
||||
border-bottom: 1px solid #ddd;
|
||||
@ -890,6 +890,7 @@ img {
|
||||
height: 54px;
|
||||
margin: -20px -5px 50px -5px;
|
||||
}
|
||||
|
||||
.motion-toolbar:first-child {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
@ -899,77 +900,78 @@ img {
|
||||
padding: 12px 15px 10px 15px;
|
||||
|
||||
}
|
||||
.header-row > div {
|
||||
|
||||
/** OS-Table **/
|
||||
.os-table .row {
|
||||
border: 1px solid #ddd;
|
||||
border-top: 0px;
|
||||
}
|
||||
|
||||
.os-table .data-row:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.os-table .data-row > div {
|
||||
padding: 10px 0px 10px 0px;
|
||||
}
|
||||
|
||||
.os-table .header-row {
|
||||
border-top: 1px solid #ddd;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.os-table .header-row > div {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#motion-table .main-header {
|
||||
.os-table .main-header {
|
||||
width: calc(100% - 50px);
|
||||
float: right;
|
||||
}
|
||||
|
||||
#motion-table .main-header .form-inline {
|
||||
.os-table .main-header .form-inline {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
#motion-table .content > div {
|
||||
.os-table .content > div { /* horizontal blocks */
|
||||
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 {
|
||||
.os-table .content > div > div { /* vertival blocks */
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
#motion-table .content > div > div:last-child {
|
||||
.os-table .content > div > div:last-child {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
#motion-table .row .centered {
|
||||
.os-table .row .centered {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#motion-table .row .col-xs-1 {
|
||||
.os-table .row .col-xs-1 {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
#motion-table .row .col-xs-4 {
|
||||
.os-table .projector {
|
||||
width: 70px !important;
|
||||
}
|
||||
|
||||
.os-table .row .col-xs-4 {
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
#motion-table .dropdown {
|
||||
.os-table .dropdown {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#motion-table .header-row .dropdown > span, #motion-table .sort-spacer {
|
||||
.os-table .header-row .dropdown > span, .os-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 {
|
||||
font-size: 110%;
|
||||
.os-table .title {
|
||||
font-size: 115%;
|
||||
margin-right: 10px;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
|
@ -699,32 +699,6 @@ angular.module('OpenSlidesApp.core', [
|
||||
}
|
||||
])
|
||||
|
||||
/*
|
||||
* This filter filters all items in an array. If the filterArray is empty, the
|
||||
* array is passed. The filterArray contains numbers of the multiselect, e. g. [1, 3, 4].
|
||||
* Then, all items in the 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"
|
||||
.filter('trusted', [
|
||||
'$sce',
|
||||
|
@ -342,6 +342,91 @@ angular.module('OpenSlidesApp.core.site', [
|
||||
}
|
||||
])
|
||||
|
||||
.factory('Multiselect', [
|
||||
function () {
|
||||
var instance = function () {
|
||||
var areFiltersSet = function () {
|
||||
var areFiltersSet = false;
|
||||
_.forEach(self.filters, function (filterList, filter) {
|
||||
if (filterList.length > 0) {
|
||||
areFiltersSet = true;
|
||||
}
|
||||
});
|
||||
return areFiltersSet;
|
||||
};
|
||||
var resetFilters = function () {
|
||||
_.forEach(self.filters, function (filterList, filter) {
|
||||
self.filters[filter] = [];
|
||||
});
|
||||
};
|
||||
var getFilterString = function (obj) {
|
||||
var newList = [];
|
||||
_.forEach(self.propertyList, function (property) {
|
||||
newList.push(obj[property]);
|
||||
});
|
||||
_.forEach(self.propertyFunctionList, function (fn) {
|
||||
newList.push(fn(obj));
|
||||
});
|
||||
_.forEach(self.propertyDict, function (idFunction, property) {
|
||||
newList.push(_.map(obj[property], idFunction).join(' '));
|
||||
});
|
||||
return newList.join(' ');
|
||||
};
|
||||
var operate = function (filter, id) {
|
||||
if (_.indexOf(self.filters[filter], id) > -1) {
|
||||
// remove id
|
||||
self.filters[filter] = _.filter(self.filters[filter], function (_id) {
|
||||
return _id != id;
|
||||
});
|
||||
} else {
|
||||
// add id
|
||||
self.filters[filter].push(id);
|
||||
}
|
||||
};
|
||||
|
||||
var self = {
|
||||
filters: {},
|
||||
areFiltersSet: areFiltersSet,
|
||||
resetFilters: resetFilters,
|
||||
operate: operate,
|
||||
getFilterString: getFilterString,
|
||||
};
|
||||
resetFilters(); //Initiate filters
|
||||
return self;
|
||||
};
|
||||
|
||||
return {
|
||||
instance: instance
|
||||
};
|
||||
}
|
||||
])
|
||||
|
||||
/*
|
||||
* This filter filters all items in an array. If the filterArray is empty, the
|
||||
* array is passed. The filterArray contains numbers of the multiselect, e. g. [1, 3, 4].
|
||||
* Then, all items in the 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('MultiselectFilter', [
|
||||
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;
|
||||
});
|
||||
};
|
||||
}
|
||||
])
|
||||
|
||||
// Load the django url patterns
|
||||
.run([
|
||||
'runtimeStates',
|
||||
|
@ -653,6 +653,33 @@ angular.module('OpenSlidesApp.motions.site', [
|
||||
}
|
||||
])
|
||||
|
||||
.factory('MotionCsvExport', [
|
||||
function () {
|
||||
return function (element, motions) {
|
||||
var csvRows = [
|
||||
['identifier', 'title', 'text', 'reason', 'submitter', 'category', 'origin'],
|
||||
];
|
||||
_.forEach(motions, function (motion) {
|
||||
var row = [];
|
||||
row.push('"' + motion.identifier + '"');
|
||||
row.push('"' + motion.getTitle() + '"');
|
||||
row.push('"' + motion.getText() + '"');
|
||||
row.push('"' + motion.getReason() + '"');
|
||||
row.push('"' + motion.submitters[0].get_full_name() + '"');
|
||||
var category = motion.category ? motion.category.name : '';
|
||||
row.push('"' + category + '"');
|
||||
row.push('"' + motion.origin + '"');
|
||||
csvRows.push(row);
|
||||
});
|
||||
|
||||
var csvString = csvRows.join("%0A");
|
||||
element.href = 'data:text/csv;charset=utf-8,' + csvString;
|
||||
element.download = 'motions-export.csv';
|
||||
element.target = '_blank';
|
||||
};
|
||||
}
|
||||
])
|
||||
|
||||
// Cache for MotionPollDetailCtrl so that users choices are keeped during user actions (e. g. save poll form).
|
||||
.value('MotionPollDetailCtrlCache', {})
|
||||
|
||||
@ -723,9 +750,11 @@ angular.module('OpenSlidesApp.motions.site', [
|
||||
'HTMLValidizer',
|
||||
'Projector',
|
||||
'ProjectionDefault',
|
||||
'MotionCsvExport',
|
||||
'Multiselect',
|
||||
function($scope, $state, $http, ngDialog, MotionForm, Motion, Category, Tag, Workflow, User, Agenda, MotionBlock,
|
||||
MotionDocxExport, MotionContentProvider, MotionCatalogContentProvider, PdfMakeConverter, PdfMakeDocumentProvider,
|
||||
gettextCatalog, HTMLValidizer, Projector, ProjectionDefault) {
|
||||
gettextCatalog, HTMLValidizer, Projector, ProjectionDefault, MotionCsvExport, Multiselect) {
|
||||
Motion.bindAll({}, $scope, 'motions');
|
||||
Category.bindAll({}, $scope, 'categories');
|
||||
MotionBlock.bindAll({}, $scope, 'motionBlocks');
|
||||
@ -743,35 +772,65 @@ angular.module('OpenSlidesApp.motions.site', [
|
||||
});
|
||||
$scope.alert = {};
|
||||
|
||||
// setup table sorting
|
||||
$scope.sortColumn = 'identifier';
|
||||
$scope.filterPresent = '';
|
||||
$scope.reverse = false;
|
||||
|
||||
$scope.multiselectFilter = {
|
||||
$scope.multiselect = Multiselect.instance();
|
||||
$scope.multiselect.filters = {
|
||||
state: [],
|
||||
category: [],
|
||||
motionBlock: [],
|
||||
tag: []
|
||||
};
|
||||
$scope.multiselect.propertyList = ['identifier', 'origin'];
|
||||
$scope.multiselect.propertyFunctionList = [
|
||||
function (motion) {return motion.getTitle();},
|
||||
function (motion) {return motion.getText();},
|
||||
function (motion) {return motion.getReason();},
|
||||
function (motion) {return motion.category ? motion.category.name : '';},
|
||||
function (motion) {return motion.motionBlock ? motion.motionBlock.name : '';},
|
||||
];
|
||||
$scope.multiselect.PropertyDict = {
|
||||
'submitters' : function (submitter) {
|
||||
return submitter.get_short_name();
|
||||
},
|
||||
'supporters' : function (submitter) {
|
||||
return supporter.get_short_name();
|
||||
},
|
||||
'tags' : function (tag) {
|
||||
return tag.name;
|
||||
},
|
||||
};
|
||||
$scope.getItemId = {
|
||||
state: function (motion) {return motion.state_id;},
|
||||
category: function (motion) {return motion.category_id;},
|
||||
motionBlock: function (motion) {return motion.motion_block_id;},
|
||||
tag: function (motion) {return motion.tags_id;}
|
||||
};
|
||||
// setup table sorting
|
||||
$scope.sortOptions = [
|
||||
{name: 'identifier',
|
||||
display_name: 'Identifier'},
|
||||
{name: 'getTitle()',
|
||||
display_name: 'Title'},
|
||||
{name: 'submitters',
|
||||
display_name: 'Submitters'},
|
||||
{name: 'category.name',
|
||||
display_name: 'Category'},
|
||||
{name: 'motionBlock.title',
|
||||
display_name: 'Motion block'},
|
||||
{name: 'state.name',
|
||||
display_name: 'State'},
|
||||
{name: 'log_messages[log_messages.length-1].time',
|
||||
display_name: 'Creation date'},
|
||||
{name: 'log_messages[0].time',
|
||||
display_name: 'Last modified'},
|
||||
];
|
||||
$scope.sortColumn = 'identifier';
|
||||
$scope.filterPresent = '';
|
||||
$scope.reverse = false;
|
||||
|
||||
// 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);
|
||||
}
|
||||
$scope.multiselect.operate(filter, id);
|
||||
}
|
||||
};
|
||||
// function to sort by clicked column
|
||||
@ -781,61 +840,15 @@ angular.module('OpenSlidesApp.motions.site', [
|
||||
}
|
||||
$scope.sortColumn = column;
|
||||
};
|
||||
// define custom search filter string
|
||||
$scope.getFilterString = function (motion) {
|
||||
var category = '';
|
||||
if (motion.category) {
|
||||
category = motion.category.name;
|
||||
}
|
||||
var motionBlock = '';
|
||||
if (motion.motionBlock) {
|
||||
motionBlock = motion.motionBlock.title;
|
||||
}
|
||||
return [
|
||||
motion.identifier,
|
||||
motion.getTitle(),
|
||||
motion.getText(),
|
||||
motion.getReason(),
|
||||
motion.origin,
|
||||
_.map(
|
||||
motion.submitters,
|
||||
function (submitter) {
|
||||
return submitter.get_short_name();
|
||||
}
|
||||
).join(" "),
|
||||
_.map(
|
||||
motion.supporters,
|
||||
function (supporter) {
|
||||
return supporter.get_short_name();
|
||||
}
|
||||
).join(" "),
|
||||
_.map(
|
||||
motion.tags,
|
||||
function (tag) {
|
||||
return tag.name;
|
||||
}
|
||||
).join(" "),
|
||||
category,
|
||||
motionBlock
|
||||
].join(" ");
|
||||
};
|
||||
// for reset-button
|
||||
$scope.reset_filters = function () {
|
||||
$scope.multiselectFilter = {
|
||||
state: [],
|
||||
category: [],
|
||||
motionBlock: [],
|
||||
tag: []
|
||||
};
|
||||
$scope.resetFilters = function () {
|
||||
$scope.multiselect.resetFilters();
|
||||
if ($scope.filter) {
|
||||
$scope.filter.search = '';
|
||||
}
|
||||
};
|
||||
$scope.are_filters_set = function () {
|
||||
return $scope.multiselectFilter.state.length > 0 ||
|
||||
$scope.multiselectFilter.category.length > 0 ||
|
||||
$scope.multiselectFilter.motionBlock.length > 0 ||
|
||||
$scope.multiselectFilter.tag.length > 0 ||
|
||||
$scope.areFiltersSet = function () {
|
||||
return $scope.multiselect.areFiltersSet() ||
|
||||
($scope.filter ? $scope.filter.search : false);
|
||||
};
|
||||
|
||||
@ -910,7 +923,7 @@ angular.module('OpenSlidesApp.motions.site', [
|
||||
};
|
||||
|
||||
// Export as a pdf file
|
||||
$scope.pdf_export = function() {
|
||||
$scope.pdfExport = function() {
|
||||
var filename = gettextCatalog.getString("Motions") + ".pdf";
|
||||
var image_sources = [];
|
||||
|
||||
@ -940,31 +953,12 @@ angular.module('OpenSlidesApp.motions.site', [
|
||||
};
|
||||
|
||||
// Export as a csv file
|
||||
$scope.csv_export = function () {
|
||||
$scope.csvExport = function () {
|
||||
var element = document.getElementById('downloadLinkCSV');
|
||||
var csvRows = [
|
||||
['identifier', 'title', 'text', 'reason', 'submitter', 'category', 'origin'],
|
||||
];
|
||||
angular.forEach($scope.motionsFiltered, function (motion) {
|
||||
var row = [];
|
||||
row.push('"' + motion.identifier + '"');
|
||||
row.push('"' + motion.getTitle() + '"');
|
||||
row.push('"' + motion.getText() + '"');
|
||||
row.push('"' + motion.getReason() + '"');
|
||||
row.push('"' + motion.submitters[0].get_full_name() + '"');
|
||||
var category = motion.category ? motion.category.name : '';
|
||||
row.push('"' + category + '"');
|
||||
row.push('"' + motion.origin + '"');
|
||||
csvRows.push(row);
|
||||
});
|
||||
|
||||
var csvString = csvRows.join("%0A");
|
||||
element.href = 'data:text/csv;charset=utf-8,' + csvString;
|
||||
element.download = 'motions-export.csv';
|
||||
element.target = '_blank';
|
||||
MotionCsvExport(element, $scope.motionsFiltered);
|
||||
};
|
||||
// Export as docx file
|
||||
$scope.docx_export = function () {
|
||||
$scope.docxExport = function () {
|
||||
MotionDocxExport.export($scope.motionsFiltered, $scope.categories);
|
||||
};
|
||||
|
||||
|
@ -51,7 +51,7 @@
|
||||
<ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownExport">
|
||||
<!-- PDF export -->
|
||||
<li>
|
||||
<a href="" ng-click="pdf_export()">
|
||||
<a href="" ng-click="pdfExport()">
|
||||
<i class="fa fa-file-pdf-o fa-lg"></i>
|
||||
PDF
|
||||
</a>
|
||||
@ -59,14 +59,14 @@
|
||||
<!-- CSV export -->
|
||||
<li>
|
||||
<a href="" id="downloadLinkCSV"
|
||||
ng-click="csv_export()">
|
||||
ng-click="csvExport()">
|
||||
<i class="fa fa-file-text-o fa-lg"></i>
|
||||
CSV
|
||||
</a>
|
||||
</li>
|
||||
<!-- DOCX export -->
|
||||
<li>
|
||||
<a href="" ng-click="docx_export()">
|
||||
<a href="" ng-click="docxExport()">
|
||||
<i class="fa fa-file-word-o fa-lg"></i>
|
||||
DOCX
|
||||
</a>
|
||||
@ -95,7 +95,7 @@
|
||||
{{(motions|filter:{selected:true}).length}} {{ "selected" | translate }}</span>
|
||||
</div>
|
||||
|
||||
<div id="motion-table" class="container-fluid">
|
||||
<div id="motion-os-table" class="os-table container-fluid">
|
||||
<div class="row header-row">
|
||||
<div class="col-xs-1 centered" ng-show="isDeleteMode">
|
||||
<i class="fa text-danger pointer" ng-class=" selectedAll ? 'fa-check-square-o' : 'fa-square-o'"
|
||||
@ -103,8 +103,8 @@
|
||||
</div>
|
||||
<div class="col-xs-11 main-header">
|
||||
<span class="form-inline text-right pull-right">
|
||||
<span class="sort-spacer pointer" ng-click="reset_filters()"
|
||||
ng-if="are_filters_set()" ng-disabled="isDeleteMode"
|
||||
<span class="sort-spacer pointer" ng-click="resetFilters()"
|
||||
ng-if="areFiltersSet()" ng-disabled="isDeleteMode"
|
||||
ng-class="{'disabled': isDeleteMode}">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
<translate>Filter</translate>
|
||||
@ -112,19 +112,19 @@
|
||||
<!-- State filter -->
|
||||
<span class="dropdown" uib-dropdown>
|
||||
<span class="pointer" id="dropdownState" uib-dropdown-toggle
|
||||
ng-class="{'bold': multiselectFilter.state.length > 0, 'disabled': isDeleteMode}"
|
||||
ng-class="{'bold': multiselect.filters.state.length > 0, 'disabled': isDeleteMode}"
|
||||
ng-disabled="isDeleteMode">
|
||||
<translate>State</translate>
|
||||
<span class="caret"></span>
|
||||
</span>
|
||||
<ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownState">
|
||||
<ul class="dropdown-menu dropdown-menu-right dropdown-entries" aria-labelledby="dropdownState">
|
||||
<li ng-repeat="state in states" ng-class="state.workflowHeader ? 'dropdown-header' : ''">
|
||||
<div class="dropdown-entry pointer" ng-if="state.workflowHeader">
|
||||
<div ng-if="state.workflowHeader">
|
||||
{{ state.name | translate }}
|
||||
</div>
|
||||
<div class="dropdown-entry pointer" ng-if="!state.workflowHeader"
|
||||
<div ng-if="!state.workflowHeader"
|
||||
ng-click="operateMultiselectFilter('state', state.id)">
|
||||
<i class="fa fa-check" ng-if="multiselectFilter.state.indexOf(state.id) > -1"></i>
|
||||
<i class="fa fa-check" ng-if="multiselect.filters.state.indexOf(state.id) > -1"></i>
|
||||
{{ state.name | translate }}
|
||||
</div>
|
||||
</li>
|
||||
@ -133,17 +133,16 @@
|
||||
<!-- Category filter -->
|
||||
<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-class="{'bold': multiselect.filters.category.length > 0, 'disabled': isDeleteMode}"
|
||||
ng-disabled="isDeleteMode">
|
||||
<translate>Category</translate>
|
||||
<span class="caret"></span>
|
||||
</span>
|
||||
<ul class="dropdown-menu dropdown-menu-right"
|
||||
<ul class="dropdown-menu dropdown-menu-right dropdown-entries"
|
||||
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>
|
||||
<div ng-click="operateMultiselectFilter('category', category.id)">
|
||||
<i class="fa fa-check" ng-if="multiselect.filters.category.indexOf(category.id) > -1"></i>
|
||||
{{ category.name }}
|
||||
</div>
|
||||
</li>
|
||||
@ -152,17 +151,16 @@
|
||||
<!-- Motion block filter -->
|
||||
<span class="dropdown" uib-dropdown ng-if="motionBlocks.length > 0">
|
||||
<span class="pointer" id="dropdownBlock" uib-dropdown-toggle
|
||||
ng-class="{'bold': multiselectFilter.motionBlock.length > 0, 'disabled': isDeleteMode}"
|
||||
ng-class="{'bold': multiselect.filters.motionBlock.length > 0, 'disabled': isDeleteMode}"
|
||||
ng-disabled="isDeleteMode">
|
||||
<translate>Motion block</translate>
|
||||
<span class="caret"></span>
|
||||
</span>
|
||||
<ul class="dropdown-menu dropdown-menu-right"
|
||||
<ul class="dropdown-menu dropdown-menu-right dropdown-entries"
|
||||
aria-labelledby="dropdownBlock">
|
||||
<li ng-repeat="block in motionBlocks">
|
||||
<div class="dropdown-entry pointer"
|
||||
ng-click="operateMultiselectFilter('motionBlock', block.id)">
|
||||
<i class="fa fa-check" ng-if="multiselectFilter.motionBlock.indexOf(block.id) > -1"></i>
|
||||
<div ng-click="operateMultiselectFilter('motionBlock', block.id)">
|
||||
<i class="fa fa-check" ng-if="multiselect.filters.motionBlock.indexOf(block.id) > -1"></i>
|
||||
{{ block.title }}
|
||||
</div>
|
||||
</li>
|
||||
@ -171,17 +169,16 @@
|
||||
<!-- Tag filter -->
|
||||
<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-class="{'bold': multiselect.filters.tag.length > 0, 'disabled': isDeleteMode}"
|
||||
ng-disabled="isDeleteMode">
|
||||
<translate>Tag</translate>
|
||||
<span class="caret"></span>
|
||||
</span>
|
||||
<ul class="dropdown-menu dropdown-menu-right"
|
||||
<ul class="dropdown-menu dropdown-menu-right dropdown-entries"
|
||||
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>
|
||||
<div ng-click="operateMultiselectFilter('tag', tag.id)">
|
||||
<i class="fa fa-check" ng-if="multiselect.filters.tag.indexOf(tag.id) > -1"></i>
|
||||
{{ tag.name }}
|
||||
</div>
|
||||
</li>
|
||||
@ -195,106 +192,29 @@
|
||||
<translate>Sort</translate>
|
||||
<span class="caret"></span>
|
||||
</span>
|
||||
<ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownSort">
|
||||
<li>
|
||||
<ul class="dropdown-menu dropdown-menu-right dropdown-entries" aria-labelledby="dropdownSort">
|
||||
<!-- item -->
|
||||
<div class="pointer dropdown-entry" ng-click="toggleSort('agenda_item.getItemNumberWithAncestors()')">
|
||||
<li>
|
||||
<div 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-style="{'visibility': sortColumn === 'agenda_item.getItemNumberWithAncestors()' ? 'visible' : 'hidden'}"
|
||||
ng-class="reverse ? 'fa-sort-desc' : 'fa-sort-asc'">
|
||||
</i>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<!-- indentifier -->
|
||||
<div class="pointer dropdown-entry" ng-click="toggleSort('identifier')">
|
||||
<translate>Identifier</translate>
|
||||
<!-- all other sortOptions -->
|
||||
<li ng-repeat="option in sortOptions">
|
||||
<div ng-click="toggleSort(option.name)">
|
||||
{{ option.display_name | translate }}
|
||||
<span class="spacer-right pull-right"></span>
|
||||
<i class="pull-right fa"
|
||||
ng-style="{'visibility': sortColumn === 'identifier' && header.sortable != false ? 'visible' : 'hidden'}"
|
||||
ng-style="{'visibility': sortColumn === option.name ? '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.name')">
|
||||
<translate>Category</translate>
|
||||
<span class="spacer-right pull-right"></span>
|
||||
<i class="pull-right fa"
|
||||
ng-style="{'visibility': sortColumn === 'category.name' && header.sortable != false ? 'visible' : 'hidden'}"
|
||||
ng-class="reverse ? 'fa-sort-desc' : 'fa-sort-asc'">
|
||||
</i>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<!-- motion block -->
|
||||
<div class="pointer dropdown-entry" ng-click="toggleSort('motionBlock.title')">
|
||||
<translate>Motion block</translate>
|
||||
<span class="spacer-right pull-right"></span>
|
||||
<i class="pull-right fa"
|
||||
ng-style="{'visibility': sortColumn === 'motionBlock.title' && 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>
|
||||
<li>
|
||||
<!-- creation date -->
|
||||
<div class="pointer dropdown-entry" ng-click="toggleSort('log_messages[log_messages.length-1].time')">
|
||||
<translate>Creation date</translate>
|
||||
<span class="spacer-right pull-right"></span>
|
||||
<i class="pull-right fa"
|
||||
ng-style="{'visibility': sortColumn === 'log_messages[log_messages.length-1].time' && header.sortable != false ? 'visible' : 'hidden'}"
|
||||
ng-class="reverse ? 'fa-sort-desc' : 'fa-sort-asc'">
|
||||
</i>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<!-- last modified -->
|
||||
<div class="pointer dropdown-entry" ng-click="toggleSort('log_messages[0].time')">
|
||||
<translate>Last modified</translate>
|
||||
<span class="spacer-right pull-right"></span>
|
||||
<i class="pull-right fa"
|
||||
ng-style="{'visibility': sortColumn === 'log_messages[0].time' && header.sortable != false ? 'visible' : 'hidden'}"
|
||||
ng-class="reverse ? 'fa-sort-asc' : 'fa-sort-desc'">
|
||||
</i>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
<!-- search field -->
|
||||
@ -309,7 +229,7 @@
|
||||
<!-- 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-if="!state.workflowHeader && multiselect.filters.state.indexOf(state.id) > -1"
|
||||
ng-click="operateMultiselectFilter('state', state.id)"
|
||||
ng-class="{'disabled': isDeleteMode}">
|
||||
<span class="nobr">
|
||||
@ -318,7 +238,7 @@
|
||||
</span>
|
||||
</span>
|
||||
<span ng-repeat="category in categories" class="pointer spacer-left-lg"
|
||||
ng-if="multiselectFilter.category.indexOf(category.id) > -1"
|
||||
ng-if="multiselect.filters.category.indexOf(category.id) > -1"
|
||||
ng-click="operateMultiselectFilter('category', category.id)"
|
||||
ng-class="{'disabled': isDeleteMode}">
|
||||
<span class="nobr">
|
||||
@ -327,7 +247,7 @@
|
||||
</span>
|
||||
</span>
|
||||
<span ng-repeat="motionBlock in motionBlocks" class="pointer spacer-left-lg"
|
||||
ng-if="multiselectFilter.motionBlock.indexOf(motionBlock.id) > -1"
|
||||
ng-if="multiselect.filters.motionBlock.indexOf(motionBlock.id) > -1"
|
||||
ng-click="operateMultiselectFilter('motionBlock', motionBlock.id)"
|
||||
ng-class="{'disabled': isDeleteMode}">
|
||||
<span class="nobr">
|
||||
@ -336,7 +256,7 @@
|
||||
</span>
|
||||
</span>
|
||||
<span ng-repeat="tag in tags" class="pointer spacer-left-lg"
|
||||
ng-if="multiselectFilter.tag.indexOf(tag.id) > -1"
|
||||
ng-if="multiselect.flters.tag.indexOf(tag.id) > -1"
|
||||
ng-click="operateMultiselectFilter('tag', tag.id)"
|
||||
ng-class="{'disabled': isDeleteMode}">
|
||||
<span class="nobr">
|
||||
@ -349,27 +269,25 @@
|
||||
</div>
|
||||
|
||||
<!-- main table -->
|
||||
<!-- data row -->
|
||||
<div class="row data-row" ng-mouseover="motion.hover=true"
|
||||
ng-mouseleave="motion.hover=false"
|
||||
ng-class="{'projected': motion.isProjected().length}"
|
||||
ng-repeat="motion in motionsFiltered = (motions
|
||||
| osFilter: filter.search : getFilterString
|
||||
| SelectMultipleFilter: multiselectFilter.state : getItemId.state
|
||||
| SelectMultipleFilter: multiselectFilter.category : getItemId.category
|
||||
| SelectMultipleFilter: multiselectFilter.motionBlock : getItemId.motionBlock
|
||||
| SelectMultipleFilter: multiselectFilter.tag : getItemId.tag
|
||||
| osFilter: filter.search : multiselect.getFilterString
|
||||
| MultiselectFilter: multiselect.filters.state : getItemId.state
|
||||
| MultiselectFilter: multiselect.filters.category : getItemId.category
|
||||
| MultiselectFilter: multiselect.filters.motionBlock : getItemId.motionBlock
|
||||
| MultiselectFilter: multiselect.filters.tag : getItemId.tag
|
||||
| toArray
|
||||
| orderBy: sortColumn : reverse)">
|
||||
|
||||
<!-- select column -->
|
||||
<div ng-show="isDeleteMode" os-perms="motions.can_manage"
|
||||
class="col-xs-1 centered" ng-class="{'deleteColumn' : motion.selected}">
|
||||
<div ng-show="isDeleteMode" os-perms="motions.can_manage" class="col-xs-1 centered">
|
||||
<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">
|
||||
<div class="col-xs-1 centered projector" os-perms="core.can_manage_projector">
|
||||
<projector-button model="motion", default-projector-id="defaultProjectorId">
|
||||
</projector-button>
|
||||
</div>
|
||||
@ -437,7 +355,7 @@
|
||||
<!-- additional content column -->
|
||||
<style>
|
||||
#motion-table .row .col-xs-4 {
|
||||
width: calc(50% - {{ isDeleteMode ? '100' : '50' }}px);
|
||||
width: calc(50% - {{ isDeleteMode ? '120' : '70' }}px);
|
||||
}
|
||||
</style>
|
||||
<div class="col-xs-4 content">
|
||||
@ -461,10 +379,9 @@
|
||||
<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 }}">
|
||||
<ul class="dropdown-menu dropdown-entries" aria-labelledby="dropdown-category{{ motion.id }}">
|
||||
<li ng-repeat="category in categories">
|
||||
<div class="dropdown-entry pointer"
|
||||
ng-click="toggle_category(motion, category)">
|
||||
<div ng-click="toggle_category(motion, category)">
|
||||
<i class="fa fa-check" ng-if="category.id == motion.category.id"></i>
|
||||
{{ category.name }}
|
||||
</div>
|
||||
@ -496,10 +413,9 @@
|
||||
<i class="fa fa-cog fa-lg spacer-left" ng-show="motion.motionBlockHover"></i>
|
||||
</span>
|
||||
</span>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdown-motionBlock{{ motion.id }}">
|
||||
<ul class="dropdown-menu dropdown-entries" aria-labelledby="dropdown-motionBlock{{ motion.id }}">
|
||||
<li ng-repeat="motionBlock in motionBlocks">
|
||||
<div class="dropdown-entry pointer"
|
||||
ng-click="toggle_motionBlock(motion, motionBlock)">
|
||||
<div ng-click="toggle_motionBlock(motion, motionBlock)">
|
||||
<i class="fa fa-check" ng-if="motionBlock.id == motion.motionBlock.id"></i>
|
||||
{{ motionBlock.title }}
|
||||
</div>
|
||||
@ -533,9 +449,9 @@
|
||||
<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 }}">
|
||||
<ul class="dropdown-menu dropdown-entries" aria-labelledby="dropdown-tags{{ motion.id }}">
|
||||
<li ng-repeat="tag in tags">
|
||||
<div class="dropdown-entry pointer" ng-click="toggle_tag(motion, tag)">
|
||||
<div ng-click="toggle_tag(motion, tag)">
|
||||
<i class="fa fa-check" ng-if="has_tag(motion, tag)"></i>
|
||||
{{ tag.name }}
|
||||
</div>
|
||||
|
@ -11,12 +11,12 @@ def get_config_variables():
|
||||
# Sorting
|
||||
yield ConfigVariable(
|
||||
name='users_sort_by',
|
||||
default_value='firstName',
|
||||
default_value='first_name',
|
||||
input_type='choice',
|
||||
label='Sort name of participants by',
|
||||
choices=(
|
||||
{'value': 'firstName', 'display_name': 'First name'},
|
||||
{'value': 'lastName', 'display_name': 'Last name'}),
|
||||
{'value': 'first_name', 'display_name': 'First name'},
|
||||
{'value': 'last_name', 'display_name': 'Last name'}),
|
||||
weight=510,
|
||||
group='Participants',
|
||||
subgroup='General')
|
||||
|
@ -101,7 +101,7 @@ angular.module('OpenSlidesApp.users', [])
|
||||
firstName = _.trim(this.first_name),
|
||||
lastName = _.trim(this.last_name),
|
||||
name = '';
|
||||
if (Config.get('users_sort_by') && Config.get('users_sort_by').value == 'lastName') {
|
||||
if (Config.get('users_sort_by') && Config.get('users_sort_by').value == 'last_name') {
|
||||
if (lastName && firstName) {
|
||||
name += [lastName, firstName].join(', ');
|
||||
} else {
|
||||
|
@ -416,6 +416,35 @@ angular.module('OpenSlidesApp.users.site', [
|
||||
}
|
||||
])
|
||||
|
||||
.factory('UserCsvExport', [
|
||||
function () {
|
||||
return function (element, users) {
|
||||
var csvRows = [
|
||||
['title', 'first_name', 'last_name', 'structure_level', 'number', 'groups', 'comment', 'is_active', 'is_present', 'is_committee'],
|
||||
];
|
||||
_.forEach(users, function (user) {
|
||||
var row = [];
|
||||
row.push('"' + user.title + '"');
|
||||
row.push('"' + user.first_name + '"');
|
||||
row.push('"' + user.last_name + '"');
|
||||
row.push('"' + user.structure_level + '"');
|
||||
row.push('"' + user.number + '"');
|
||||
row.push('"' + user.groups_id.join(',') + '"');
|
||||
row.push('"' + user.comment + '"');
|
||||
row.push(user.is_active ? '1' : '0');
|
||||
row.push(user.is_present ? '1' : '0');
|
||||
row.push(user.is_committee ? '1' : '0');
|
||||
csvRows.push(row);
|
||||
});
|
||||
|
||||
var csvString = csvRows.join("%0A");
|
||||
element.href = 'data:text/csv;charset=utf-8,' + csvString;
|
||||
element.download = 'users-export.csv';
|
||||
element.target = '_blank';
|
||||
};
|
||||
}
|
||||
])
|
||||
|
||||
.controller('UserListCtrl', [
|
||||
'$scope',
|
||||
'$state',
|
||||
@ -432,8 +461,11 @@ angular.module('OpenSlidesApp.users.site', [
|
||||
'UserAccessDataListContentProvider',
|
||||
'PdfMakeDocumentProvider',
|
||||
'gettextCatalog',
|
||||
'UserCsvExport',
|
||||
'Multiselect',
|
||||
function($scope, $state, $http, ngDialog, UserForm, User, Group, PasswordGenerator, Projector, ProjectionDefault,
|
||||
UserListContentProvider, Config, UserAccessDataListContentProvider, PdfMakeDocumentProvider, gettextCatalog) {
|
||||
UserListContentProvider, Config, UserAccessDataListContentProvider, PdfMakeDocumentProvider, gettextCatalog,
|
||||
UserCsvExport, Multiselect) {
|
||||
User.bindAll({}, $scope, 'users');
|
||||
Group.bindAll({where: {id: {'>': 1}}}, $scope, 'groups');
|
||||
$scope.$watch(function () {
|
||||
@ -445,10 +477,40 @@ angular.module('OpenSlidesApp.users.site', [
|
||||
}
|
||||
});
|
||||
$scope.alert = {};
|
||||
$scope.groupFilter = undefined;
|
||||
|
||||
$scope.multiselect = Multiselect.instance();
|
||||
$scope.multiselect.filters = {
|
||||
group: [],
|
||||
};
|
||||
$scope.multiselect.propertyList = ['first_name', 'last_name', 'title', 'number', 'comment', 'structure_level'];
|
||||
$scope.multiselect.PropertyDict = {
|
||||
'groups_id' : function (group_id) {
|
||||
return Group.get(group_id).name;
|
||||
},
|
||||
};
|
||||
$scope.getItemId = {
|
||||
group: function (user) {return user.groups_id;},
|
||||
};
|
||||
// setup table sorting
|
||||
$scope.sortColumn = 'first_name'; //TODO: sort by first OR last name
|
||||
$scope.sortOptions = [
|
||||
{name: 'first_name',
|
||||
display_name: 'First name'},
|
||||
{name: 'last_name',
|
||||
display_name: 'Last name'},
|
||||
{name: 'is_present',
|
||||
display_name: 'Present'},
|
||||
{name: 'is_active',
|
||||
display_name: 'Active'},
|
||||
{name: 'is_committee',
|
||||
display_name: 'Committee'},
|
||||
{name: 'number',
|
||||
display_name: 'Number'},
|
||||
{name: 'structure_level',
|
||||
display_name: 'Structure level'},
|
||||
{name: 'comment',
|
||||
display_name: 'Comment'},
|
||||
];
|
||||
$scope.sortColumn = $scope.config('users_sort_by');
|
||||
$scope.filterPresent = '';
|
||||
$scope.reverse = false;
|
||||
// function to sort by clicked column
|
||||
@ -458,6 +520,29 @@ angular.module('OpenSlidesApp.users.site', [
|
||||
}
|
||||
$scope.sortColumn = column;
|
||||
};
|
||||
// function to operate the multiselectFilter
|
||||
$scope.operateMultiselectFilter = function (filter, id) {
|
||||
if (!$scope.isSelectMode) {
|
||||
$scope.multiselect.operate(filter, id);
|
||||
}
|
||||
};
|
||||
// for reset-button
|
||||
$scope.resetFilters = function () {
|
||||
$scope.multiselect.resetFilters();
|
||||
if ($scope.filter) {
|
||||
$scope.filter.search = '';
|
||||
}
|
||||
$scope.isPresentFilter = undefined;
|
||||
$scope.isActiveFilter = undefined;
|
||||
$scope.isCommitteeFilter = undefined;
|
||||
};
|
||||
$scope.areFiltersSet = function () {
|
||||
return $scope.multiselect.areFiltersSet() ||
|
||||
$scope.isPresentFilter ||
|
||||
$scope.isActiveFilter ||
|
||||
$scope.isCommitteeFilter ||
|
||||
($scope.filter ? $scope.filter.search : false);
|
||||
};
|
||||
|
||||
// pagination
|
||||
$scope.currentPage = 1;
|
||||
@ -467,6 +552,17 @@ angular.module('OpenSlidesApp.users.site', [
|
||||
$scope.limitBegin = ($scope.currentPage - 1) * $scope.itemsPerPage;
|
||||
};
|
||||
|
||||
// Toggle group from user
|
||||
$scope.toggleGroup = function (user, group) {
|
||||
if (_.indexOf(user.groups_id, group.id) > -1) {
|
||||
user.groups_id = _.filter(user.groups_id, function (group_id) {
|
||||
return group_id != group.id;
|
||||
});
|
||||
} else {
|
||||
user.groups_id.push(group.id);
|
||||
}
|
||||
$scope.save(user);
|
||||
};
|
||||
// open new/edit dialog
|
||||
$scope.openDialog = function (user) {
|
||||
ngDialog.open(UserForm.getDialog(user));
|
||||
@ -490,7 +586,8 @@ angular.module('OpenSlidesApp.users.site', [
|
||||
$scope.isSelectMode = false;
|
||||
// check all checkboxes
|
||||
$scope.checkAll = function () {
|
||||
angular.forEach($scope.users, function (user) {
|
||||
$scope.selectedAll = !$scope.selectedAll;
|
||||
_.forEach($scope.usersFiltered, function (user) {
|
||||
user.selected = $scope.selectedAll;
|
||||
});
|
||||
};
|
||||
@ -559,20 +656,25 @@ angular.module('OpenSlidesApp.users.site', [
|
||||
$scope.uncheckAll();
|
||||
};
|
||||
|
||||
$scope.makePDF_userList = function () {
|
||||
// Export as PDF
|
||||
$scope.pdfExportUserList = function () {
|
||||
var filename = gettextCatalog.getString("List of participants")+".pdf";
|
||||
var userListContentProvider = UserListContentProvider.createInstance($scope.users, $scope.groups);
|
||||
var userListContentProvider = UserListContentProvider.createInstance($scope.usersFiltered, $scope.groups);
|
||||
var documentProvider = PdfMakeDocumentProvider.createInstance(userListContentProvider);
|
||||
pdfMake.createPdf(documentProvider.getDocument()).download(filename);
|
||||
};
|
||||
|
||||
$scope.makePDF_userAccessDataList = function () {
|
||||
$scope.pdfExportUserAccessDataList = function () {
|
||||
var filename = gettextCatalog.getString("List of access data")+".pdf";
|
||||
var userAccessDataListContentProvider = UserAccessDataListContentProvider.createInstance(
|
||||
$scope.users, $scope.groups, Config);
|
||||
$scope.usersFiltered, $scope.groups, Config);
|
||||
var documentProvider = PdfMakeDocumentProvider.createInstance(userAccessDataListContentProvider);
|
||||
pdfMake.createPdf(documentProvider.getDocument()).download(filename);
|
||||
};
|
||||
// Export as a csv file
|
||||
$scope.csvExport = function () {
|
||||
var element = document.getElementById('downloadLinkCSV');
|
||||
UserCsvExport(element, $scope.usersFiltered);
|
||||
};
|
||||
}
|
||||
])
|
||||
|
||||
|
@ -13,26 +13,6 @@
|
||||
<i class="fa fa-download fa-lg"></i>
|
||||
<translate>Import</translate>
|
||||
</a>
|
||||
<div class="dropdown pull-right" uib-dropdown>
|
||||
<button os-perms="users.can_manage" class="btn btn-default btn-sm" uib-dropdown-toggle
|
||||
id="user-pdf-dropdown">
|
||||
<i class="fa fa-file-pdf-o fa-lg"></i>
|
||||
<translate>PDF</translate>
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" dropdown-menu-right aria-labelledby="user-pdf-dropdown">
|
||||
<li>
|
||||
<a href="" ng-click="makePDF_userList()">
|
||||
<i class="fa fa-list fa-fw"></i>
|
||||
<translate>List of participants</translate>
|
||||
</a>
|
||||
<li os-perms="users.can_manage">
|
||||
<a href="" ng-click="makePDF_userAccessDataList()">
|
||||
<i class="fa fa-qrcode fa-fw"></i>
|
||||
<translate>List of access data</translate>
|
||||
</a>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<h1 translate>Participants</h1>
|
||||
</div>
|
||||
@ -50,35 +30,36 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="form-inline text-right">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<div class="input-group-addon"><i class="fa fa-search"></i></div>
|
||||
<input type="text" ng-model="filter.search" ng-model-options="{debounce: 500}" class="form-control"
|
||||
placeholder="{{ 'Search' | translate}}">
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-default" ng-click="$parent.isFilterOpen = !$parent.isFilterOpen"
|
||||
ng-class="$parent.isFilterOpen ? 'btn-primary' : 'btn-default'">
|
||||
<i class="fa fa-filter"></i>
|
||||
<translate>Filter ...</translate>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div uib-collapse="!$parent.isFilterOpen" class="row spacer">
|
||||
<div class="col-sm-6 text-right"></div>
|
||||
<div class="col-sm-6 text-right form-inline">
|
||||
<!-- group filter -->
|
||||
<select ng-model="groupFilter" class="form-control" id="groupFilter">
|
||||
<option ng-click="groupFilter = undefined" value="" translate>--- Select group ---</option>
|
||||
<option ng-repeat="group in groups" value="{{ group.id }}">{{ group.name | translate }}</option>
|
||||
</select>
|
||||
<!-- isPresent filter -->
|
||||
<span os-perms="users.can_see_extra_data">
|
||||
<input type="checkbox" ng-model="$parent.filterPresent" ng-false-value="''">
|
||||
<translate>Is present</translate>
|
||||
<div class="dropdown pull-right" uib-dropdown>
|
||||
<button os-perms="users.can_manage" class="btn btn-default" uib-dropdown-toggle
|
||||
id="user-pdf-dropdown">
|
||||
<i class="fa fa-upload"></i>
|
||||
<span ng-if="usersFiltered.length == users.length" translate>
|
||||
Export all
|
||||
</span>
|
||||
<span ng-if="usersFiltered.length != users.length" translate>
|
||||
Export filtered
|
||||
</span>
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" dropdown-menu-right aria-labelledby="user-pdf-dropdown">
|
||||
<li>
|
||||
<a href="" ng-click="pdfExportUserList()">
|
||||
<i class="fa fa-list fa-fw"></i>
|
||||
<translate>List of participants</translate> (PDF)
|
||||
</a>
|
||||
<li os-perms="users.can_manage">
|
||||
<a href="" ng-click="pdfExportUserAccessDataList()">
|
||||
<i class="fa fa-qrcode fa-fw"></i>
|
||||
<translate>List of access data</translate> (PDF)
|
||||
</a>
|
||||
<li>
|
||||
<a href="" id="downloadLinkCSV" ng-click="csvExport()">
|
||||
<i class="fa fa-file-text-o fa-fw"></i>
|
||||
CSV
|
||||
</a>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div uib-collapse="!isSelectMode" class="row spacer">
|
||||
@ -134,94 +115,311 @@
|
||||
|
||||
<!-- filter users (for user with 'can_see_extra_data' permission) - consider present filter -->
|
||||
<div os-perms="users.can_see_extra_data">
|
||||
<span ng-repeat="user in $parent.usersFiltered = (users | filter: filter.search | filter: {groups_id: groupFilter} |
|
||||
filter: {is_present: filterPresent} | orderBy: sortColumn:reverse)"></span>
|
||||
<span ng-repeat="user in $parent.usersFiltered = (users
|
||||
| osFilter: filter.search : multiselect.getFilterString
|
||||
| filter: {is_present: isPresentFilter}
|
||||
| filter: {is_active: isActiveFilter}
|
||||
| filter: {is_committee: isCommitteeFilter}
|
||||
| MultiselectFilter: multiselect.filters.group : getItemId.group
|
||||
| orderBy: sortColumn:reverse)"></span>
|
||||
</div>
|
||||
<!-- filter users (for user without 'can_see_extra_data' permission) -->
|
||||
<div os-perms="!users.can_see_extra_data"
|
||||
ng-repeat="user in $parent.usersFiltered = (users | filter: filter.search | filter: {groups_id: groupFilter} |
|
||||
orderBy: sortColumn:reverse)"></div>
|
||||
ng-repeat="user in $parent.usersFiltered = (users
|
||||
| osFilter: filter.search : multiselect.getFilterString
|
||||
| filter: {is_committee: isCommitteeFilter}
|
||||
| MultiselectFilter: multiselect.filters.group : getItemId.group
|
||||
| orderBy: sortColumn:reverse)"></div>
|
||||
|
||||
<table class="table table-striped table-bordered table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<!-- projector column -->
|
||||
<th ng-show="!isSelectMode" os-perms="core.can_manage_projector" class="minimum">
|
||||
|
||||
<!-- selection column -->
|
||||
<th ng-show="isSelectMode" os-perms="users.can_manage" class="minimum deleteColumn">
|
||||
<input type="checkbox" ng-model="$parent.selectedAll" ng-change="checkAll()">
|
||||
|
||||
<!-- name column -->
|
||||
<th ng-click="toggleSort('first_name')" class="sortable">
|
||||
<translate>Name</translate>
|
||||
<!-- TODO: sort by first OR last name -->
|
||||
<i class="pull-right fa" ng-show="sortColumn === 'first_name' && header.sortable != false"
|
||||
ng-class="reverse ? 'fa-sort-desc' : 'fa-sort-asc'">
|
||||
</i>
|
||||
|
||||
<!-- structure level column -->
|
||||
<th ng-click="toggleSort('structure_level')" class="sortable optional">
|
||||
<translate>Structure level</translate>
|
||||
<i class="pull-right fa" ng-show="sortColumn === 'structure_level' && header.sortable != false"
|
||||
ng-class="reverse ? 'fa-sort-desc' : 'fa-sort-asc'">
|
||||
</i>
|
||||
|
||||
<!-- groups column -->
|
||||
<th ng-click="toggleSort('groups')" class="sortable optional">
|
||||
<translate>Groups</translate>
|
||||
<i class="pull-right fa" ng-show="sortColumn === 'groups' && header.sortable != false"
|
||||
ng-class="reverse ? 'fa-sort-desc' : 'fa-sort-asc'">
|
||||
</i>
|
||||
|
||||
<!-- present column -->
|
||||
<th os-perms="users.can_see_extra_data" ng-click="toggleSort('is_present')" class="sortable minimum">
|
||||
<translate>Present</translate>
|
||||
<i class="pull-right fa" ng-show="sortColumn === 'is_present' && header.sortable != false"
|
||||
ng-class="reverse ? 'fa-sort-desc' : 'fa-sort-asc'">
|
||||
</i>
|
||||
<tbody>
|
||||
<tr ng-repeat="user in usersFiltered | limitTo : itemsPerPage : limitBegin" class="animate-item"
|
||||
ng-class="{ 'activeline': user.isProjected().length, 'selected': user.selected }">
|
||||
<!-- projector column -->
|
||||
<td ng-show="!isSelectMode" os-perms="core.can_manage_projector">
|
||||
<projector-button model="user" default-projector-id="defaultProjectorId">
|
||||
</projector-button>
|
||||
<!-- selection column -->
|
||||
<td ng-show="isSelectMode" os-perms="users.can_manage" class="deleteColumn">
|
||||
<input type="checkbox" ng-model="user.selected">
|
||||
<!-- user data colums -->
|
||||
<td ng-mouseover="user.hover=true" ng-mouseleave="user.hover=false">
|
||||
<strong>
|
||||
<i ng-show="user.is_active === false" class="fa fa-ban"
|
||||
title="{{ 'Is inactive' | translate }}"></i>
|
||||
<i ng-show="user.is_committee" class="fa fa-users"
|
||||
title="{{ 'Is a committee' | translate }}"></i>
|
||||
<a ui-sref="users.user.detail({id: user.id})">{{ user.get_short_name() }}</a>
|
||||
</strong>
|
||||
<div ng-if="user.comment">
|
||||
<small><i class="fa fa-info-circle"></i> {{ user.comment }}</small>
|
||||
<div class="os-table container-fluid">
|
||||
<div class="row header-row">
|
||||
<div class="col-xs-1 centered" ng-show="isSelectMode" os-perms="users.can_manage">
|
||||
<i class="fa text-danger pointer" ng-class="selectedAll ? 'fa-check-square-o' : 'fa-square-o'"
|
||||
ng-click="checkAll()"></i>
|
||||
</div>
|
||||
<div ng-if="user.number"> {{ user.number }} </div>
|
||||
<div os-perms="users.can_manage" class="hoverActions" ng-class="{'hiddenDiv': !user.hover}">
|
||||
<a href="" ng-click="openDialog(user)" translate>Edit</a> |
|
||||
<a ui-sref="users.user.change-password({id: user.id})" translate>Change password</a> |
|
||||
<div class="col-xs-11 main-header">
|
||||
<span class="form-inline text-right pull-right">
|
||||
<!-- reset Filters -->
|
||||
<span class="sort-spacer pointer" ng-click="resetFilters()"
|
||||
ng-if="areFiltersSet()" ng-disabled="isSelectMode"
|
||||
ng-class="{'disabled': isSelectMode}">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
<translate>Filter</translate>
|
||||
</span>
|
||||
<!-- Group filter -->
|
||||
<span class="dropdown" uib-dropdown>
|
||||
<span class="pointer" id="dropdownGroup" uib-dropdown-toggle
|
||||
ng-class="{'bold': multiselect.filters.group.length > 0, 'disabled': isSelectMode}"
|
||||
ng-disabled="isSelectMode">
|
||||
<translate>Groups</translate>
|
||||
<span class="caret"></span>
|
||||
</span>
|
||||
<ul class="dropdown-menu dropdown-menu-right dropdown-entries" aria-labelledby="dropdownGroup">
|
||||
<li ng-repeat="group in groups">
|
||||
<div ng-click="operateMultiselectFilter('group', group.id)">
|
||||
<i class="fa fa-check" ng-if="multiselect.filters.group.indexOf(group.id) > -1"></i>
|
||||
{{ group.name | translate }}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
<!-- Present filter -->
|
||||
<span class="dropdown" uib-dropdown>
|
||||
<span class="pointer" id="dropdownPresent" uib-dropdown-toggle
|
||||
ng-class="{'bold': isPresentFilter !== undefined, 'disabled': isSelectMode}"
|
||||
ng-disabled="isSelectMode">
|
||||
<translate>Present</translate>
|
||||
<span class="caret"></span>
|
||||
</span>
|
||||
<ul class="dropdown-menu dropdown-menu-right dropdown-entries" aria-labelledby="dropdownPresent">
|
||||
<li>
|
||||
<div ng-click="isPresentFilter = (isPresentFilter ? undefined : true)">
|
||||
<i class="fa" ng-class="{'fa-check': isPresentFilter === true}"></i>
|
||||
<translate>Is present</translate>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div ng-click="isPresentFilter = (isPresentFilter === false) ? undefined : false">
|
||||
<i class="fa" ng-class="{'fa-check': isPresentFilter === false}"></i>
|
||||
<translate>Is not present</translate>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
<!-- Active filter -->
|
||||
<span class="dropdown" uib-dropdown>
|
||||
<span class="pointer" id="dropdownActive" uib-dropdown-toggle
|
||||
ng-class="{'bold': isActiveFilter !== undefined, 'disabled': isSelectMode}"
|
||||
ng-disabled="isSelecteMode">
|
||||
<translate>Active</translate>
|
||||
<span class="caret"></span>
|
||||
</span>
|
||||
<ul class="dropdown-menu dropdown-menu-right dropdown-entries" aria-labelledby="dropdownActive">
|
||||
<li>
|
||||
<div ng-click="isActiveFilter = (isActiveFilter ? undefined : true)">
|
||||
<i class="fa" ng-class="{'fa-check': isActiveFilter === true}"></i>
|
||||
<translate>Is active</translate>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div ng-click="isActiveFilter = (isActiveFilter === false ? undefined : false)">
|
||||
<i class="fa" ng-class="{'fa-check': isActiveFilter === false}"></i>
|
||||
<translate>Is not active</translate>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
<!-- Committee filter -->
|
||||
<span class="dropdown" uib-dropdown>
|
||||
<span class="pointer" id="dropdownCommittee" uib-dropdown-toggle
|
||||
ng-class="{'bold': isCommitteeFilter !== undefined, 'disabled': isSelectMode}"
|
||||
ng-disabled="isSelecteMode">
|
||||
<translate>Committee</translate>
|
||||
<span class="caret"></span>
|
||||
</span>
|
||||
<ul class="dropdown-menu dropdown-menu-right dropdown-entries" aria-labelledby="dropdownCommittee">
|
||||
<li>
|
||||
<div ng-click="isCommitteeFilter = (isCommitteeFilter ? undefined : true)">
|
||||
<i class="fa" ng-class="{'fa-check': isCommitteeFilter === true}"></i>
|
||||
<translate>Yes</translate>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div ng-click="isCommitteeFilter = (isCommitteeFilter === false ? undefined : false)">
|
||||
<i class="fa" ng-class="{'fa-check': isCommitteeFilter === false}"></i>
|
||||
<translate>No</translate>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
<!-- dropdown sort -->
|
||||
<span class="dropdown" uib-dropdown>
|
||||
<span class="pointer" id="dropdownSort" uib-dropdown-toggle
|
||||
ng-class="{'disabled': isSelectMode}"
|
||||
ng-disabled="isSelectMode">
|
||||
<translate>Sort</translate>
|
||||
<span class="caret"></span>
|
||||
</span>
|
||||
<ul class="dropdown-menu dropdown-menu-right dropdown-entries" aria-labelledby="dropdownSort">
|
||||
<li ng-repeat="option in sortOptions">
|
||||
<div ng-click="toggleSort(option.name)">
|
||||
{{ option.display_name | translate }}
|
||||
<span class="spacer-right pull-right"></span>
|
||||
<i class="pull-right fa"
|
||||
ng-style="{'visibility': sortColumn === option.name ? '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" ng-model-options="{debounce: 500}"
|
||||
placeholder="{{ 'Search' | translate}}" ng-disabled="isSelectMode">
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<!-- show all selected multiselectoptions -->
|
||||
<span>
|
||||
<span ng-repeat="group in groups" class="pointer spacer-left-lg"
|
||||
ng-if="multiselect.filters.group.indexOf(group.id) > -1"
|
||||
ng-click="operateMultiselectFilter('group', group.id)"
|
||||
ng-class="{'disabled': isSelectMode}">
|
||||
<span class="nobr">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
{{ group.name | translate }}
|
||||
</span>
|
||||
</span>
|
||||
<span ng-hide="isPresentFilter === undefined"
|
||||
class="pointer spacer-left-lg"
|
||||
ng-click="isPresentFilter = undefined;"
|
||||
ng-class="{'disabled': isSelectMode}">
|
||||
<span class="nobr">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
{{ isPresentFilter ? 'Is present' : 'Is not present' | translate }}
|
||||
</span>
|
||||
</span>
|
||||
<span ng-hide="isActiveFilter === undefined"
|
||||
class="pointer spacer-left-lg"
|
||||
ng-click="isActiveFilter = undefined;"
|
||||
ng-class="{'disabled': isSelectMode}">
|
||||
<span class="nobr">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
{{ isActiveFilter ? 'Is active' : 'Is not active' | translate }}
|
||||
</span>
|
||||
</span>
|
||||
<span ng-hide="isCommitteeFilter === undefined"
|
||||
class="pointer spacer-left-lg"
|
||||
ng-click="isCommitteeFilter = undefined;"
|
||||
ng-class="{'disabled': isSelectMode}">
|
||||
<span class="nobr">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
{{ isCommitteeFilter ? 'Is committee' : 'Is not committee' | translate }}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- main table -->
|
||||
<div class="row data-row" ng-mouseover="user.hover=true"
|
||||
ng-mouseleave="user.hover=false"
|
||||
ng-class="{'projected': user.isProjected().length}"
|
||||
ng-repeat="user in usersFiltered
|
||||
| limitTo : itemsPerPage : limitBegin">
|
||||
|
||||
<!-- select column -->
|
||||
<div ng-show="isSelectMode" os-perms="users.can_manage" class="col-xs-1 centered">
|
||||
<i class="fa text-danger pointer" ng-click="user.selected=!user.selected"
|
||||
ng-class="user.selected ? 'fa-check-square-o' : 'fa-square-o'"></i>
|
||||
</div>
|
||||
<!-- projector column -->
|
||||
<div class="col-xs-1 centered projector" os-perms="core.can_manage_projector">
|
||||
<projector-button model="user", default-projector-id="defaultProjectorId">
|
||||
</projector-button>
|
||||
</div>
|
||||
<!-- main content column -->
|
||||
<div class="col-xs-6 content">
|
||||
<div class="spacer-right"> <!-- horizontal block -->
|
||||
<strong>
|
||||
<i ng-style="{'visibility': user.is_active === false ? 'visible' : 'hidden'}" class="fa fa-ban"
|
||||
title="{{ 'Is inactive' | translate }}"></i>
|
||||
<i ng-style="{'visibility': user.is_committee ? 'visible' : 'hidden'}" class="fa fa-university"
|
||||
title="{{ 'Is a committee' | translate }}"></i>
|
||||
</strong>
|
||||
</div>
|
||||
<div>
|
||||
<div> <!-- vertical block -->
|
||||
<strong>
|
||||
<a ui-sref="users.user.detail({id: user.id})" class="title">{{ user.get_short_name() }}</a>
|
||||
</strong>
|
||||
</div>
|
||||
<div ng-if="user.number"><translate>No.</translate> {{ user.number }} </div>
|
||||
<div os-perms="users.can_manage" ng-class="{'hiddenDiv': !user.hover}">
|
||||
<small>
|
||||
<a href="" ng-click="openDialog(user)" translate>Edit</a> ·
|
||||
<a ui-sref="users.user.change-password({id: user.id})" translate>Change password</a> ·
|
||||
<a href="" class="text-danger"
|
||||
ng-bootbox-confirm="{{ 'Are you sure you want to delete this entry?' | translate }}<br>
|
||||
<b>{{ user.get_short_name() }}</b>"
|
||||
ng-bootbox-confirm-action="delete(user)" translate>Delete</a>
|
||||
</small>
|
||||
</div>
|
||||
<td class="optional">{{ user.structure_level }}
|
||||
<td class="optional">
|
||||
<div ng-repeat="group in user.groups_id">
|
||||
{{ (groups | filter: {id: group})[0].name | translate }}
|
||||
</div>
|
||||
<td os-perms="users.can_see_extra_data">
|
||||
<span os-perms="!users.can_manage">
|
||||
<i ng-if="user.is_present" class="fa fa-check-square-o"></i>
|
||||
</div>
|
||||
<!-- additional content column -->
|
||||
<style>
|
||||
.os-table .row .col-xs-4 {
|
||||
width: calc(50% - {{ isSelectMode ? '120' : '70' }}px);
|
||||
}
|
||||
</style>
|
||||
<div class="col-xs-4 content">
|
||||
<div style="width: 60%;" class="optional">
|
||||
<small>
|
||||
<!-- Group dropdown for manage user -->
|
||||
<div os-perms="users.can_manage" ng-show="groups.length > 0"
|
||||
uib-tooltip="{{ 'Groups' | translate }}" tooltip-placement="top-left"
|
||||
ng-mouseover="user.groupHover=true"
|
||||
ng-mouseleave="user.groupHover=false">
|
||||
<span uib-dropdown>
|
||||
<span id="dropdown-group{{ user.id }}" class="pointer" uib-dropdown-toggle>
|
||||
<span ng-if="!user.groups_id.length" ng-show="user.hover">
|
||||
<i class="fa fa-users"></i>
|
||||
<i class="fa fa-plus"></i>
|
||||
</span>
|
||||
<input os-perms="users.can_manage" type="checkbox" ng-model="user.is_present" ng-click="save(user)">
|
||||
</table>
|
||||
<span ng-if="user.groups_id.length">
|
||||
<i class="fa fa-users"></i>
|
||||
<span ng-repeat="group in user.groups_id | limitTo:2">
|
||||
{{ (groups | filter: {id: group})[0].name | translate }}<span ng-if="!$last">,</span></span><span ng-if="user.groups_id.length > 2">,
|
||||
... [+{{ user.groups_id.length - 2}}]</span>
|
||||
<i class="fa fa-cog fa-lg spcaer-left" ng-show="user.groupHover"></i>
|
||||
</span>
|
||||
</span>
|
||||
<ul class="dropdown-menu dropdown-entries" aria-labelledby="dropdown-group{{ user.id }}">
|
||||
<li ng-repeat="group in groups">
|
||||
<div ng-click="toggleGroup(user, group)">
|
||||
<i class="fa fa-check" ng-if="inArray(user.groups_id, group.id)"></i>
|
||||
{{ group.name | translate }}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
</div>
|
||||
<!-- Group dropdown for normal user -->
|
||||
<div os-perms="!users.can_manage" ng-show="user.groups_id.length"
|
||||
uib-tooltip="{{ 'Groups' | translate }}" tooltip-placement="top-left">
|
||||
<i class="fa fa-users"></i>
|
||||
<span ng-repeat="group in user.groups_id | limitTo:2">
|
||||
{{ (groups | filter: {id: group})[0].name | translate }}<span ng-if="!$last">,</span></span><span ng-if="user.groups_id.length > 2">,
|
||||
... [+{{ user.groups_id.length - 2}}]</span>
|
||||
<!-- sorry for merging them together, but otherwise there would be a whitespace because of the new line -->
|
||||
</div>
|
||||
<div ng-if="user.structure_level" uib-tooltip="{{ 'Structure level' | translate }}" tooltip-placement="top-left">
|
||||
<i class="fa fa-flag"></i>
|
||||
{{ user.structure_level }}
|
||||
</div>
|
||||
<div ng-if="user.comment" uib-tooltip="{{ 'Comment' | translate }}" tooltip-placement="top-left">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
{{ user.comment | limitTo:25}}{{ user.comment.length > 25 ? '...' : '' }}
|
||||
</div>
|
||||
</small>
|
||||
</div>
|
||||
<div style="width: 40%;" class="pull-right" os-perms="users.can_see_extra_data">
|
||||
<div os-perms="users.can_manage">
|
||||
<span class="pointer" ng-click="user.is_present = !user.is_present; save(user);">
|
||||
<i class="fa" ng-class="user.is_present ? 'fa-check-square-o' : 'fa-square-o'"></i>
|
||||
<span class="spacer-left" translate>Present</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- end data row -->
|
||||
</div><!-- end os-table -->
|
||||
|
||||
<ul uib-pagination
|
||||
total-items="usersFiltered.length"
|
||||
items-per-page="itemsPerPage"
|
||||
|
Loading…
Reference in New Issue
Block a user