Improved csv import for agenda and users.
- Fix users list.
This commit is contained in:
parent
0701838bc8
commit
31c320f9ef
@ -262,14 +262,15 @@ angular.module('OpenSlidesApp.agenda.site', ['OpenSlidesApp.agenda'])
|
||||
|
||||
.controller('AgendaImportCtrl', [
|
||||
'$scope',
|
||||
'gettext',
|
||||
'Agenda',
|
||||
'Customslide',
|
||||
function($scope, Agenda, Customslide) {
|
||||
function($scope, gettext, Agenda, Customslide) {
|
||||
// import from textarea
|
||||
$scope.importByLine = function () {
|
||||
$scope.items = $scope.itemlist[0].split("\n");
|
||||
$scope.titleItems = $scope.itemlist[0].split("\n");
|
||||
$scope.importcounter = 0;
|
||||
$scope.items.forEach(function(title) {
|
||||
$scope.titleItems.forEach(function(title) {
|
||||
var item = {title: title};
|
||||
// TODO: create all items in bulk mode
|
||||
Customslide.create(item).then(
|
||||
@ -280,29 +281,63 @@ angular.module('OpenSlidesApp.agenda.site', ['OpenSlidesApp.agenda'])
|
||||
});
|
||||
};
|
||||
|
||||
// import from csv file
|
||||
// *** CSV import ***
|
||||
// set initial data for csv import
|
||||
$scope.items = []
|
||||
$scope.separator = ',';
|
||||
$scope.encoding = 'UTF-8';
|
||||
$scope.encodingOptions = ['UTF-8', 'ISO-8859-1'];
|
||||
$scope.csv = {
|
||||
content: null,
|
||||
header: true,
|
||||
separator: ',',
|
||||
headerVisible: false,
|
||||
separator: $scope.separator,
|
||||
separatorVisible: false,
|
||||
encoding: $scope.encoding,
|
||||
encodingVisible: false,
|
||||
result: null
|
||||
};
|
||||
$scope.importByCSV = function (result) {
|
||||
var obj = JSON.parse(JSON.stringify(result));
|
||||
$scope.csvimporting = true;
|
||||
$scope.csvlines = Object.keys(obj).length;
|
||||
$scope.csvimportcounter = 0;
|
||||
for (var i = 0; i < obj.length; i++) {
|
||||
var item = {};
|
||||
item.title = obj[i].title;
|
||||
item.text = obj[i].text;
|
||||
// TODO: save also 'duration' in related agenda item
|
||||
// set csv file encoding
|
||||
$scope.setEncoding = function () {
|
||||
$scope.csv.encoding = $scope.encoding;
|
||||
};
|
||||
// set csv file encoding
|
||||
$scope.setSeparator = function () {
|
||||
$scope.csv.separator = $scope.separator;
|
||||
};
|
||||
// detect if csv file is loaded
|
||||
$scope.$watch('csv.result', function () {
|
||||
$scope.items = [];
|
||||
var quotionRe = /^"(.*)"$/;
|
||||
angular.forEach($scope.csv.result, function (item) {
|
||||
// title
|
||||
if (item.title) {
|
||||
item.title = item.title.replace(quotionRe, '$1');
|
||||
}
|
||||
if (!item.title) {
|
||||
item.importerror = true;
|
||||
item.title_error = gettext('Error: Title is required.');
|
||||
}
|
||||
// text
|
||||
if (item.text) {
|
||||
item.text = item.text.replace(quotionRe, '$1');
|
||||
}
|
||||
$scope.items.push(item);
|
||||
});
|
||||
});
|
||||
|
||||
// import from csv file
|
||||
$scope.import = function () {
|
||||
$scope.csvImporting = true;
|
||||
angular.forEach($scope.items, function (item) {
|
||||
if (!item.importerror) {
|
||||
Customslide.create(item).then(
|
||||
function(success) {
|
||||
$scope.csvimportcounter++;
|
||||
item.imported = true;
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
$scope.csvimported = true;
|
||||
};
|
||||
$scope.clear = function () {
|
||||
|
@ -11,7 +11,7 @@
|
||||
</div>
|
||||
|
||||
<div class="details">
|
||||
<h3 translate>Import by copy/paste</h3>
|
||||
<h2 translate>Import by copy/paste</h2>
|
||||
<p translate>Copy and paste your agenda item titles in this textbox.
|
||||
Keep each item in a single line.</p>
|
||||
|
||||
@ -23,71 +23,121 @@ Keep each item in a single line.</p>
|
||||
|
||||
<div class="clearfix">
|
||||
<button ng-click="importByLine()" class="btn btn-primary pull-left" translate>Import</button>
|
||||
<div class="col-xs-5" ng-if="items">
|
||||
<progressbar animate="false" type="success" max="items.length" value="importcounter">
|
||||
<i>{{ importcounter }} / {{ items.length }} {{ "imported" | translate }}</i>
|
||||
<div class="col-xs-5" ng-if="titleItems">
|
||||
<progressbar animate="false" type="success" max="titleItems.length" value="importcounter">
|
||||
<i>{{ importcounter }} / {{ titleItems.length }} {{ "imported" | translate }}</i>
|
||||
</progressbar>
|
||||
</div>
|
||||
</div>
|
||||
<div class="spacer">
|
||||
<a ng-if="importcounter > 0 && importcounter == titleItems.length" ui-sref="agenda.item.list"
|
||||
class="btn btn-default">
|
||||
<i class="fa fa-angle-double-left fa-lg"></i>
|
||||
<translate>Back to agenda</translate>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3 translate>Import by CSV file</h3>
|
||||
<p translate>Select a CSV file to import agenda items!
|
||||
<h2 translate>Import by CSV file</h2>
|
||||
<div class="block row">
|
||||
<div class="title">
|
||||
<h3 translate>Select a CSV file
|
||||
</div>
|
||||
<div class="block right import">
|
||||
<label class="label" for="inputSeparator" translate>Separator</label>
|
||||
<input type="text" ng-model="separator" ng-change="setSeparator()" ng-init="separator=separator" id="inputSeparator">
|
||||
<br>
|
||||
<label class="label" for="selectEncoding" translate>Encoding</label>
|
||||
<select ng-model="encoding" ng-options="enc as enc for enc in encodingOptions"
|
||||
ng-selected="setEncoding()" ng-init="encoding=encoding" id="selectEncoding"></select>
|
||||
<ng-csv-import
|
||||
content="csv.content"
|
||||
header="csv.header"
|
||||
header-visible="csv.headerVisible"
|
||||
separator="csv.separator"
|
||||
separator-visible="csv.separatorVisible"
|
||||
result="csv.result"
|
||||
encoding="csv.encoding"
|
||||
encoding-visible="csv.encodingVisible"></ng-csv-import>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p translate>Please note:</p>
|
||||
<ul><!--TODO: utf-8 encoding still required with angular-csv? -->
|
||||
<li><translate>Required comma separated values</translate>:<br>
|
||||
<code translate>'title, text'</code>
|
||||
<li translate>Text and duration are optional and may be empty.
|
||||
<li translate>The header in first line is required.
|
||||
<li translate>Required CSV file encoding is UTF-8.
|
||||
<h4 translate>Please note:</h4>
|
||||
<ul>
|
||||
<li><translate>Required comma or semicolon separated values with these column header names in the first row</translate>:<br>
|
||||
<code>'title, text'</code>
|
||||
<li translate>Text is optional and may be empty.
|
||||
<li translate>Only double quotes are accepted as text delimiter (no single quotes).
|
||||
<li><a href="https://github.com/OpenSlides/OpenSlides/wiki/CSV-Import" translate>
|
||||
Use the CSV example file from OpenSlides Wiki.
|
||||
</a>
|
||||
</ul>
|
||||
|
||||
<ng-csv-import
|
||||
content="csv.content"
|
||||
class="import"
|
||||
header="csv.header"
|
||||
separator="csv.separator"
|
||||
result="csv.result"></ng-csv-import>
|
||||
|
||||
<div ng-if="csv.result" class="col-sm-6 well">
|
||||
<div ng-if="csv.result">
|
||||
<h3 translate>Preview</h3>
|
||||
<table class="table table-striped table-bordered table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<th>#
|
||||
<th translate>Title
|
||||
<th translate>Text
|
||||
<th translate>Duration</th>
|
||||
<tbody ng-repeat="item in csv.result">
|
||||
<th translate>Text</th>
|
||||
<tbody ng-repeat="item in items">
|
||||
<tr>
|
||||
<td class="minimum"
|
||||
ng-class="{ 'text-danger': item.importerror, 'text-success': item.imported }">
|
||||
<span ng-if="item.importerror">
|
||||
<i class="fa fa-exclamation-triangle fa-lg"></i>
|
||||
</span>
|
||||
<span ng-if="!item.importerror && !item.imported">
|
||||
<i class="fa fa-check-circle-o fa-lg"></i>
|
||||
</span>
|
||||
<span ng-if="item.imported">
|
||||
<i class="fa fa-check-circle fa-lg"></i>
|
||||
</span>
|
||||
<td>{{ $index + 1 }}
|
||||
<td>{{ item.title }}
|
||||
<td>{{ item.text }}
|
||||
<td>{{ item.duration }}
|
||||
<td ng-class="{ 'text-danger': item.title_error }">
|
||||
<span ng-if="item.title_error" title="{{ item.title_error | translate }}">
|
||||
<i class="fa fa-exclamation-triangle"></i>
|
||||
</span>
|
||||
{{ item.title }}
|
||||
<td>{{ item.text | limitTo:80 }}{{ item.text.length > 80 ? '...' : '' }}
|
||||
</table>
|
||||
|
||||
<div class="clearfix">
|
||||
<button ng-if="!csvimporting" ng-click="importByCSV(csv.result)" class="btn btn-primary pull-left" translate>Import</button>
|
||||
<div ng-if="csvimporting">
|
||||
<progressbar animate="false" type="success" max="csvlines" value="csvimportcounter">
|
||||
<i>{{ csvimportcounter }} / {{ csvlines }} {{ "imported" | translate }}</i>
|
||||
</progressbar>
|
||||
<div class="text-danger">
|
||||
<div ng-repeat="item in itemsFailed = (items | filter:{importerror:true})"></div>
|
||||
<i class="fa fa-exclamation-triangle"></i>
|
||||
{{ itemsFailed.length }}
|
||||
<translate>agenda items will be not imported.</translate>
|
||||
</div>
|
||||
<a ng-if="csvimported" ui-sref="agenda.item.list" class="btn btn-sm btn-default">
|
||||
<i class="fa fa-angle-double-left fa-lg"></i>
|
||||
<translate>Back to agenda overview</translate>
|
||||
</a>
|
||||
<div>
|
||||
<div ng-repeat="item in itemsPassed = (items | filter:{importerror:false})"></div>
|
||||
<i class="fa fa-check-circle-o fa-lg"></i>
|
||||
{{ items.length - itemsFailed.length }}
|
||||
<translate>items will be imported.</translate>
|
||||
</div>
|
||||
<p>
|
||||
<div class="form-group">
|
||||
<button ng-if="!csvimporting" ng-click="clear()" class="btn btn-default" translate>
|
||||
Clear
|
||||
<div ng-repeat="item in itemsImported = (items | filter:{imported:true})"></div>
|
||||
<div ng-if="itemsImported.length > 0" class="text-success">
|
||||
<hr class="smallhr">
|
||||
<i class="fa fa-check-circle fa-lg"></i>
|
||||
{{ itemsImported.length }}
|
||||
<translate>items were successfully imported.</translate>
|
||||
</div>
|
||||
|
||||
<div class="spacer">
|
||||
<button ng-click="clear()" class="btn btn-default" translate>
|
||||
Clear preview
|
||||
</button>
|
||||
<button ng-if="!csvImporting" ng-click="import()" class="btn btn-primary" translate>
|
||||
Import {{ items.length - itemsFailed.length }} items
|
||||
</button>
|
||||
</div>
|
||||
<div class="spacer">
|
||||
<a ng-if="csvimported" ui-sref="agenda.item.list" class="btn btn-default">
|
||||
<i class="fa fa-angle-double-left fa-lg"></i>
|
||||
<translate>Back to agenda</translate>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -4,14 +4,6 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Project-Id-Version: \n"
|
||||
|
||||
#: users/static/templates/users/user-import.html:41
|
||||
msgid "'title, first_name, last_name, structure level, groups, comment, is active'"
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:41
|
||||
msgid "'title, text'"
|
||||
msgstr ""
|
||||
|
||||
#: assignments/static/templates/assignments/assignment-list.html:54
|
||||
msgid "--- Select phase ---"
|
||||
msgstr ""
|
||||
@ -20,7 +12,7 @@ msgstr ""
|
||||
msgid "--- Select state ---"
|
||||
msgstr ""
|
||||
|
||||
#: users/static/js/users/site.js:323
|
||||
#: users/static/js/users/site.js:348
|
||||
#: users/static/templates/users/user-detail-profile.html:39
|
||||
#: users/static/templates/users/user-detail.html:37
|
||||
msgid "About me"
|
||||
@ -77,7 +69,7 @@ msgstr ""
|
||||
msgid "Agenda item"
|
||||
msgstr ""
|
||||
|
||||
#: users/static/templates/users/user-import.html:45
|
||||
#: users/static/templates/users/user-import.html:74
|
||||
msgid ""
|
||||
"At least first name or last name have to be filled in. All\n"
|
||||
" other fields are optional and may be empty."
|
||||
@ -88,13 +80,11 @@ msgid "Attachment"
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-detail.html:6
|
||||
#: agenda/static/templates/agenda/item-import.html:139
|
||||
#: agenda/static/templates/agenda/item-import.html:36
|
||||
msgid "Back to agenda"
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:83
|
||||
msgid "Back to agenda overview"
|
||||
msgstr ""
|
||||
|
||||
#: motions/static/templates/motions/motion-import.html:136
|
||||
msgid "Back to motions overview"
|
||||
msgstr ""
|
||||
@ -118,7 +108,8 @@ msgstr ""
|
||||
msgid "Back to overview"
|
||||
msgstr ""
|
||||
|
||||
#: users/static/templates/users/user-import.html:93
|
||||
#: users/static/templates/users/user-import.html:166
|
||||
#: users/static/templates/users/user-import.html:36
|
||||
msgid "Back to users overview"
|
||||
msgstr ""
|
||||
|
||||
@ -167,12 +158,9 @@ msgstr ""
|
||||
msgid "Category"
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:88
|
||||
#: users/static/templates/users/user-import.html:98
|
||||
msgid "Clear"
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:129
|
||||
#: motions/static/templates/motions/motion-import.html:126
|
||||
#: users/static/templates/users/user-import.html:156
|
||||
msgid "Clear preview"
|
||||
msgstr ""
|
||||
|
||||
@ -185,9 +173,9 @@ msgid "Closed"
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-list.html:174
|
||||
#: users/static/js/users/site.js:316
|
||||
#: users/static/js/users/site.js:341
|
||||
#: users/static/templates/users/user-detail.html:45
|
||||
#: users/static/templates/users/user-import.html:72
|
||||
#: users/static/templates/users/user-import.html:94
|
||||
msgid "Comment"
|
||||
msgstr ""
|
||||
|
||||
@ -233,15 +221,15 @@ msgstr ""
|
||||
msgid "Default comment on the ballot paper"
|
||||
msgstr ""
|
||||
|
||||
#: users/static/templates/users/user-import.html:42
|
||||
#: users/static/templates/users/user-import.html:71
|
||||
msgid "Default groups"
|
||||
msgstr ""
|
||||
|
||||
#: users/static/js/users/site.js:304
|
||||
#: users/static/js/users/site.js:329
|
||||
msgid "Default password"
|
||||
msgstr ""
|
||||
|
||||
#: users/static/templates/users/user-import.html:43
|
||||
#: users/static/templates/users/user-import.html:72
|
||||
msgid "Delegate"
|
||||
msgstr ""
|
||||
|
||||
@ -287,7 +275,6 @@ msgstr ""
|
||||
msgid "Drag and drop items to change the order of the agenda. Your modification will be saved immediately."
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:65
|
||||
#: agenda/static/templates/agenda/item-list.html:114
|
||||
#: agenda/static/templates/agenda/item-list.html:184
|
||||
msgid "Duration"
|
||||
@ -379,7 +366,9 @@ msgstr ""
|
||||
msgid "Elections"
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:51
|
||||
#: motions/static/templates/motions/motion-import.html:23
|
||||
#: users/static/templates/users/user-import.html:52
|
||||
msgid "Encoding"
|
||||
msgstr ""
|
||||
|
||||
@ -387,6 +376,10 @@ msgstr ""
|
||||
msgid "English"
|
||||
msgstr ""
|
||||
|
||||
#: users/static/js/users/site.js:652
|
||||
msgid "Error: First or last name is required."
|
||||
msgstr ""
|
||||
|
||||
#: motions/static/js/motions/site.js:618
|
||||
msgid "Error: Identifier already exists."
|
||||
msgstr ""
|
||||
@ -395,6 +388,7 @@ msgstr ""
|
||||
msgid "Error: Text is required."
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/js/agenda/site.js:319
|
||||
#: motions/static/js/motions/site.js:628
|
||||
msgid "Error: Title is required."
|
||||
msgstr ""
|
||||
@ -422,9 +416,9 @@ msgstr ""
|
||||
msgid "Filter"
|
||||
msgstr ""
|
||||
|
||||
#: users/static/js/users/site.js:270
|
||||
#: users/static/js/users/site.js:295
|
||||
#: users/static/templates/users/user-detail-profile.html:26
|
||||
#: users/static/templates/users/user-import.html:68
|
||||
#: users/static/templates/users/user-import.html:90
|
||||
msgid "First name"
|
||||
msgstr ""
|
||||
|
||||
@ -440,10 +434,10 @@ msgstr ""
|
||||
msgid "Group"
|
||||
msgstr ""
|
||||
|
||||
#: users/static/js/users/site.js:291
|
||||
#: users/static/js/users/site.js:316
|
||||
#: users/static/templates/users/group-list.html:13
|
||||
#: users/static/templates/users/user-detail.html:33
|
||||
#: users/static/templates/users/user-import.html:71
|
||||
#: users/static/templates/users/user-import.html:93
|
||||
#: users/static/templates/users/user-list.html:10
|
||||
#: users/static/templates/users/user-list.html:109
|
||||
msgid "Groups"
|
||||
@ -482,11 +476,9 @@ msgid "Identifier, reason, submitter and category are optional and may be empty.
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:25
|
||||
#: agenda/static/templates/agenda/item-import.html:75
|
||||
#: agenda/static/templates/agenda/item-list.html:14
|
||||
#: motions/static/templates/motions/motion-list.html:18
|
||||
#: users/static/templates/users/user-import.html:25
|
||||
#: users/static/templates/users/user-import.html:85
|
||||
#: users/static/templates/users/user-list.html:14
|
||||
msgid "Import"
|
||||
msgstr ""
|
||||
@ -495,8 +487,8 @@ msgstr ""
|
||||
msgid "Import agenda items"
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:35
|
||||
#: users/static/templates/users/user-import.html:35
|
||||
#: agenda/static/templates/agenda/item-import.html:42
|
||||
#: users/static/templates/users/user-import.html:42
|
||||
msgid "Import by CSV file"
|
||||
msgstr ""
|
||||
|
||||
@ -513,15 +505,23 @@ msgstr ""
|
||||
msgid "Import participants"
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:132
|
||||
msgid "Import {{ items.length - itemsFailed.length }} items"
|
||||
msgstr ""
|
||||
|
||||
#: motions/static/templates/motions/motion-import.html:129
|
||||
msgid "Import {{ motions.length - motionsFailed.length }} motions"
|
||||
msgstr ""
|
||||
|
||||
#: users/static/js/users/site.js:722
|
||||
#: users/static/templates/users/user-import.html:159
|
||||
msgid "Import {{ users.length - usersFailed.length }} participants"
|
||||
msgstr ""
|
||||
|
||||
#: users/static/js/users/site.js:809
|
||||
msgid "Important: Please change your password!"
|
||||
msgstr ""
|
||||
|
||||
#: users/static/js/users/site.js:720
|
||||
#: users/static/js/users/site.js:807
|
||||
msgid "Installation was successfully."
|
||||
msgstr ""
|
||||
|
||||
@ -531,11 +531,12 @@ msgstr ""
|
||||
msgid "Invalid votes"
|
||||
msgstr ""
|
||||
|
||||
#: users/static/js/users/site.js:338
|
||||
#: users/static/js/users/site.js:363
|
||||
#: users/static/templates/users/user-import.html:95
|
||||
msgid "Is active"
|
||||
msgstr ""
|
||||
|
||||
#: users/static/js/users/site.js:331
|
||||
#: users/static/js/users/site.js:356
|
||||
#: users/static/templates/users/user-list.html:70
|
||||
msgid "Is present"
|
||||
msgstr ""
|
||||
@ -544,9 +545,9 @@ msgstr ""
|
||||
msgid "Item number"
|
||||
msgstr ""
|
||||
|
||||
#: users/static/js/users/site.js:277
|
||||
#: users/static/js/users/site.js:302
|
||||
#: users/static/templates/users/user-detail-profile.html:30
|
||||
#: users/static/templates/users/user-import.html:69
|
||||
#: users/static/templates/users/user-import.html:91
|
||||
msgid "Last name"
|
||||
msgstr ""
|
||||
|
||||
@ -692,7 +693,9 @@ msgstr ""
|
||||
msgid "Old speakers:"
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:71
|
||||
#: motions/static/templates/motions/motion-import.html:43
|
||||
#: users/static/templates/users/user-import.html:76
|
||||
msgid "Only double quotes are accepted as text delimiter (no single quotes)."
|
||||
msgstr ""
|
||||
|
||||
@ -743,9 +746,9 @@ msgstr ""
|
||||
msgid "Phase"
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:38
|
||||
#: agenda/static/templates/agenda/item-import.html:66
|
||||
#: motions/static/templates/motions/motion-import.html:38
|
||||
#: users/static/templates/users/user-import.html:38
|
||||
#: users/static/templates/users/user-import.html:67
|
||||
msgid "Please note:"
|
||||
msgstr ""
|
||||
|
||||
@ -766,9 +769,9 @@ msgstr ""
|
||||
msgid "Present"
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:58
|
||||
#: agenda/static/templates/agenda/item-import.html:78
|
||||
#: motions/static/templates/motions/motion-import.html:50
|
||||
#: users/static/templates/users/user-import.html:62
|
||||
#: users/static/templates/users/user-import.html:83
|
||||
msgid "Preview"
|
||||
msgstr ""
|
||||
|
||||
@ -838,20 +841,12 @@ msgstr ""
|
||||
msgid "Remove message"
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:44
|
||||
#: users/static/templates/users/user-import.html:48
|
||||
msgid "Required CSV file encoding is UTF-8."
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:68
|
||||
#: motions/static/templates/motions/motion-import.html:40
|
||||
#: users/static/templates/users/user-import.html:69
|
||||
msgid "Required comma or semicolon separated values with these column header names in the first row"
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:40
|
||||
#: users/static/templates/users/user-import.html:40
|
||||
msgid "Required comma separated values"
|
||||
msgstr ""
|
||||
|
||||
#: core/static/templates/core/projector-controls.html:99
|
||||
msgid "Reset countdown"
|
||||
msgstr ""
|
||||
@ -899,23 +894,17 @@ msgstr ""
|
||||
msgid "Select"
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:45
|
||||
#: motions/static/templates/motions/motion-import.html:17
|
||||
#: users/static/templates/users/user-import.html:46
|
||||
msgid "Select a CSV file"
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:36
|
||||
msgid "Select a CSV file to import agenda items!"
|
||||
msgstr ""
|
||||
|
||||
#: users/static/templates/users/user-import.html:36
|
||||
msgid "Select a CSV file to import users!"
|
||||
msgstr ""
|
||||
|
||||
#: motions/static/js/motions/site.js:214
|
||||
msgid "Select or search a category..."
|
||||
msgstr ""
|
||||
|
||||
#: users/static/js/users/site.js:297
|
||||
#: users/static/js/users/site.js:322
|
||||
msgid "Select or search a group..."
|
||||
msgstr ""
|
||||
|
||||
@ -947,7 +936,9 @@ msgstr ""
|
||||
msgid "Select or search an attachment..."
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:48
|
||||
#: motions/static/templates/motions/motion-import.html:20
|
||||
#: users/static/templates/users/user-import.html:49
|
||||
msgid "Separator"
|
||||
msgstr ""
|
||||
|
||||
@ -980,7 +971,7 @@ msgstr ""
|
||||
msgid "Special values"
|
||||
msgstr ""
|
||||
|
||||
#: users/static/templates/users/user-import.html:44
|
||||
#: users/static/templates/users/user-import.html:73
|
||||
msgid "Staff"
|
||||
msgstr ""
|
||||
|
||||
@ -1005,10 +996,10 @@ msgstr ""
|
||||
msgid "Stop current speaker"
|
||||
msgstr ""
|
||||
|
||||
#: users/static/js/users/site.js:284
|
||||
#: users/static/js/users/site.js:309
|
||||
#: users/static/templates/users/user-detail-profile.html:35
|
||||
#: users/static/templates/users/user-detail.html:31
|
||||
#: users/static/templates/users/user-import.html:70
|
||||
#: users/static/templates/users/user-import.html:92
|
||||
#: users/static/templates/users/user-list.html:104
|
||||
msgid "Structure level"
|
||||
msgstr ""
|
||||
@ -1052,7 +1043,7 @@ msgstr ""
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:64
|
||||
#: agenda/static/templates/agenda/item-import.html:85
|
||||
#: core/static/js/core/site.js:472
|
||||
#: motions/static/js/motions/site.js:169
|
||||
#: motions/static/templates/motions/motion-detail.html:232
|
||||
@ -1060,17 +1051,12 @@ msgstr ""
|
||||
msgid "Text"
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:42
|
||||
msgid "Text and duration are optional and may be empty."
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:43
|
||||
#: users/static/templates/users/user-import.html:47
|
||||
msgid "The header in first line is required."
|
||||
#: agenda/static/templates/agenda/item-import.html:70
|
||||
msgid "Text is optional and may be empty."
|
||||
msgstr ""
|
||||
|
||||
#. academic degree
|
||||
#: agenda/static/templates/agenda/item-import.html:63
|
||||
#: agenda/static/templates/agenda/item-import.html:84
|
||||
#: agenda/static/templates/agenda/item-list.html:170
|
||||
#: assignments/static/js/assignments/site.js:77
|
||||
#: assignments/static/templates/assignments/assignment-list.html:140
|
||||
@ -1081,9 +1067,9 @@ msgstr ""
|
||||
#: motions/static/js/motions/site.js:161
|
||||
#: motions/static/templates/motions/motion-import.html:57
|
||||
#: motions/static/templates/motions/motion-list.html:95
|
||||
#: users/static/js/users/site.js:263
|
||||
#: users/static/js/users/site.js:288
|
||||
#: users/static/templates/users/user-detail-profile.html:22
|
||||
#: users/static/templates/users/user-import.html:67
|
||||
#: users/static/templates/users/user-import.html:89
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
|
||||
@ -1109,13 +1095,13 @@ msgstr ""
|
||||
msgid "Uploaded by"
|
||||
msgstr ""
|
||||
|
||||
#: users/static/js/users/site.js:721
|
||||
#: users/static/js/users/site.js:808
|
||||
msgid "Use <strong>admin</strong> and <strong>admin</strong> for first login."
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:45
|
||||
#: agenda/static/templates/agenda/item-import.html:72
|
||||
#: motions/static/templates/motions/motion-import.html:44
|
||||
#: users/static/templates/users/user-import.html:49
|
||||
#: users/static/templates/users/user-import.html:77
|
||||
msgid "Use the CSV example file from OpenSlides Wiki."
|
||||
msgstr ""
|
||||
|
||||
@ -1125,7 +1111,7 @@ msgstr ""
|
||||
msgid "Username"
|
||||
msgstr ""
|
||||
|
||||
#: users/static/js/users/site.js:742
|
||||
#: users/static/js/users/site.js:829
|
||||
msgid "Username or password was not correct."
|
||||
msgstr ""
|
||||
|
||||
@ -1171,6 +1157,10 @@ msgstr ""
|
||||
msgid "Yes"
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:112
|
||||
msgid "agenda items will be not imported."
|
||||
msgstr ""
|
||||
|
||||
#: assignments/static/templates/assignments/assignment-list.html:72
|
||||
msgid "elections"
|
||||
msgstr ""
|
||||
@ -1181,9 +1171,7 @@ msgid "h"
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:28
|
||||
#: agenda/static/templates/agenda/item-import.html:78
|
||||
#: users/static/templates/users/user-import.html:28
|
||||
#: users/static/templates/users/user-import.html:88
|
||||
msgid "imported"
|
||||
msgstr ""
|
||||
|
||||
@ -1191,6 +1179,14 @@ msgstr ""
|
||||
msgid "items"
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:125
|
||||
msgid "items were successfully imported."
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-import.html:118
|
||||
msgid "items will be imported."
|
||||
msgstr ""
|
||||
|
||||
#: motions/static/templates/motions/motion-detail.html:117
|
||||
msgid "majority"
|
||||
msgstr ""
|
||||
@ -1215,6 +1211,18 @@ msgstr ""
|
||||
msgid "participants"
|
||||
msgstr ""
|
||||
|
||||
#: users/static/templates/users/user-import.html:152
|
||||
msgid "participants were successfully imported."
|
||||
msgstr ""
|
||||
|
||||
#: users/static/templates/users/user-import.html:145
|
||||
msgid "participants will be imported."
|
||||
msgstr ""
|
||||
|
||||
#: users/static/templates/users/user-import.html:139
|
||||
msgid "participants will be not imported."
|
||||
msgstr ""
|
||||
|
||||
#: agenda/static/templates/agenda/item-list.html:99
|
||||
#: assignments/static/templates/assignments/assignment-list.html:73
|
||||
#: motions/static/templates/motions/motion-list.html:79
|
||||
@ -1225,3 +1233,7 @@ msgstr ""
|
||||
#: motions/static/templates/motions/motion-detail.html:118
|
||||
msgid "undocumented"
|
||||
msgstr ""
|
||||
|
||||
#: users/static/templates/users/user-import.html:127
|
||||
msgid "{{ groupname }}"
|
||||
msgstr ""
|
||||
|
@ -80,6 +80,11 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
|
||||
.state('users.user.import', {
|
||||
url: '/import',
|
||||
controller: 'UserImportCtrl',
|
||||
resolve: {
|
||||
groups: function(Group) {
|
||||
return Group.findAll();
|
||||
}
|
||||
}
|
||||
})
|
||||
// groups
|
||||
.state('users.group', {
|
||||
@ -372,6 +377,7 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
|
||||
function($scope, $state, ngDialog, User, Group) {
|
||||
User.bindAll({}, $scope, 'users');
|
||||
Group.bindAll({}, $scope, 'groups');
|
||||
$scope.alert = {};
|
||||
|
||||
// setup table sorting
|
||||
$scope.sortColumn = 'first_name'; //TODO: sort by first OR last name
|
||||
@ -408,7 +414,7 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
|
||||
};
|
||||
// save changed user
|
||||
$scope.save = function (user) {
|
||||
Assignment.save(user).then(
|
||||
User.save(user).then(
|
||||
function(success) {
|
||||
//user.quickEdit = false;
|
||||
$scope.alert.show = false;
|
||||
@ -572,13 +578,15 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
|
||||
|
||||
.controller('UserImportCtrl', [
|
||||
'$scope',
|
||||
'gettext',
|
||||
'User',
|
||||
function($scope, User) {
|
||||
'Group',
|
||||
function($scope, gettext, User, Group) {
|
||||
// import from textarea
|
||||
$scope.importByLine = function () {
|
||||
$scope.users = $scope.userlist[0].split("\n");
|
||||
$scope.usernames = $scope.userlist[0].split("\n");
|
||||
$scope.importcounter = 0;
|
||||
$scope.users.forEach(function(name) {
|
||||
$scope.usernames.forEach(function(name) {
|
||||
// Split each full name in first and last name.
|
||||
// The last word is set as last name, rest is the first name(s).
|
||||
// (e.g.: "Max Martin Mustermann" -> last_name = "Mustermann")
|
||||
@ -598,42 +606,106 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
|
||||
});
|
||||
};
|
||||
|
||||
// import from csv file
|
||||
// *** csv import ***
|
||||
// set initial data for csv import
|
||||
$scope.users = []
|
||||
$scope.separator = ',';
|
||||
$scope.encoding = 'UTF-8';
|
||||
$scope.encodingOptions = ['UTF-8', 'ISO-8859-1'];
|
||||
$scope.csv = {
|
||||
content: null,
|
||||
header: true,
|
||||
separator: ',',
|
||||
headerVisible: false,
|
||||
separator: $scope.separator,
|
||||
separatorVisible: false,
|
||||
encoding: $scope.encoding,
|
||||
encodingVisible: false,
|
||||
result: null
|
||||
};
|
||||
|
||||
$scope.importByCSV = function (result) {
|
||||
var obj = JSON.parse(JSON.stringify(result));
|
||||
$scope.csvimporting = true;
|
||||
$scope.csvlines = Object.keys(obj).length;
|
||||
$scope.csvimportcounter = 0;
|
||||
for (var i = 0; i < obj.length; i++) {
|
||||
var user = {};
|
||||
user.title = obj[i].titel;
|
||||
user.first_name = obj[i].first_name;
|
||||
user.last_name = obj[i].last_name;
|
||||
user.structure_level = obj[i].structure_level;
|
||||
// set csv file encoding
|
||||
$scope.setEncoding = function () {
|
||||
$scope.csv.encoding = $scope.encoding;
|
||||
};
|
||||
// set csv file encoding
|
||||
$scope.setSeparator = function () {
|
||||
$scope.csv.separator = $scope.separator;
|
||||
};
|
||||
// detect if csv file is loaded
|
||||
$scope.$watch('csv.result', function () {
|
||||
$scope.users = [];
|
||||
var quotionRe = /^"(.*)"$/;
|
||||
angular.forEach($scope.csv.result, function (user) {
|
||||
// title
|
||||
if (user.title) {
|
||||
user.title = user.title.replace(quotionRe, '$1');
|
||||
}
|
||||
// first name
|
||||
if (user.first_name) {
|
||||
user.first_name = user.first_name.replace(quotionRe, '$1');
|
||||
}
|
||||
// last name
|
||||
if (user.last_name) {
|
||||
user.last_name = user.last_name.replace(quotionRe, '$1');
|
||||
}
|
||||
if (!user.first_name && !user.last_name) {
|
||||
user.importerror = true;
|
||||
user.name_error = gettext('Error: First or last name is required.');
|
||||
}
|
||||
// structure level
|
||||
if (user.structure_level) {
|
||||
user.structure_level = user.structure_level.replace(quotionRe, '$1');
|
||||
}
|
||||
// groups
|
||||
if (user.groups) {
|
||||
var csvGroups = user.groups.replace(quotionRe, '$1').split(",");
|
||||
user.groups = [];
|
||||
if (obj[i].groups !== '') {
|
||||
var groups = obj[i].groups.replace('"','').split(",");
|
||||
groups.forEach(function(group) {
|
||||
user.groups.push(group);
|
||||
user.groupnames = [];
|
||||
if (csvGroups != '') {
|
||||
// All group objects are already loaded via the resolve statement from ui-router.
|
||||
var allGroups = Group.getAll();
|
||||
csvGroups.forEach(function(csvGroup) {
|
||||
allGroups.forEach(function (allGroup) {
|
||||
if (csvGroup == allGroup.id) {
|
||||
user.groups.push(allGroup.id);
|
||||
user.groupnames.push(allGroup.name);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
user.comment = obj[i].comment;
|
||||
} else {
|
||||
user.groups = [];
|
||||
}
|
||||
// comment
|
||||
if (user.comment) {
|
||||
user.comment = user.comment.replace(quotionRe, '$1');
|
||||
}
|
||||
// is active
|
||||
if (user.is_active) {
|
||||
user.is_active = user.is_active.replace(quotionRe, '$1');
|
||||
if (user.is_active == '1') {
|
||||
user.is_active = true;
|
||||
} else {
|
||||
user.is_active = false;
|
||||
}
|
||||
}
|
||||
$scope.users.push(user);
|
||||
});
|
||||
});
|
||||
|
||||
// import from csv file
|
||||
$scope.import = function () {
|
||||
$scope.csvImporting = true;
|
||||
angular.forEach($scope.users, function (user) {
|
||||
if (!user.importerror) {
|
||||
User.create(user).then(
|
||||
function(success) {
|
||||
$scope.csvimportcounter++;
|
||||
user.imported = true;
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
$scope.csvimported = true;
|
||||
};
|
||||
|
||||
$scope.clear = function () {
|
||||
$scope.csv.result = null;
|
||||
};
|
||||
|
@ -11,7 +11,7 @@
|
||||
</div>
|
||||
|
||||
<div class="details">
|
||||
<h3 translate>Import by copy/paste</h3>
|
||||
<h2 translate>Import by copy/paste</h2>
|
||||
<p translate>Copy and paste your participant names in this textbox.
|
||||
Keep each person in a single line.</p>
|
||||
|
||||
@ -23,81 +23,148 @@
|
||||
|
||||
<div class="clearfix">
|
||||
<button ng-click="importByLine()" class="btn btn-primary pull-left" translate>Import</button>
|
||||
<div class="col-xs-5" ng-if="users">
|
||||
<progressbar animate="false" type="success" max="users.length" value="importcounter">
|
||||
<i>{{ importcounter }} / {{ users.length }} {{ "imported" | translate }}</i>
|
||||
<div class="col-xs-5" ng-if="usernames">
|
||||
<progressbar animate="false" type="success" max="usernames.length" value="importcounter">
|
||||
<i>{{ importcounter }} / {{ usernames.length }} {{ "imported" | translate }}</i>
|
||||
</progressbar>
|
||||
</div>
|
||||
</div>
|
||||
<div class="spacer">
|
||||
<a ng-if="importcounter > 0 && importcounter == usernames.length" ui-sref="users.user.list"
|
||||
class="btn btn-default">
|
||||
<i class="fa fa-angle-double-left fa-lg"></i>
|
||||
<translate>Back to users overview</translate>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3 translate>Import by CSV file</h3>
|
||||
<p translate>Select a CSV file to import users!
|
||||
<h2 translate>Import by CSV file</h2>
|
||||
|
||||
<p translate>Please note:</p>
|
||||
<div class="block row">
|
||||
<div class="title">
|
||||
<h3 translate>Select a CSV file
|
||||
</div>
|
||||
<div class="block right import">
|
||||
<label class="label" for="inputSeparator" translate>Separator</label>
|
||||
<input type="text" ng-model="separator" ng-change="setSeparator()" ng-init="separator=separator" id="inputSeparator">
|
||||
<br>
|
||||
<label class="label" for="selectEncoding" translate>Encoding</label>
|
||||
<select ng-model="encoding" ng-options="enc as enc for enc in encodingOptions"
|
||||
ng-selected="setEncoding()" ng-init="encoding=encoding" id="selectEncoding"></select>
|
||||
<ng-csv-import
|
||||
content="csv.content"
|
||||
header="csv.header"
|
||||
header-visible="csv.headerVisible"
|
||||
separator="csv.separator"
|
||||
separator-visible="csv.separatorVisible"
|
||||
result="csv.result"
|
||||
encoding="csv.encoding"
|
||||
encoding-visible="csv.encodingVisible"></ng-csv-import>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 translate>Please note:</h4>
|
||||
<ul>
|
||||
<li><translate>Required comma separated values</translate>:<br>
|
||||
<code translate>'title, first_name, last_name, structure level, groups, comment, is active'</code>
|
||||
<li><translate>Required comma or semicolon separated values with these column header names in the first row</translate>:<br>
|
||||
<code>'title, first_name, last_name, structure level, groups, comment, is active'</code>
|
||||
<li><translate>Default groups</translate>:
|
||||
<translate>Delegate</translate> <code>3</code>,
|
||||
<translate>Staff</translate> <code>4</code>
|
||||
<li translate>At least first name or last name have to be filled in. All
|
||||
other fields are optional and may be empty.
|
||||
<li translate>The header in first line is required.
|
||||
<li translate>Required CSV file encoding is UTF-8.
|
||||
<li translate>Only double quotes are accepted as text delimiter (no single quotes).
|
||||
<li><a href="https://github.com/OpenSlides/OpenSlides/wiki/CSV-Import" translate>
|
||||
Use the CSV example file from OpenSlides Wiki.
|
||||
</a>
|
||||
</ul>
|
||||
|
||||
<ng-csv-import
|
||||
content="csv.content"
|
||||
class="import"
|
||||
header="csv.header"
|
||||
separator="csv.separator"
|
||||
result="csv.result"></ng-csv-import>
|
||||
|
||||
<div ng-if="csv.result" class="col-sm-10 well">
|
||||
<div ng-if="csv.result">
|
||||
<h3 translate>Preview</h3>
|
||||
<table class="table table-striped table-bordered table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<th>#
|
||||
<th translate>Title
|
||||
<th translate>First name
|
||||
<th translate>Last name
|
||||
<th translate>Structure level
|
||||
<th translate>Groups
|
||||
<th translate>Comment</th>
|
||||
<tbody ng-repeat="user in csv.result">
|
||||
<th translate>Comment
|
||||
<th translate>Is active</th>
|
||||
<tbody ng-repeat="user in users">
|
||||
<tr>
|
||||
<td>{{ $index+1 }}
|
||||
<td>{{ user.title }}
|
||||
<td>{{ user.first_name }}
|
||||
<td>{{ user.last_name }}
|
||||
<td>{{ user.structure_level }}
|
||||
<td>{{ user.groups }}
|
||||
<td>{{ user.comment }}
|
||||
<td class="minimum"
|
||||
ng-class="{ 'text-danger': user.importerror, 'text-success': user.imported }">
|
||||
<span ng-if="user.importerror">
|
||||
<i class="fa fa-exclamation-triangle fa-lg"></i>
|
||||
</span>
|
||||
<span ng-if="!user.importerror && !user.imported">
|
||||
<i class="fa fa-check-circle-o fa-lg"></i>
|
||||
</span>
|
||||
<span ng-if="user.imported">
|
||||
<i class="fa fa-check-circle fa-lg"></i>
|
||||
</span>
|
||||
<td>
|
||||
{{ $index + 1 }}
|
||||
<td>
|
||||
{{ user.title }}
|
||||
<td ng-class="{ 'text-danger': user.name_error }">
|
||||
<span ng-if="!user.first_name && user.name_error" title="{{ user.name_error | translate }}">
|
||||
<i class="fa fa-exclamation-triangle"></i>
|
||||
</span>
|
||||
{{ user.first_name }}
|
||||
<td ng-class="{ 'text-danger': user.name_error }">
|
||||
<span ng-if="!user.last_name && user.name_error" title="{{ user.name_error | translate }}">
|
||||
<i class="fa fa-exclamation-triangle"></i>
|
||||
</span>
|
||||
{{ user.last_name }}
|
||||
<td>
|
||||
{{ user.structure_level }}
|
||||
<td>
|
||||
<div ng-repeat="groupname in user.groupnames">
|
||||
<translate>{{ groupname }}</translate>
|
||||
</div>
|
||||
<td>
|
||||
{{ user.comment }}
|
||||
<td>
|
||||
<i ng-if="user.is_active" class="fa fa-check-square-o"></i>
|
||||
</table>
|
||||
|
||||
<div class="clearfix">
|
||||
<button ng-if="!csvimporting" ng-click="importByCSV(csv.result)" class="btn btn-primary pull-left" translate>Import</button>
|
||||
<div ng-if="csvimporting">
|
||||
<progressbar animate="false" type="success" max="csvlines" value="csvimportcounter">
|
||||
<i>{{ csvimportcounter }} / {{ csvlines }} {{ "imported" | translate }}</i>
|
||||
</progressbar>
|
||||
<div class="text-danger">
|
||||
<div ng-repeat="user in usersFailed = (users | filter:{importerror:true})"></div>
|
||||
<i class="fa fa-exclamation-triangle"></i>
|
||||
{{ usersFailed.length }}
|
||||
<translate>participants will be not imported.</translate>
|
||||
</div>
|
||||
<a ng-if="csvimported" ui-sref="users.user.list" class="btn btn-sm btn-default">
|
||||
<div>
|
||||
<div ng-repeat="user in usersPassed = (users | filter:{importerror:false})"></div>
|
||||
<i class="fa fa-check-circle-o fa-lg"></i>
|
||||
{{ users.length - usersFailed.length }}
|
||||
<translate>participants will be imported.</translate>
|
||||
</div>
|
||||
<div ng-repeat="user in usersImported = (users | filter:{imported:true})"></div>
|
||||
<div ng-if="usersImported.length > 0" class="text-success">
|
||||
<hr class="smallhr">
|
||||
<i class="fa fa-check-circle fa-lg"></i>
|
||||
{{ usersImported.length }}
|
||||
<translate>participants were successfully imported.</translate>
|
||||
</div>
|
||||
|
||||
<div class="spacer">
|
||||
<button ng-click="clear()" class="btn btn-default" translate>
|
||||
Clear preview
|
||||
</button>
|
||||
<button ng-if="!csvImporting" ng-click="import()" class="btn btn-primary" translate>
|
||||
Import {{ users.length - usersFailed.length }} participants
|
||||
</button>
|
||||
</div>
|
||||
<div class="spacer">
|
||||
<a ng-if="csvimported" ui-sref="users.user.list" class="btn btn-default">
|
||||
<i class="fa fa-angle-double-left fa-lg"></i>
|
||||
<translate>Back to users overview</translate>
|
||||
</a>
|
||||
</div>
|
||||
<p>
|
||||
<div class="form-group">
|
||||
<button ng-if="!csvimporting" ng-click="clear()" class="btn btn-default" translate>
|
||||
Clear
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user