Merge pull request #2721 from FinnStutzenstein/AgendaTable
Agenda table
This commit is contained in:
commit
486f0601eb
@ -178,6 +178,7 @@ OpenSlides uses the following projects or parts of them:
|
||||
* `angular-bootstrap <http://angular-ui.github.io/bootstrap>`_, License: MIT
|
||||
* `angular-bootstrap-colorpicker <https://github.com/buberdds/angular-bootstrap-colorpicker>`_, License: MIT
|
||||
* `angular-chosen-localytics <http://github.com/leocaseiro/angular-chosen>`_, License: MIT
|
||||
* `angular-cookies <https://github.com/angular/bower-angular-cookies>`_, License: MIT
|
||||
* `angular-csv-import <https://github.com/bahaaldine/angular-csv-import>`_, License: MIT
|
||||
* `angular-formly <http://formly-js.github.io/angular-formly/>`_, License: MIT
|
||||
* `angular-formly-templates-bootstrap <https://github.com/formly-js/angular-formly-templates-bootstrap>`_, License: MIT
|
||||
@ -191,6 +192,7 @@ OpenSlides uses the following projects or parts of them:
|
||||
* `angular-ui-router <http://angular-ui.github.io/ui-router/>`_, License: MIT
|
||||
* `angular-ui-tinymce <http://angular-ui.github.com>`_, License: MIT
|
||||
* `angular-ui-tree <https://github.com/angular-ui-tree/angular-ui-tree>`_, License: MIT
|
||||
* `angular-xeditable <https://github.com/vitalets/angular-xeditable>`_, License: MIT
|
||||
* `api-check <https://github.com/kentcdodds/api-check>`_, License: MIT
|
||||
* `bootstrap <http://getbootstrap.com>`_, License: MIT
|
||||
* `bootstrap-ui-datetime-picker <https://github.com/Gillardo/bootstrap-ui-datetime-picker>`_, License: MIT
|
||||
|
@ -9,6 +9,7 @@
|
||||
"angular-bootstrap-colorpicker": "~3.0.25",
|
||||
"angular-chosen-localytics": "~1.5.0",
|
||||
"angular-csv-import": "0.0.36",
|
||||
"angular-cookies": "~1.5.9",
|
||||
"angular-file-saver": "~1.1.2",
|
||||
"angular-formly": "~8.4.0",
|
||||
"angular-formly-templates-bootstrap": "~6.2.0",
|
||||
@ -21,6 +22,7 @@
|
||||
"angular-ui-router": "~0.3.1",
|
||||
"angular-ui-tinymce": "~0.0.17",
|
||||
"angular-ui-tree": "~2.22.0",
|
||||
"angular-xeditable": "~0.5.0",
|
||||
"bootstrap-css-only": "~3.3.6",
|
||||
"bootstrap-ui-datetime-picker": "~2.4.0",
|
||||
"docxtemplater": "~2.1.5",
|
||||
|
@ -107,8 +107,12 @@ angular.module('OpenSlidesApp.agenda.site', [
|
||||
'AgendaContentProvider',
|
||||
'PdfMakeDocumentProvider',
|
||||
'gettextCatalog',
|
||||
'gettext',
|
||||
'osTableFilter',
|
||||
'AgendaCsvExport',
|
||||
function($scope, $filter, $http, $state, DS, operator, ngDialog, Agenda, TopicForm, AgendaTree, Projector,
|
||||
ProjectionDefault, AgendaContentProvider, PdfMakeDocumentProvider, gettextCatalog) {
|
||||
ProjectionDefault, AgendaContentProvider, PdfMakeDocumentProvider, gettextCatalog, gettext, osTableFilter,
|
||||
AgendaCsvExport) {
|
||||
// Bind agenda tree to the scope
|
||||
$scope.$watch(function () {
|
||||
return Agenda.lastModified();
|
||||
@ -132,6 +136,86 @@ angular.module('OpenSlidesApp.agenda.site', [
|
||||
});
|
||||
$scope.alert = {};
|
||||
|
||||
|
||||
// Filtering
|
||||
$scope.filter = osTableFilter.createInstance('AgendaTableFilter');
|
||||
|
||||
if (!$scope.filter.existsCookie()) {
|
||||
$scope.filter.booleanFilters = {
|
||||
closed: {
|
||||
value: undefined,
|
||||
displayName: gettext('Closed items'),
|
||||
choiceYes: gettext('Closed items'),
|
||||
choiceNo: gettext('Open items'),
|
||||
},
|
||||
is_hidden: {
|
||||
value: undefined,
|
||||
displayName: gettext('Internal items'),
|
||||
choiceYes: gettext('Internal items'),
|
||||
choiceNo: gettext('No internal items'),
|
||||
},
|
||||
};
|
||||
|
||||
$scope.filter.save();
|
||||
}
|
||||
$scope.filter.propertyList = ['item_number', 'title', 'title_list_view', 'comment', 'duration'];
|
||||
$scope.filter.propertyFunctionList = [
|
||||
function (item) {return item.getListViewTitle();},
|
||||
];
|
||||
$scope.filter.propertyDict = {
|
||||
'speakers' : function (speaker) {
|
||||
return '';
|
||||
},
|
||||
};
|
||||
|
||||
// pagination
|
||||
$scope.currentPage = 1;
|
||||
$scope.itemsPerPage = 100;
|
||||
$scope.limitBegin = 0;
|
||||
$scope.pageChanged = function() {
|
||||
$scope.limitBegin = ($scope.currentPage - 1) * $scope.itemsPerPage;
|
||||
};
|
||||
|
||||
// parse duration for inline editing
|
||||
$scope.generateDurationText = function (item) {
|
||||
//convert data from model format (m) to view format (hh:mm)
|
||||
if (item.duration) {
|
||||
var time = "",
|
||||
totalminutes = item.duration;
|
||||
if (totalminutes < 0) {
|
||||
time = "-";
|
||||
totalminutes = -totalminutes;
|
||||
}
|
||||
var hh = Math.floor(totalminutes / 60);
|
||||
var mm = Math.floor(totalminutes % 60);
|
||||
// Add leading "0" for double digit values
|
||||
mm = ("0"+mm).slice(-2);
|
||||
time += hh + ":" + mm;
|
||||
item.durationText = time;
|
||||
} else {
|
||||
item.durationText = "";
|
||||
}
|
||||
};
|
||||
$scope.setDurationText = function (item) {
|
||||
//convert data from view format (hh:mm) to model format (m)
|
||||
var time = item.durationText.replace('h', '').split(':');
|
||||
var data;
|
||||
if (time.length > 1 && !isNaN(time[0]) && !isNaN(time[1])) {
|
||||
data = (+time[0]) * 60 + (+time[1]);
|
||||
if (data < 0) {
|
||||
data = "-"+data;
|
||||
}
|
||||
item.duration = parseInt(data);
|
||||
} else if (time.length == 1 && !isNaN(time[0])) {
|
||||
data = (+time[0]);
|
||||
item.duration = parseInt(data);
|
||||
} else {
|
||||
item.duration = 0;
|
||||
}
|
||||
$scope.save(item);
|
||||
};
|
||||
|
||||
/** Duration calculations **/
|
||||
$scope.sumDurations = function () {
|
||||
var totalDuration = 0;
|
||||
$scope.items.forEach(function (item) {
|
||||
@ -141,7 +225,6 @@ angular.module('OpenSlidesApp.agenda.site', [
|
||||
});
|
||||
return totalDuration;
|
||||
};
|
||||
|
||||
$scope.calculateEndTime = function () {
|
||||
var totalDuration = $scope.sumDurations();
|
||||
var startTimestamp = $scope.config('agenda_start_event_date_time');
|
||||
@ -156,22 +239,33 @@ angular.module('OpenSlidesApp.agenda.site', [
|
||||
}
|
||||
};
|
||||
|
||||
$scope.getUpdateStatePrefix = function (item) {
|
||||
var prefix = item.content_object.collection.replace('/','.');
|
||||
// Hotfix for Issue 2566.
|
||||
// The changes could be reverted if Issue 2480 is closed.
|
||||
prefix = prefix.replace('motion-block', 'motionBlock');
|
||||
return prefix;
|
||||
/** Agenda item functions **/
|
||||
// open dialog for new topics // TODO Remove this. Don't forget import button in template.
|
||||
$scope.newDialog = function () {
|
||||
ngDialog.open(TopicForm.getDialog());
|
||||
};
|
||||
|
||||
// pagination
|
||||
$scope.currentPage = 1;
|
||||
$scope.itemsPerPage = 100;
|
||||
$scope.limitBegin = 0;
|
||||
$scope.pageChanged = function() {
|
||||
$scope.limitBegin = ($scope.currentPage - 1) * $scope.itemsPerPage;
|
||||
// save changed item
|
||||
$scope.save = function (item) {
|
||||
Agenda.save(item).then(
|
||||
function(success) {
|
||||
$scope.alert.show = false;
|
||||
},
|
||||
function(error){
|
||||
var message = '';
|
||||
for (var e in error.data) {
|
||||
message += e + ': ' + error.data[e] + ' ';
|
||||
}
|
||||
$scope.alert = { type: 'danger', msg: message, show: true };
|
||||
});
|
||||
};
|
||||
// delete related item
|
||||
$scope.deleteRelatedItem = function (item) {
|
||||
DS.destroy(item.content_object.collection, item.content_object.id);
|
||||
};
|
||||
// auto numbering of agenda items
|
||||
$scope.autoNumbering = function() {
|
||||
$http.post('/rest/agenda/item/numbering/', {});
|
||||
};
|
||||
|
||||
// check open permission
|
||||
// TODO: Use generic solution here.
|
||||
$scope.isAllowedToSeeOpenLink = function (item) {
|
||||
@ -189,47 +283,37 @@ angular.module('OpenSlidesApp.agenda.site', [
|
||||
return false;
|
||||
}
|
||||
};
|
||||
// open dialog for new topics // TODO Remove this. Don't forget import button in template.
|
||||
$scope.newDialog = function () {
|
||||
ngDialog.open(TopicForm.getDialog());
|
||||
$scope.getUpdateStatePrefix = function (item) {
|
||||
var prefix = item.content_object.collection.replace('/','.');
|
||||
// Hotfix for Issue 2566.
|
||||
// The changes could be reverted if Issue 2480 is closed.
|
||||
prefix = prefix.replace('motion-block', 'motionBlock');
|
||||
return prefix;
|
||||
};
|
||||
// cancel QuickEdit mode
|
||||
$scope.cancelQuickEdit = function (item) {
|
||||
// revert all changes by restore (refresh) original item object from server
|
||||
Agenda.refresh(item);
|
||||
item.quickEdit = false;
|
||||
// export
|
||||
$scope.pdfExport = function () {
|
||||
var filename = gettextCatalog.getString('Agenda') + '.pdf';
|
||||
var agendaContentProvider = AgendaContentProvider.createInstance($scope.itemsFiltered);
|
||||
var documentProvider = PdfMakeDocumentProvider.createInstance(agendaContentProvider);
|
||||
pdfMake.createPdf(documentProvider.getDocument()).download(filename);
|
||||
};
|
||||
// save changed item
|
||||
$scope.save = function (item) {
|
||||
Agenda.save(item).then(
|
||||
function(success) {
|
||||
item.quickEdit = false;
|
||||
$scope.alert.show = false;
|
||||
},
|
||||
function(error){
|
||||
var message = '';
|
||||
for (var e in error.data) {
|
||||
message += e + ': ' + error.data[e] + ' ';
|
||||
}
|
||||
$scope.alert = { type: 'danger', msg: message, show: true };
|
||||
});
|
||||
};
|
||||
// delete related item
|
||||
$scope.deleteRelatedItem = function (item) {
|
||||
DS.destroy(item.content_object.collection, item.content_object.id);
|
||||
$scope.csvExport = function () {
|
||||
var element = document.getElementById('downloadLinkCSV');
|
||||
AgendaCsvExport(element, $scope.itemsFiltered);
|
||||
};
|
||||
|
||||
// *** delete mode functions ***
|
||||
$scope.isDeleteMode = false;
|
||||
/** select mode functions **/
|
||||
$scope.isSelectMode = false;
|
||||
// check all checkboxes
|
||||
$scope.checkAll = function () {
|
||||
$scope.selectedAll = !$scope.selectedAll;
|
||||
angular.forEach($scope.items, function (item) {
|
||||
item.selected = $scope.selectedAll;
|
||||
});
|
||||
};
|
||||
// uncheck all checkboxes if isDeleteMode is closed
|
||||
$scope.uncheckAll = function () {
|
||||
if (!$scope.isDeleteMode) {
|
||||
if (!$scope.isSelectMode) {
|
||||
$scope.selectedAll = false;
|
||||
angular.forEach($scope.items, function (item) {
|
||||
item.selected = false;
|
||||
@ -243,7 +327,7 @@ angular.module('OpenSlidesApp.agenda.site', [
|
||||
DS.destroy(item.content_object.collection, item.content_object.id);
|
||||
}
|
||||
});
|
||||
$scope.isDeleteMode = false;
|
||||
$scope.isSelectMode = false;
|
||||
$scope.uncheckAll();
|
||||
};
|
||||
|
||||
@ -319,17 +403,6 @@ angular.module('OpenSlidesApp.agenda.site', [
|
||||
});
|
||||
return projectorIds;
|
||||
};
|
||||
// auto numbering of agenda items
|
||||
$scope.autoNumbering = function() {
|
||||
$http.post('/rest/agenda/item/numbering/', {});
|
||||
};
|
||||
|
||||
$scope.makePDF = function() {
|
||||
var filename = gettextCatalog.getString('Agenda') + '.pdf';
|
||||
var agendaContentProvider = AgendaContentProvider.createInstance($scope.items);
|
||||
var documentProvider = PdfMakeDocumentProvider.createInstance(agendaContentProvider);
|
||||
pdfMake.createPdf(documentProvider.getDocument()).download(filename);
|
||||
};
|
||||
}
|
||||
])
|
||||
|
||||
@ -613,6 +686,30 @@ angular.module('OpenSlidesApp.agenda.site', [
|
||||
}
|
||||
])
|
||||
|
||||
.factory('AgendaCsvExport', [
|
||||
function () {
|
||||
return function (element, agenda) {
|
||||
var csvRows = [
|
||||
['title', 'text', 'duration', 'comment', 'is_hidden'],
|
||||
];
|
||||
_.forEach(agenda, function (item) {
|
||||
var row = [];
|
||||
row.push('"' + (item.title || '') + '"');
|
||||
row.push('"' + (item.text || '') + '"');
|
||||
row.push('"' + (item.duration || '') + '"');
|
||||
row.push('"' + (item.comment || '') + '"');
|
||||
row.push('"' + (item.is_hidden ? '1' : '') + '"');
|
||||
csvRows.push(row);
|
||||
});
|
||||
|
||||
var csvString = csvRows.join("%0A");
|
||||
element.href = 'data:text/csv;charset=utf-8,' + csvString;
|
||||
element.download = 'agenda-export.csv';
|
||||
element.target = '_blank';
|
||||
};
|
||||
}
|
||||
])
|
||||
|
||||
//mark all agenda config strings for translation with Javascript
|
||||
.config([
|
||||
'gettext',
|
||||
|
@ -61,66 +61,61 @@
|
||||
|
||||
<div class="details">
|
||||
<div class="row">
|
||||
<div class="col-sm-7">
|
||||
<div class="form-inline">
|
||||
<!-- delete mode -->
|
||||
<div class="col-sm-12">
|
||||
<!-- select mode -->
|
||||
<button os-perms="agenda.can_manage" class="btn btn-sm"
|
||||
ng-class="$parent.isDeleteMode ? 'btn-primary' : 'btn-default'"
|
||||
ng-click="$parent.isDeleteMode = !$parent.isDeleteMode; uncheckAll()">
|
||||
ng-class="$parent.isSelectMode ? 'btn-primary' : 'btn-default'"
|
||||
ng-click="$parent.isSelectMode = !$parent.isSelectMode; uncheckAll()">
|
||||
<i class="fa fa-check-square-o"></i>
|
||||
<translate>Select ...</translate>
|
||||
</button>
|
||||
|
||||
<!-- sort button -->
|
||||
<a ui-sref="agenda.item.sort" os-perms="agenda.can_manage" class="btn btn-default btn-sm">
|
||||
<i class="fa fa-sitemap fa-lg"></i>
|
||||
<translate>Sort ...</translate>
|
||||
</a>
|
||||
<!-- auto numbering button -->
|
||||
<a os-perms="core.can_manage_projector"
|
||||
class="btn btn-default btn-sm"
|
||||
<button os-perms="core.can_manage_projector" class="btn btn-default btn-sm"
|
||||
ng-click="autoNumbering()">
|
||||
<i class="fa fa-sort-numeric-asc"></i>
|
||||
<translate>Numbering</translate>
|
||||
</a>
|
||||
<!-- pdf -->
|
||||
<a ng-click="makePDF()" class="btn btn-default btn-sm">
|
||||
<i class="fa fa-file-pdf-o fa-lg"></i>
|
||||
<translate>PDF</translate>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-5">
|
||||
<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" class="form-control"
|
||||
placeholder="{{ 'Search' | translate}}">
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-default" ng-click="isFilterOpen = !isFilterOpen"
|
||||
ng-class="isFilterOpen ? 'btn-primary' : 'btn-default'">
|
||||
<i class="fa fa-filter"></i>
|
||||
<translate>Filter ...</translate>
|
||||
</button>
|
||||
<!-- pdf -->
|
||||
<div class="pull-right" uib-dropdown>
|
||||
<button type="button" class="btn btn-default" id="dropdownExport" uib-dropdown-toggle>
|
||||
<i class="fa fa-upload"></i>
|
||||
<span ng-if="itemsFiltered.length == items.length" translate>
|
||||
Export all
|
||||
</span>
|
||||
<span ng-if="itemsFiltered.length != items.length" translate>
|
||||
Export filtered
|
||||
</span>
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownExport">
|
||||
<!-- PDF export -->
|
||||
<li>
|
||||
<a href="" ng-click="pdfExport()">
|
||||
<i class="fa fa-file-pdf-o fa-lg"></i>
|
||||
PDF
|
||||
</a>
|
||||
</li>
|
||||
<!-- CSV export -->
|
||||
<li>
|
||||
<a href="" id="downloadLinkCSV"
|
||||
ng-click="csvExport()">
|
||||
<i class="fa fa-file-text-o fa-lg"></i>
|
||||
CSV
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div uib-collapse="!isFilterOpen" class="row spacer">
|
||||
<div class="col-sm-12 text-right">
|
||||
<!-- hidden item filter -->
|
||||
<input type="checkbox" ng-model="filter.showHiddenItems" ng-true-value="" ng-false-value="false">
|
||||
<translate> Show internal items</translate>
|
||||
<!-- closed filter -->
|
||||
<input type="checkbox" ng-model="filter.showClosedItems" ng-true-value="" ng-false-value="false">
|
||||
<translate> Show closed items</translate>
|
||||
</div>
|
||||
</div>
|
||||
<div uib-collapse="!isDeleteMode" class="row spacer">
|
||||
<div uib-collapse="!isSelectMode" class="row spacer">
|
||||
<div class="col-sm-12 text-left">
|
||||
<!-- delete button -->
|
||||
<a ng-show="isDeleteMode" os-perms="agenda.can_manage"
|
||||
<a ng-show="isSelectMode" os-perms="agenda.can_manage"
|
||||
ng-bootbox-confirm="{{ 'Are you sure you want to delete all selected agenda items?' | translate }}"
|
||||
ng-bootbox-confirm-action="deleteMultiple()"
|
||||
class="btn btn-primary">
|
||||
@ -134,37 +129,98 @@
|
||||
<span os-perms="agenda.can_see_hidden_items">{{ itemsFiltered.length }} /</span>
|
||||
{{ items.length }} {{ "items" | translate }}<span ng-if="(items|filter:{selected:true}).length > 0">,
|
||||
{{(items|filter:{selected:true}).length}} {{ "selected" | translate }}</span>
|
||||
</div>
|
||||
<table class="table table-striped table-bordered table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<!-- projector column -->
|
||||
<th ng-show="!isDeleteMode" os-perms="core.can_manage_projector" class="minimum"></th>
|
||||
<!-- delete selection column -->
|
||||
<th ng-show="isDeleteMode" os-perms="agenda.can_manage" class="minimum deleteColumn"
|
||||
ng-click="$event.stopPropagation();">
|
||||
<input type="checkbox" ng-model="$parent.selectedAll" ng-change="checkAll()">
|
||||
<!-- agenda item column -->
|
||||
<th>
|
||||
<translate>Agenda item</translate>
|
||||
<th os-perms="agenda.can_see_hidden_items" class="optional">
|
||||
<translate>Duration</translate>
|
||||
<span ng-if="sumDurations() > 0">
|
||||
<span os-perms="agenda.can_see_hidden_items" class="optional">
|
||||
<span ng-if="sumDurations() > 0">·
|
||||
<translate>Duration</translate>:
|
||||
{{ sumDurations() | osMinutesToTime }}h
|
||||
<span ng-if="config('agenda_start_event_date_time')">
|
||||
(<translate>Estimated end:</translate> {{ calculateEndTime() }})
|
||||
</span>
|
||||
</span>
|
||||
<th class="minimum optional">
|
||||
<translate>Done</translate>
|
||||
<tbody>
|
||||
<tr ng-repeat="item in itemsFiltered = (items | filter: filter.search |
|
||||
filter: {is_hidden: filter.showHiddenItems} | filter: {closed: filter.showClosedItems}) |
|
||||
limitTo : itemsPerPage : limitBegin"
|
||||
class="animate-item"
|
||||
ng-class="{ 'activeline': item.isProjected().length, 'selected': item.selected, 'hiddenrow': item.is_hidden}">
|
||||
</div>
|
||||
|
||||
<div class="os-table container-fluid">
|
||||
<div class="row header-row">
|
||||
<div class="col-xs-1 centered" ng-show="isSelectMode">
|
||||
<i class="fa text-danger pointer" ng-class="selectedAll ? 'fa-check-square-o' : 'fa-square-o'"
|
||||
ng-click="checkAll()"></i>
|
||||
</div>
|
||||
<div class="col-xs-11 main-header">
|
||||
<span class="form-inline text-right pull-right">
|
||||
<!-- clear all filters -->
|
||||
<span class="sort-spacer pointer" ng-click="filter.reset()"
|
||||
ng-if="filter.areFiltersSet()" ng-disabled="isSelectMode"
|
||||
ng-class="{'disabled': isSelectMode}">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
<translate>Filter</translate>
|
||||
</span>
|
||||
<!-- boolean Filters -->
|
||||
<span ng-repeat="(name, booleanFilter) in filter.booleanFilters" uib-dropdown>
|
||||
<span class="pointer" id="dropdown{{ name }}" uib-dropdown-toggle
|
||||
ng-class="{'bold': booleanFilter.value !== undefined, 'disabled': isSelectMode}"
|
||||
ng-disabled="isSelectMode">
|
||||
{{ booleanFilter.displayName }}
|
||||
<span class="caret"></span>
|
||||
</span>
|
||||
<ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdown{{ name }}">
|
||||
<li>
|
||||
<a href ng-click="booleanFilter.value = (booleanFilter.value ? undefined : true); filter.save();">
|
||||
<i class="fa" ng-class="{'fa-check': booleanFilter.value === true}"></i>
|
||||
{{ booleanFilter.choiceYes }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href ng-click="booleanFilter.value = (booleanFilter.value === false) ? undefined : false; filter.save();">
|
||||
<i class="fa" ng-class="{'fa-check': booleanFilter.value === false}"></i>
|
||||
{{ booleanFilter.choiceNo }}
|
||||
</a>
|
||||
</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.filterString" class="form-control"
|
||||
placeholder="{{ 'Search' | translate}}" ng-disabled="isSelectMode"
|
||||
ng-change="filter.save()">
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<!-- show all selected multiselectoptions -->
|
||||
<span>
|
||||
<!-- for all boolean Filters -->
|
||||
<span ng-repeat="(name, booleanFilter) in filter.booleanFilters"
|
||||
ng-hide="booleanFilter.value === undefined"
|
||||
class="pointer spacer-left-lg"
|
||||
ng-click="booleanFilter.value = undefined; filter.save();"
|
||||
ng-class="{'disabled': isSelectMode}">
|
||||
<span class="nobr">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
{{ booleanFilter.value ? booleanFilter.choiceYes : booleanFilter.choiceNo | translate }}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- main table -->
|
||||
<div class="row data-row" ng-mouseover="item.hover=true"
|
||||
ng-mouseleave="item.hover=false"
|
||||
ng-class="{'projected': item.isProjected().length}"
|
||||
ng-repeat="item in itemsFiltered = (items
|
||||
| osFilter: filter.filterString : filter.getObjectQueryString
|
||||
| filter: {closed: filter.booleanFilters.closed.value}
|
||||
| filter: {is_hidden: filter.booleanFilters.is_hidden.value}
|
||||
| limitTo : itemsPerPage : limitBegin)">
|
||||
|
||||
<!-- select column -->
|
||||
<div ng-show="isSelectMode" os-perms="agenda.can_manage" class="col-xs-1 centered">
|
||||
<i class="fa text-danger pointer" ng-click="item.selected=!item.selected"
|
||||
ng-class="item.selected ? 'fa-check-square-o' : 'fa-square-o'"></i>
|
||||
</div>
|
||||
<!-- projector column -->
|
||||
<td ng-show="!isDeleteMode" os-perms="core.can_manage_projector">
|
||||
<div class="col-xs-1 centered projector" os-perms="core.can_manage_projector">
|
||||
<div class="btn-group" style="min-width:{{ (item.hasSubitems(items) || projectors.length > 1) ? '54' : '34' }}px;" uib-dropdown
|
||||
uib-tooltip="{{ 'Projector' | translate }} {{ item.isProjected(item.tree)[0] || '' }}"
|
||||
tooltip-enable="item.isProjected(item.tree).length">
|
||||
@ -199,83 +255,88 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- delete selection column -->
|
||||
<td ng-show="isDeleteMode" os-perms="agenda.can_manage" class="deleteColumn">
|
||||
<input type="checkbox" ng-model="item.selected">
|
||||
<!-- agenda data columns -->
|
||||
<td ng-if="!item.quickEdit" ng-mouseover="item.hover=true" ng-mouseleave="item.hover=false"
|
||||
style="padding-left: calc(8px + {{ item.parentCount }}*15px)">
|
||||
</div>
|
||||
|
||||
<!-- main content column -->
|
||||
<div class="col-xs-6 content" style="padding-left: calc({{ item.parentCount }}*15px)">
|
||||
<div class="spacer-right">
|
||||
<strong>
|
||||
<a ui-sref="{{ getUpdateStatePrefix(item) }}.detail({id: item.content_object.id})" ng-show="isAllowedToSeeOpenLink(item)">
|
||||
<i class="fa fa-ban fa-lg" ng-style="{'visibility': item.is_hidden ? 'visible' : 'hidden'}"
|
||||
title="{{ 'Internal item' | translate }}"></i>
|
||||
</strong>
|
||||
</div>
|
||||
<div>
|
||||
<!-- ID and title -->
|
||||
<div>
|
||||
<strong>
|
||||
<a class="title" ui-sref="{{ getUpdateStatePrefix(item) }}.detail({id: item.content_object.id})" ng-show="isAllowedToSeeOpenLink(item)">
|
||||
{{ item.getListViewTitle() }}
|
||||
</a>
|
||||
<span ng-hide="isAllowedToSeeOpenLink(item)">
|
||||
<span class="title" ng-hide="isAllowedToSeeOpenLink(item)">
|
||||
{{ item.getListViewTitle() }}
|
||||
</span>
|
||||
</strong>
|
||||
<span ng-if="item.is_hidden" title="{{ 'Internal item' | translate }}"><i class="fa fa-ban"></i></span>
|
||||
<div ng-if="item.comment">
|
||||
<small><i class="fa fa-info-circle"></i> {{ item.comment }}</small>
|
||||
</div>
|
||||
<div os-perms="agenda.can_see" class="hoverActions" ng-class="{'hiddenDiv': !item.hover}">
|
||||
<!-- hover menu -->
|
||||
<div os-perms="agenda.can_see" ng-class="{'hiddenDiv': !item.hover}">
|
||||
<small>
|
||||
<a ui-sref="agenda.item.detail({id: item.id})" translate>List of speakers</a>
|
||||
<span os-perms="agenda.can_manage"> |
|
||||
<span os-perms="agenda.can_manage"> ·
|
||||
<a ui-sref="{{ getUpdateStatePrefix(item) }}.detail.update({id: item.content_object.id})"
|
||||
translate>Edit</a> |
|
||||
<a href="" ng-click="item.quickEdit=true" translate>QuickEdit</a> |
|
||||
translate>Edit</a> ·
|
||||
<a href="" class="text-danger"
|
||||
ng-bootbox-confirm="{{ 'Are you sure you want to delete this entry?' | translate }}<br>
|
||||
<b>{{ item.getTitle() }}</b>"
|
||||
ng-bootbox-confirm-action="deleteRelatedItem(item)" translate>Delete</a>
|
||||
</span>
|
||||
</small>
|
||||
</div>
|
||||
<td ng-show="!item.quickEdit" os-perms="agenda.can_see_hidden_items" class="optional">
|
||||
{{ item.duration | osMinutesToTime }}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- additional content column -->
|
||||
<div class="col-xs-4 content" ng-style="{'width': isSelectMode ? 'calc(50% - 120px)' : 'calc(50% - 70px)'}">
|
||||
<div style="width: 60%;" class="optional">
|
||||
<small>
|
||||
<div ng-style="{'visibility': (item.duration || item.hover) ? 'visible' : 'hidden'}">
|
||||
<div class="popover-wrapper">
|
||||
<i class="fa fa-clock-o"></i>
|
||||
<span editable-text="item.durationText" e-placeholder="hh:mm"
|
||||
onshow="generateDurationText(item)" onaftersave="setDurationText(item)">
|
||||
{{ (item.duration | osMinutesToTime) || ('Set duration...' | translate) }}
|
||||
<span ng-if="item.duration" translate-comment="'h' means time in hours" translate>h</span>
|
||||
<td ng-if="!item.quickEdit" class="optional">
|
||||
<span os-perms="!agenda.can_manage">
|
||||
<i ng-if="item.closed" class="fa fa-check-square-o"></i>
|
||||
</span>
|
||||
<input os-perms="agenda.can_manage" type="checkbox" ng-model="item.closed" ng-change="save(item);">
|
||||
<!-- quickEdit columns -->
|
||||
<td ng-show="item.quickEdit" os-perms="agenda.can_manage" colspan="3">
|
||||
<h4>{{ item.getTitle() }} <span class="text-muted">– QuickEdit</span></h4>
|
||||
<div uib-alert ng-show="alert.show" ng-class="'alert-' + (alert.type || 'warning')" ng-click="alert={}" close="alert={}">
|
||||
{{ alert.msg }}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<label for="inputItemNumber" translate>Item number</label>
|
||||
<input type="text" ng-model="item.item_number" class="form-control input-sm" id="inputItemNumber">
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<label for="inputComment" translate>Comment</label>
|
||||
<input type="text" ng-model="item.comment" class="form-control input-sm" id="inputComment">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<!-- item type: AGENDA_ITEM = 1, HIDDEN_ITEM = 2 -->
|
||||
<input type="checkbox" ng-model="item.type" ng-true-value="1" ng-false-value="2">
|
||||
<translate>Show as agenda item</translate>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<label for="inputDuration" translate>Duration</label>
|
||||
<input type="text" ng-model="item.duration" placeholder="hh:mm" hour-min-format
|
||||
class="form-control input-sm" id="inputDuration">
|
||||
<div ng-style="{'visibility': (item.comment || item.hover) ? 'visible' : 'hidden'}">
|
||||
<div class="popover-wrapper">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
<span editable-text="item.comment" onaftersave="save(item)">{{ item.comment || ('Set comment...' | translate)}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="spacer">
|
||||
<button ng-click="cancelQuickEdit(item)" class="btn btn-default pull-left" translate>
|
||||
Cancel
|
||||
</button>
|
||||
<button ng-click="save(item)" class="btn btn-primary" translate>
|
||||
Update
|
||||
</button>
|
||||
<a ui-sref="{{ item.content_object.collection.replace('/','.') }}.detail.update({id: item.content_object.id})"
|
||||
class="pull-right"><translate>Edit ...</translate></a>
|
||||
</small>
|
||||
</div>
|
||||
</table>
|
||||
<div style="width: 40%;" class="pull-right">
|
||||
<div os-perms="agenda.can_manage">
|
||||
<div class="pointer nobr" ng-click="item.type = (item.type == 1) ? 2 : 1; save(item);" ng-show="item.hover || item.is_hidden">
|
||||
<i class="fa" ng-class="item.is_hidden ? 'fa-check-square-o' : 'fa-square-o'"></i>
|
||||
<span class="spacer-left" translate>Internal item</span>
|
||||
</div>
|
||||
<div class="pointer nobr" ng-click="item.closed = !item.closed; save(item);" ng-show="item.hover || item.closed">
|
||||
<i class="fa" ng-class="item.closed ? 'fa-check-square-o' : 'fa-square-o'"></i>
|
||||
<span class="spacer-left" translate>Done</span>
|
||||
</div>
|
||||
</div>
|
||||
<div os-perms="!agenda.can_manage" ng-if="item.closed">
|
||||
<i class="fa fa-check-square-o"></i>
|
||||
<span class="spacer-left" translate>Done</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- data row -->
|
||||
|
||||
</div> <!-- container -->
|
||||
|
||||
<ul uib-pagination
|
||||
ng-show="itemsFiltered.length > itemsPerPage"
|
||||
total-items="itemsFiltered.length"
|
||||
@ -288,4 +349,4 @@
|
||||
first-text="«"
|
||||
last-text="»">
|
||||
</ul>
|
||||
</div>
|
||||
</div> <!-- details -->
|
||||
|
@ -303,9 +303,10 @@ angular.module('OpenSlidesApp.assignments.site', [
|
||||
'User',
|
||||
'osTableFilter',
|
||||
'osTableSort',
|
||||
'gettext',
|
||||
function($scope, ngDialog, AssignmentForm, Assignment, Tag, Agenda, phases, Projector, ProjectionDefault,
|
||||
gettextCatalog, AssignmentContentProvider, AssignmentCatalogContentProvider, PdfMakeDocumentProvider,
|
||||
User, osTableFilter, osTableSort) {
|
||||
User, osTableFilter, osTableSort, gettext) {
|
||||
Assignment.bindAll({}, $scope, 'assignments');
|
||||
Tag.bindAll({}, $scope, 'tags');
|
||||
$scope.$watch(function () {
|
||||
@ -320,11 +321,14 @@ angular.module('OpenSlidesApp.assignments.site', [
|
||||
$scope.alert = {};
|
||||
|
||||
// Filtering
|
||||
$scope.filter = osTableFilter.createInstance();
|
||||
$scope.filter = osTableFilter.createInstance('AssignmentTableFilter');
|
||||
|
||||
if (!$scope.filter.existsCookie()) {
|
||||
$scope.filter.multiselectFilters = {
|
||||
tag: [],
|
||||
phase: [],
|
||||
};
|
||||
}
|
||||
$scope.filter.propertyList = ['title', 'description'];
|
||||
$scope.filter.propertyFunctionList = [
|
||||
function (assignment) {
|
||||
|
@ -162,7 +162,8 @@
|
||||
<span class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-search"></i></span>
|
||||
<input type="text" ng-model="filter.filterString" class="form-control"
|
||||
placeholder="{{ 'Search' | translate}}">
|
||||
placeholder="{{ 'Search' | translate}}" ng-disable="isSelectMode"
|
||||
ng-change="filter.save()">
|
||||
</span>
|
||||
</span>
|
||||
|
||||
@ -224,7 +225,9 @@
|
||||
<!-- title and phase -->
|
||||
<div>
|
||||
<strong>
|
||||
<a ui-sref="assignments.assignment.detail({id: assignment.id})">{{ assignment.title }}</a>
|
||||
<a ui-sref="assignments.assignment.detail({id: assignment.id})">
|
||||
{{ assignment.title }}
|
||||
</a>
|
||||
</strong>
|
||||
<span style="padding: 5px;" ng-mouseover="assignment.phaseHover=true" ng-mouseleave="assignment.phaseHover=false">
|
||||
<span class="label" ng-class="{'label-primary': assignment.phase == 0,
|
||||
|
@ -978,7 +978,7 @@ img {
|
||||
|
||||
.os-table .title {
|
||||
font-size: 115%;
|
||||
margin-right: 5px;
|
||||
margin-right: 3px;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
@ -1370,6 +1370,21 @@ img {
|
||||
animation: fade-out 0.25s linear;
|
||||
}
|
||||
|
||||
/* xeditable */
|
||||
.editable-click {
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.editable-click:hover {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.popover-wrapper .editable-hide {
|
||||
display: inline !important;
|
||||
}
|
||||
|
||||
@keyframes fade-out {
|
||||
0% { opacity: 1; background: none; }
|
||||
100% { opacity: 0; background: none; }
|
||||
|
@ -13,12 +13,14 @@ angular.module('OpenSlidesApp.core.site', [
|
||||
'formlyBootstrap',
|
||||
'localytics.directives',
|
||||
'ngBootbox',
|
||||
'ngCookies',
|
||||
'ngDialog',
|
||||
'ngFileSaver',
|
||||
'ngMessages',
|
||||
'ngCsvImport',
|
||||
'ui.tinymce',
|
||||
'luegg.directives',
|
||||
'xeditable',
|
||||
])
|
||||
|
||||
// Can be used to find out if the projector or the side is used
|
||||
@ -102,11 +104,25 @@ angular.module('OpenSlidesApp.core.site', [
|
||||
.run([
|
||||
'loadGlobalData',
|
||||
'operator',
|
||||
function(loadGlobalData, operator) {
|
||||
function (loadGlobalData, operator) {
|
||||
operator.onOperatorChange(loadGlobalData);
|
||||
}
|
||||
])
|
||||
|
||||
.run([
|
||||
'editableOptions',
|
||||
'gettext',
|
||||
function (editableOptions, gettext) {
|
||||
editableOptions.theme = 'bs3';
|
||||
editableOptions.cancelButtonAriaLabel = gettext('Cancel');
|
||||
editableOptions.cancelButtonTitle = gettext('Cancel');
|
||||
editableOptions.clearButtonAriaLabel = gettext('Clear');
|
||||
editableOptions.clearButtonTitle = gettext('Clear');
|
||||
editableOptions.submitButtonAriaLabel = gettext('Submit');
|
||||
editableOptions.submitButtonTitle = gettext('Submit');
|
||||
}
|
||||
])
|
||||
|
||||
.config([
|
||||
'mainMenuProvider',
|
||||
'gettext',
|
||||
@ -355,13 +371,26 @@ angular.module('OpenSlidesApp.core.site', [
|
||||
* - propertyList, propertyFunctionList, propertyDict: See function getObjectQueryString
|
||||
*/
|
||||
.factory('osTableFilter', [
|
||||
function () {
|
||||
var createInstance = function () {
|
||||
'$cookies',
|
||||
function ($cookies) {
|
||||
var createInstance = function (cookieName) {
|
||||
var self = {
|
||||
multiselectFilters: {},
|
||||
booleanFilters: {},
|
||||
filterString: '',
|
||||
};
|
||||
var existsCookie = function () {
|
||||
return $cookies.getObject(cookieName);
|
||||
};
|
||||
var cookie = existsCookie();
|
||||
if (cookie) {
|
||||
self = cookie;
|
||||
}
|
||||
|
||||
self.existsCookie = existsCookie;
|
||||
self.save = function () {
|
||||
$cookies.putObject(cookieName, self);
|
||||
};
|
||||
self.areFiltersSet = function () {
|
||||
var areFiltersSet = _.find(self.multiselectFilters, function (filterList) {
|
||||
return filterList.length > 0;
|
||||
@ -380,6 +409,7 @@ angular.module('OpenSlidesApp.core.site', [
|
||||
self.booleanFilters[filter].value = undefined;
|
||||
});
|
||||
self.filterString = '';
|
||||
self.save();
|
||||
};
|
||||
self.operateMultiselectFilter = function (filter, id, danger) {
|
||||
if (!danger) {
|
||||
@ -390,6 +420,7 @@ angular.module('OpenSlidesApp.core.site', [
|
||||
// add id
|
||||
self.multiselectFilters[filter].push(id);
|
||||
}
|
||||
self.save();
|
||||
}
|
||||
};
|
||||
/* Three things are could be given to create the query string:
|
||||
|
@ -99,7 +99,9 @@ angular.module('OpenSlidesApp.mediafiles.site', ['ngFileUpload', 'OpenSlidesApp.
|
||||
updatePresentedMediafiles();
|
||||
|
||||
// Filtering
|
||||
$scope.filter = osTableFilter.createInstance();
|
||||
$scope.filter = osTableFilter.createInstance('MediafilesTableFilter');
|
||||
|
||||
if (!$scope.filter.existsCookie()) {
|
||||
$scope.filter.booleanFilters = {
|
||||
isPrivate: {
|
||||
value: undefined,
|
||||
@ -115,6 +117,7 @@ angular.module('OpenSlidesApp.mediafiles.site', ['ngFileUpload', 'OpenSlidesApp.
|
||||
choiceNo: gettext('Is no PDF file'),
|
||||
},
|
||||
};
|
||||
}
|
||||
$scope.filter.propertyList = ['title_or_filename'];
|
||||
$scope.filter.propertyFunctionList = [
|
||||
function (mediafile) {return mediafile.uploader.get_short_name();},
|
||||
|
@ -170,13 +170,13 @@
|
||||
</span>
|
||||
<ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdown{{ name }}">
|
||||
<li>
|
||||
<a href ng-click="booleanFilter.value = (booleanFilter.value ? undefined : true)">
|
||||
<a href ng-click="booleanFilter.value = (booleanFilter.value ? undefined : true); filter.save();">
|
||||
<i class="fa" ng-class="{'fa-check': booleanFilter.value === true}"></i>
|
||||
{{ booleanFilter.choiceYes }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href ng-click="booleanFilter.value = (booleanFilter.value === false) ? undefined : false">
|
||||
<a href ng-click="booleanFilter.value = (booleanFilter.value === false) ? undefined : false; filter.save();">
|
||||
<i class="fa" ng-class="{'fa-check': booleanFilter.value === false}"></i>
|
||||
{{ booleanFilter.choiceNo }}
|
||||
</a>
|
||||
@ -209,7 +209,8 @@
|
||||
<span class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-search"></i></span>
|
||||
<input type="text" ng-model="filter.filterString" class="form-control"
|
||||
placeholder="{{ 'Search' | translate}}" ng-disabled="isSelectMode">
|
||||
placeholder="{{ 'Search' | translate}}" ng-disabled="isSelectMode"
|
||||
ng-change="filter.save()">
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
@ -219,7 +220,7 @@
|
||||
<span ng-repeat="(name, booleanFilter) in filter.booleanFilters"
|
||||
ng-hide="booleanFilter.value === undefined"
|
||||
class="pointer spacer-left-lg"
|
||||
ng-click="booleanFilter.value = undefined;"
|
||||
ng-click="booleanFilter.value = undefined; filter.save();"
|
||||
ng-class="{'disabled': isSelectMode}">
|
||||
<span class="nobr">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
|
@ -822,13 +822,16 @@ angular.module('OpenSlidesApp.motions.site', [
|
||||
$scope.alert = {};
|
||||
|
||||
// Filtering
|
||||
$scope.filter = osTableFilter.createInstance();
|
||||
$scope.filter = osTableFilter.createInstance('MotionTableFilter');
|
||||
|
||||
if (!$scope.filter.existsCookie()) {
|
||||
$scope.filter.multiselectFilters = {
|
||||
state: [],
|
||||
category: [],
|
||||
motionBlock: [],
|
||||
tag: []
|
||||
};
|
||||
}
|
||||
$scope.filter.propertyList = ['identifier', 'origin'];
|
||||
$scope.filter.propertyFunctionList = [
|
||||
function (motion) {return motion.getTitle();},
|
||||
@ -907,7 +910,7 @@ angular.module('OpenSlidesApp.motions.site', [
|
||||
|
||||
// Use this methon instead of Motion.save(), because otherwise
|
||||
// you have to provide always a title and a text
|
||||
var save = function (motion) {
|
||||
$scope.save = function (motion) {
|
||||
motion.title = motion.getTitle(-1);
|
||||
motion.text = motion.getText(-1);
|
||||
motion.reason = motion.getReason(-1);
|
||||
@ -922,7 +925,7 @@ angular.module('OpenSlidesApp.motions.site', [
|
||||
} else {
|
||||
motion.tags_id.push(tag.id);
|
||||
}
|
||||
save(motion);
|
||||
$scope.save(motion);
|
||||
};
|
||||
$scope.toggleCategory = function (motion, category) {
|
||||
if (motion.category_id == category.id) {
|
||||
@ -930,7 +933,7 @@ angular.module('OpenSlidesApp.motions.site', [
|
||||
} else {
|
||||
motion.category_id = category.id;
|
||||
}
|
||||
save(motion);
|
||||
$scope.save(motion);
|
||||
};
|
||||
$scope.toggleMotionBlock = function (motion, block) {
|
||||
if (motion.motion_block_id == block.id) {
|
||||
@ -938,7 +941,7 @@ angular.module('OpenSlidesApp.motions.site', [
|
||||
} else {
|
||||
motion.motion_block_id = block.id;
|
||||
}
|
||||
save(motion);
|
||||
$scope.save(motion);
|
||||
};
|
||||
|
||||
// open new/edit dialog
|
||||
|
@ -241,7 +241,8 @@
|
||||
<span class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-search"></i></span>
|
||||
<input type="text" ng-model="filter.filterString" class="form-control"
|
||||
placeholder="{{ 'Search' | translate}}" ng-disabled="isSelectMode">
|
||||
placeholder="{{ 'Search' | translate}}" ng-disabled="isSelectMode"
|
||||
ng-change="filter.save()">
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
@ -506,7 +507,11 @@
|
||||
<!-- Origin -->
|
||||
<div ng-if="motion.origin">
|
||||
<i class="fa fa-share spacer-right" uib-tooltip="{{ 'Origin' | translate }}"></i>
|
||||
<div class="popover-wrapper">
|
||||
<span editable-text="motion.origin" onaftersave="save(motion)">
|
||||
{{ motion.origin | limitTo:25 }}{{ motion.origin.length > 25 ? '...' : '' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</small>
|
||||
</div>
|
||||
|
@ -510,16 +510,12 @@ angular.module('OpenSlidesApp.users.site', [
|
||||
$scope.alert = {};
|
||||
|
||||
// Filtering
|
||||
$scope.filter = osTableFilter.createInstance();
|
||||
$scope.filter = osTableFilter.createInstance('UserTableFilter');
|
||||
|
||||
if (!$scope.filter.existsCookie()) {
|
||||
$scope.filter.multiselectFilters = {
|
||||
group: [],
|
||||
};
|
||||
$scope.filter.propertyList = ['first_name', 'last_name', 'title', 'number', 'comment', 'structure_level'];
|
||||
$scope.filter.propertyDict = {
|
||||
'groups_id' : function (group_id) {
|
||||
return Group.get(group_id).name;
|
||||
},
|
||||
};
|
||||
$scope.filter.booleanFilters = {
|
||||
isPresent: {
|
||||
value: undefined,
|
||||
@ -543,6 +539,13 @@ angular.module('OpenSlidesApp.users.site', [
|
||||
},
|
||||
|
||||
};
|
||||
}
|
||||
$scope.filter.propertyList = ['first_name', 'last_name', 'title', 'number', 'comment', 'structure_level'];
|
||||
$scope.filter.propertyDict = {
|
||||
'groups_id' : function (group_id) {
|
||||
return Group.get(group_id).name;
|
||||
},
|
||||
};
|
||||
$scope.getItemId = {
|
||||
group: function (user) {return user.groups_id;},
|
||||
};
|
||||
|
@ -181,13 +181,13 @@
|
||||
</span>
|
||||
<ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdown{{ name }}">
|
||||
<li>
|
||||
<a href ng-click="booleanFilter.value = (booleanFilter.value ? undefined : true)">
|
||||
<a href ng-click="booleanFilter.value = (booleanFilter.value ? undefined : true); filter.save();">
|
||||
<i class="fa" ng-class="{'fa-check': booleanFilter.value === true}"></i>
|
||||
{{ booleanFilter.choiceYes }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href ng-click="booleanFilter.value = (booleanFilter.value === false) ? undefined : false">
|
||||
<a href ng-click="booleanFilter.value = (booleanFilter.value === false) ? undefined : false; filter.save();">
|
||||
<i class="fa" ng-class="{'fa-check': booleanFilter.value === false}"></i>
|
||||
{{ booleanFilter.choiceNo }}
|
||||
</a>
|
||||
@ -220,7 +220,8 @@
|
||||
<span class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-search"></i></span>
|
||||
<input type="text" ng-model="filter.filterString" class="form-control" ng-model-options="{debounce: 500}"
|
||||
placeholder="{{ 'Search' | translate}}" ng-disabled="isSelectMode">
|
||||
placeholder="{{ 'Search' | translate}}" ng-disabled="isSelectMode"
|
||||
ng-change="filter.save()">
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
@ -245,7 +246,7 @@
|
||||
<span ng-repeat="(name, booleanFilter) in filter.booleanFilters"
|
||||
ng-hide="booleanFilter.value === undefined"
|
||||
class="pointer spacer-left-lg"
|
||||
ng-click="booleanFilter.value = undefined;"
|
||||
ng-click="booleanFilter.value = undefined; filter.save();"
|
||||
ng-class="{'disabled': isSelectMode}">
|
||||
<span class="nobr">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
@ -289,7 +290,14 @@
|
||||
<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"> <!-- user number -->
|
||||
<div ng-if="user.number" editable-text="user.number" onaftersave="save(user)">
|
||||
<translate translate-comment="abbreviation for number">No.</translate> {{ user.number }}
|
||||
</div>
|
||||
</div>
|
||||
<div os-perms="!users.can_manage" ng-if="user.number">
|
||||
<translate translate-comment="abbreviation for number">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> ·
|
||||
@ -345,19 +353,41 @@
|
||||
... [+{{ 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 os-perms="users.can_manage" ng-show="user.structure_level || user.hover">
|
||||
<div class="popover-wrapper" uib-tooltip="{{ 'Structure level' | translate }}" tooltip-placement="top-left">
|
||||
<i class="fa fa-flag"></i>
|
||||
<span editable-text="user.structure_level" onaftersave="save(user)">
|
||||
{{ user.structure_level || ('Set structure level...' | translate)}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div os-perms="!users.can_manage">
|
||||
<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>
|
||||
|
||||
<div os-perms="users.can_manage" ng-show="user.comment || user.hover">
|
||||
<div class="popover-wrapper" uib-tooltip="{{ 'Comment' | translate }}" tooltip-placement="top-left">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
<span editable-text="user.comment" onaftersave="save(user)">
|
||||
{{ user.comment || ('Set comment...' | translate)}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div os-perms="!users.can_manage">
|
||||
<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>
|
||||
</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 nobr" ng-click="user.is_present = !user.is_present; save(user);">
|
||||
<span class="pointer nobr" ng-click="user.is_present = !user.is_present; save(user);" ng-show="user.hover || user.is_present">
|
||||
<i class="fa" ng-class="user.is_present ? 'fa-check-square-o' : 'fa-square-o'"></i>
|
||||
<span class="spacer-left" translate>Present</span>
|
||||
</span>
|
||||
|
Loading…
Reference in New Issue
Block a user