Merge pull request #2973 from FinnStutzenstein/Dialogs

Dialogs for some views
This commit is contained in:
Emanuel Schütze 2017-02-17 22:20:59 +01:00 committed by GitHub
commit 6bad8e8cc6
27 changed files with 478 additions and 350 deletions

View File

@ -48,6 +48,8 @@ Core:
- Added success/error symbol to config to show if saving was successful.
- Added UTF-8 byte order mark for every CSV export.
- Moved full-text search to client-side (removed the server-side search engine Whoosh).
- Better dialog handling. Show dialog just in forground without changing the state url.
Added new dialog for profile, change password, tag and category update view.
Motions:
- Added adjustable line numbering mode (outside, inside, none) for each

View File

@ -81,6 +81,7 @@ angular.module('OpenSlidesApp.agenda.site', [
'$filter',
'$http',
'$state',
'$injector',
'DS',
'operator',
'ngDialog',
@ -96,9 +97,9 @@ angular.module('OpenSlidesApp.agenda.site', [
'osTableFilter',
'AgendaCsvExport',
'PdfCreate',
function($scope, $filter, $http, $state, DS, operator, ngDialog, Agenda, TopicForm, AgendaTree, Projector,
ProjectionDefault, AgendaContentProvider, PdfMakeDocumentProvider, gettextCatalog, gettext, osTableFilter,
AgendaCsvExport, PdfCreate) {
function($scope, $filter, $http, $state, $injector, DS, operator, ngDialog, Agenda, TopicForm,
AgendaTree, Projector, ProjectionDefault, AgendaContentProvider, PdfMakeDocumentProvider,
gettextCatalog, gettext, osTableFilter, AgendaCsvExport, PdfCreate) {
// Bind agenda tree to the scope
$scope.$watch(function () {
return Agenda.lastModified();
@ -268,13 +269,22 @@ angular.module('OpenSlidesApp.agenda.site', [
return false;
}
};
$scope.getUpdateStatePrefix = function (item) {
$scope.getDetailStatePrefix = 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;
};
$scope.edit = function (item) {
var formName = item.content_object.collection.split('/')[1];
// Hotfix for Issue 2566.
// The changes could be reverted if Issue 2480 is closed.
formName = formName.replace('motion-block', 'motionBlock');
formName = formName.charAt(0).toUpperCase() + formName.slice(1) + 'Form';
var form = $injector.get(formName);
ngDialog.open(form.getDialog({id: item.content_object.id}));
};
// export
$scope.pdfExport = function () {
var filename = gettextCatalog.getString('Agenda') + '.pdf';

View File

@ -271,7 +271,7 @@
<div>
<!-- ID and title -->
<div>
<a class="title" ui-sref="{{ getUpdateStatePrefix(item) }}.detail({id: item.content_object.id})" ng-show="isAllowedToSeeOpenLink(item)">
<a class="title" ui-sref="{{ getDetailStatePrefix(item) }}.detail({id: item.content_object.id})" ng-show="isAllowedToSeeOpenLink(item)">
{{ item.getListViewTitle() }}
</a>
<span class="title" ng-hide="isAllowedToSeeOpenLink(item)">
@ -283,8 +283,7 @@
<small>
<a ui-sref="agenda.item.detail({id: item.id})" translate>List of speakers</a>
<span os-perms="agenda.can_manage"> &middot;
<a ui-sref="{{ getUpdateStatePrefix(item) }}.detail.update({id: item.content_object.id})"
translate>Edit</a> &middot;
<a href="" ng-click="edit(item)" translate>Edit</a> &middot;
<a href="" class="text-danger"
ng-bootbox-confirm="{{ 'Are you sure you want to delete this entry?' | translate }}<br>
<b>{{ item.getTitle() }}</b>"

View File

@ -682,8 +682,9 @@ angular.module('OpenSlidesApp.core', [
.factory('Projector', [
'DS',
'$http',
'$injector',
'Config',
function(DS, $http, Config) {
function(DS, $http, $injector, Config) {
return DS.defineResource({
name: 'core/projector',
onConflict: 'replace',
@ -701,13 +702,13 @@ angular.module('OpenSlidesApp.core', [
{"action": action, "direction": direction}
);
},
getStateForCurrentSlide: function () {
getFormOrStateForCurrentSlide: function () {
var return_dict;
angular.forEach(this.elements, function(value, key) {
if (value.name == 'agenda/list-of-speakers') {
return_dict = {
'state': 'agenda.item.detail',
'param': {id: value.id}
state: 'agenda.item.detail',
id: value.id,
};
} else if (
value.name != 'agenda/item-list' &&
@ -715,9 +716,14 @@ angular.module('OpenSlidesApp.core', [
value.name != 'core/countdown' &&
value.name != 'core/projector-message' &&
value.name != 'agenda/current-list-of-speakers' ) {
var formName = value.name.split('/')[1];
// Hotfix for Issue 2566.
// The changes could be reverted if Issue 2480 is closed.
formName = formName.replace('motion-block', 'motionBlock');
formName = formName.charAt(0).toUpperCase() + formName.slice(1) + 'Form';
return_dict = {
'state': value.name.replace('/', '.')+'.detail.update',
'param': {id: value.id}
form: $injector.get(formName),
id: value.id
};
}
});

View File

@ -370,25 +370,7 @@ angular.module('OpenSlidesApp.core.site', [
basePerm: 'core.can_manage_tags',
},
})
.state('core.tag.list', {})
.state('core.tag.create', {})
.state('core.tag.detail', {
resolve: {
tagId: ['$stateParams', function($stateParams) {
return $stateParams.id;
}],
}
})
.state('core.tag.detail.update', {
resolve: {
tagId: ['$stateParams', function($stateParams) {
return $stateParams.id;
}],
},
views: {
'@core.tag': {}
}
});
.state('core.tag.list', {});
$locationProvider.html5Mode(true);
}
@ -438,6 +420,38 @@ angular.module('OpenSlidesApp.core.site', [
}
])
.factory('TagForm', [
'gettextCatalog',
function (gettextCatalog) {
return {
getDialog: function (tag) {
return {
template: 'static/templates/core/tag-form.html',
controller: (tag) ? 'TagUpdateCtrl' : 'TagCreateCtrl',
className: 'ngdialog-theme-default wide-form',
closeByEscape: false,
closeByDocument: false,
resolve: {
tagId: function () {return tag ? tag.id : void 0;},
},
};
},
getFormFields: function() {
return [
{
key: 'name',
type: 'input',
templateOptions: {
label: gettextCatalog.getString('Name'),
required: true
}
},
];
},
};
}
])
/* This factory handles the filtering of the OS-data-tables. It contains
* all logic needed for the table header filtering. Things to configure:
* - multiselectFilters: A dict associating the filter name to a list (empty per default). E.g.
@ -615,6 +629,11 @@ angular.module('OpenSlidesApp.core.site', [
extends: 'textarea',
templateUrl: 'static/templates/core/editor.html',
});
formlyConfig.setType({
name: 'password',
extends: 'input',
templateUrl: 'static/templates/core/password.html',
});
formlyConfig.setType({
name: 'select-single',
extends: 'select',
@ -1122,9 +1141,13 @@ angular.module('OpenSlidesApp.core.site', [
};
$scope.editCurrentSlide = function (projector) {
var state = projector.getStateForCurrentSlide();
if (state) {
$state.go(state.state, state.param);
var data = projector.getFormOrStateForCurrentSlide();
if (data) {
if (data.form) {
ngDialog.open(data.form.getDialog({id: data.id}));
} else {
$state.go(data.state, {id: data.id});
}
}
};
@ -1227,13 +1250,14 @@ angular.module('OpenSlidesApp.core.site', [
.controller('ManageProjectorsCtrl', [
'$scope',
'$http',
'$state',
'$timeout',
'Projector',
'ProjectionDefault',
'Config',
'ProjectorMessage',
function ($scope, $http, $state, $timeout, Projector, ProjectionDefault, Config, ProjectorMessage) {
'ngDialog',
function ($scope, $http, $timeout, Projector, ProjectionDefault, Config,
ProjectorMessage, ngDialog) {
ProjectionDefault.bindAll({}, $scope, 'projectiondefaults');
// watch for changes in projector_broadcast
@ -1316,9 +1340,13 @@ angular.module('OpenSlidesApp.core.site', [
}
};
$scope.editCurrentSlide = function (projector) {
var state = projector.getStateForCurrentSlide();
if (state) {
$state.go(state.state, state.param);
var data = projector.getFormOrStateForCurrentSlide();
if (data) {
if (data.form) {
ngDialog.open(data.form.getDialog({id: data.id}));
} else {
$state.go(data.state, {id: data.id});
}
}
};
$scope.editName = function (projector) {
@ -1378,8 +1406,12 @@ angular.module('OpenSlidesApp.core.site', [
.controller('TagListCtrl', [
'$scope',
'Tag',
function($scope, Tag) {
'ngDialog',
'TagForm',
'gettext',
function($scope, Tag, ngDialog, TagForm, gettext) {
Tag.bindAll({}, $scope, 'tags');
$scope.alert = {};
// setup table sorting
$scope.sortColumn = 'name';
@ -1391,40 +1423,48 @@ angular.module('OpenSlidesApp.core.site', [
}
$scope.sortColumn = column;
};
// save changed tag
$scope.save = function (tag) {
Tag.save(tag);
};
$scope.delete = function (tag) {
Tag.destroy(tag.id).then(
function(success) {
//TODO: success message
$scope.alert = {
type: 'success',
msg: gettext('The delete was successful.'),
show: true,
};
}, function (error) {
var message = '';
for (var e in error.data) {
message += e + ': ' + error.data[e] + ' ';
}
$scope.alert = {type: 'danger', msg: message, show: true};
}
);
};
}
])
.controller('TagDetailCtrl', [
'$scope',
'Tag',
'tagId',
function($scope, Tag, tagId) {
Tag.bindOne(tagId, $scope, 'tag');
$scope.editOrCreate = function (tag) {
ngDialog.open(TagForm.getDialog(tag));
};
}
])
.controller('TagCreateCtrl', [
'$scope',
'$state',
'Tag',
function($scope, $state, Tag) {
$scope.tag = {};
'TagForm',
function($scope, Tag, TagForm) {
$scope.model = {};
$scope.alert = {};
$scope.formFields = TagForm.getFormFields();
$scope.save = function (tag) {
Tag.create(tag).then(
function (success) {
$state.go('core.tag.list');
$scope.closeThisDialog();
},
function (error) {
var message = '';
for (var e in error.data) {
message += e + ': ' + error.data[e] + ' ';
}
$scope.alert = {type: 'danger', msg: message, show: true};
}
);
};
@ -1433,17 +1473,27 @@ angular.module('OpenSlidesApp.core.site', [
.controller('TagUpdateCtrl', [
'$scope',
'$state',
'Tag',
'tagId',
function($scope, $state, Tag, tagId) {
$scope.tag = Tag.get(tagId);
'TagForm',
function($scope, Tag, tagId, TagForm) {
$scope.model = angular.copy(Tag.get(tagId));
$scope.alert = {};
$scope.formFields = TagForm.getFormFields();
$scope.save = function (tag) {
Tag.save(tag).then(
function(success) {
$state.go('core.tag.list');
Tag.inject(tag);
Tag.save(tag).then(function(success) {
$scope.closeThisDialog();
}, function (error) {
// save error: revert all changes by restore
// the original object
Tag.refresh(tag);
var message = '';
for (var e in error.data) {
message += e + ': ' + error.data[e] + ' ';
}
);
$scope.alert = {type: 'danger', msg: message, show: true};
});
};
}
])
@ -1493,12 +1543,15 @@ angular.module('OpenSlidesApp.core.site', [
// increment unread messages counter for each new message
$scope.$watch('chatmessages', function (newVal, oldVal) {
// add new message id if there is really a new message which is not yet tracked
if (oldVal.length > 0) {
if (oldVal.length > 0 && newVal.length > 0) {
if ((oldVal[oldVal.length-1].id != newVal[newVal.length-1].id) &&
($.inArray(newVal[newVal.length-1].id, NewChatMessages) == -1)) {
NewChatMessages.push(newVal[newVal.length-1].id);
$scope.unreadMessages = NewChatMessages.length;
}
} else if (newVal.length === 0) {
NewChatMessages = [];
$scope.unreadMessages = 0;
}
});

View File

@ -133,7 +133,7 @@
<div class="nobr">
<!-- edit -->
<a ng-click="editCurrentSlide(projector)"
ng-disabled="!projector.getStateForCurrentSlide()"
ng-disabled="!projector.getFormOrStateForCurrentSlide()"
class="btn btn-default btn-sm"
title="{{ 'Edit current slide' | translate}}">
<i class="fa fa-pencil"></i>

View File

@ -0,0 +1 @@
<input type="password" ng-model="model[options.key]" class="form-control">

View File

@ -86,7 +86,7 @@
<div os-perms="core.can_manage_projector" class="nobr">
<!-- edit -->
<a ng-click="editCurrentSlide(active_projector)"
ng-disabled="!active_projector.getStateForCurrentSlide()"
ng-disabled="!active_projector.getFormOrStateForCurrentSlide()"
class="btn btn-default btn-sm"
title="{{ 'Edit current slide' | translate}}">
<i class="fa fa-pencil"></i>

View File

@ -1,14 +0,0 @@
<div class="header">
<div class="title">
<div class="submenu">
<a ui-sref="core.tag.list" class="btn btn-sm btn-default">
<i class="fa fa-angle-double-left fa-lg"></i>
<translate>Back to overview</translate>
</a>
</div>
<h1>{{ tag.name }}</h1>
<h2 translate>Tag</h2>
</div>
</div>
<div class="details"></div>

View File

@ -1,28 +1,17 @@
<div class="header">
<div class="title">
<div class="submenu">
<a ui-sref="core.tag.list" class="btn btn-sm btn-default">
<i class="fa fa-angle-double-left fa-lg"></i>
<translate>Back to overview</translate>
</a>
</div>
<h1 ng-if="tag.id" translate>Edit tag</h1>
<h1 ng-if="!tag.id" translate>New tag</h1>
</div>
<h1 ng-if="model.id" translate>Edit tag</h1>
<h1 ng-if="!model.id" translate>New tag</h1>
<div uib-alert ng-show="alert.show" ng-class="'alert-' + (alert.type || 'warning')" close="alert={}">
{{ alert.msg }}
</div>
<div class="details">
<form name="tagForm">
<div class="form-group">
<label for="inputName" translate>Name</label>
<input type="text" ng-model="tag.name" class="form-control" name="inputName" required>
</div>
<button type="submit" ng-click="save(tag)" class="btn btn-primary" translate>
<form name="userForm" ng-submit="save(model)">
<formly-form model="model" fields="formFields">
<button type="submit" ng-disabled="userForm.$invalid" class="btn btn-primary" translate>
Save
</button>
<button ui-sref="core.tag.list" class="btn btn-default" translate>
<button ng-click="closeThisDialog()" class="btn btn-default" translate>
Cancel
</button>
</formly-form>
</form>
</div>

View File

@ -5,7 +5,7 @@
<i class="fa fa-angle-double-left fa-lg"></i>
<translate>Back to overview</translate>
</a>
<a ui-sref="core.tag.create" os-perms="core.can_manage_tags" class="btn btn-primary btn-sm">
<a href="" ng-click="editOrCreate()" os-perms="core.can_manage_tags" class="btn btn-primary btn-sm">
<i class="fa fa-plus fa-lg"></i>
<translate>New</translate>
</a>
@ -16,7 +16,11 @@
<div class="details">
<div class="row form-group">
<div class="col-sm-8"></div>
<div class="col-sm-8">
<div uib-alert ng-show="alert.show" ng-class="'alert-' + (alert.type || 'warning')" close="alert={}">
{{ alert.msg }}
</div>
</div>
<div class="col-sm-4">
<input type="text" ng-model="filter.search" class="form-control"
placeholder="{{ 'Filter' | translate}}">
@ -37,7 +41,7 @@
<td ng-mouseover="tag.hover=true" ng-mouseleave="tag.hover=false">
<strong>{{ tag.name }}</strong>
<div class="hoverActions" ng-class="{'hiddenDiv': !tag.hover}">
<a ui-sref="core.tag.detail.update({id: tag.id })" translate>Edit</a> &middot;
<a href="" ng-click="editOrCreate(tag)" translate>Edit</a> &middot;
<a href="" class="text-danger"
ng-bootbox-confirm="{{ 'Are you sure you want to delete this entry?' | translate }}<br>
<b>{{ tag.name }}</b>"

View File

@ -91,12 +91,13 @@
</a>
<ul class="dropdown-menu pull-right" uib-dropdown-menu aria-labelledby="user-settings-dropdown">
<li>
<a ui-sref="users.user.detail.profile({ id: operator.user.id })">
<!--<a ui-sref="users.user.detail.profile({ id: operator.user.id })">-->
<a href="" ng-click="editProfile()">
<i class="fa fa-cog"></i>
<translate>Edit profile</translate>
</a>
<li>
<a ui-sref="users.user.detail.password({ id: operator.user.id })">
<a href="" ng-click="changePassword()">
<i class="fa fa-key"></i>
<translate>Change password</translate>
</a>

View File

@ -153,7 +153,7 @@ angular.module('OpenSlidesApp.motions.motionBlock', [])
$scope.defaultProjectorId = projectiondefault.projector_id;
}
});
$scope.openDialog = function (topic) {
$scope.openDialog = function (motionBlock) {
ngDialog.open(MotionBlockForm.getDialog(motionBlock));
};
$scope.followRecommendations = function () {

View File

@ -103,19 +103,6 @@ angular.module('OpenSlidesApp.motions.site', [
},
})
.state('motions.category.list', {})
.state('motions.category.create', {})
.state('motions.category.detail', {
resolve: {
categoryId: ['$stateParams', function($stateParams) {
return $stateParams.id;
}]
}
})
.state('motions.category.detail.update', {
views: {
'@motions.category': {}
}
})
.state('motions.category.sort', {
url: '/sort/{id}',
controller: 'CategorySortCtrl',
@ -524,6 +511,45 @@ angular.module('OpenSlidesApp.motions.site', [
}
])
.factory('CategoryForm', [
'gettextCatalog',
function (gettextCatalog) {
return {
getDialog: function (category) {
return {
template: 'static/templates/motions/category-form.html',
controller: category ? 'CategoryUpdateCtrl' : 'CategoryCreateCtrl',
className: 'ngdialog-theme-default wide-form',
closeByEscape: false,
closeByDocument: false,
resolve: {
categoryId: function () {return category ? category.id : void 0;},
},
};
},
getFormFields: function () {
return [
{
key: 'prefix',
type: 'input',
templateOptions: {
label: gettextCatalog.getString('Prefix')
},
},
{
key: 'name',
type: 'input',
templateOptions: {
label: gettextCatalog.getString('Name')
},
}
];
},
};
}
])
// Provide generic motionpoll form fields for poll update view
.factory('MotionPollForm', [
'gettextCatalog',
@ -1780,7 +1806,9 @@ angular.module('OpenSlidesApp.motions.site', [
.controller('CategoryListCtrl', [
'$scope',
'Category',
function($scope, Category) {
'ngDialog',
'CategoryForm',
function($scope, Category, ngDialog, CategoryForm) {
Category.bindAll({}, $scope, 'categories');
// setup table sorting
@ -1798,28 +1826,31 @@ angular.module('OpenSlidesApp.motions.site', [
$scope.delete = function (category) {
Category.destroy(category.id);
};
}
])
.controller('CategoryDetailCtrl', [
'$scope',
'Category',
'categoryId',
function($scope, Category, categoryId) {
Category.bindOne(categoryId, $scope, 'category');
$scope.editOrCreate = function (category) {
ngDialog.open(CategoryForm.getDialog(category));
};
}
])
.controller('CategoryCreateCtrl', [
'$scope',
'$state',
'Category',
function($scope, $state, Category) {
$scope.category = {};
'CategoryForm',
function($scope, Category, CategoryForm) {
$scope.model = {};
$scope.alert = {};
$scope.formFields = CategoryForm.getFormFields();
$scope.save = function (category) {
Category.create(category).then(
function(success) {
$state.go('motions.category.list');
$scope.closeThisDialog();
},
function (error) {
var message = '';
for (var e in error.data) {
message += e + ': ' + error.data[e] + ' ';
}
$scope.alert = {type: 'danger', msg: message, show: true};
}
);
};
@ -1828,15 +1859,28 @@ angular.module('OpenSlidesApp.motions.site', [
.controller('CategoryUpdateCtrl', [
'$scope',
'$state',
'Category',
'categoryId',
function($scope, $state, Category, categoryId) {
$scope.category = Category.get(categoryId);
'CategoryForm',
function($scope, Category, categoryId, CategoryForm) {
$scope.alert = {};
$scope.model = angular.copy(Category.get(categoryId));
$scope.formFields = CategoryForm.getFormFields();
$scope.save = function (category) {
Category.inject(category);
Category.save(category).then(
function(success) {
$state.go('motions.category.list');
$scope.closeThisDialog();
},
function (error) {
// save error: revert all changes by restore
// (refresh) original category object from server
Category.refresh(category);
var message = '';
for (var e in error.data) {
message += e + ': ' + error.data[e] + ' ';
}
$scope.alert = {type: 'danger', msg: message, show: true};
}
);
};

View File

@ -1,17 +0,0 @@
<div class="header">
<div class="title">
<div class="submenu">
<a ui-sref="motions.category.list" class="btn btn-sm btn-default">
<i class="fa fa-angle-double-left fa-lg"></i>
<translate>Back to overview</translate>
</a>
</div>
<h1>{{ category.name }}</h1>
<h2 translate>Category</h2>
</div>
</div>
<div class="details">
<strong translate>Prefix:</strong>
{{ category.prefix }}
</div>

View File

@ -1,32 +1,17 @@
<div class="header">
<div class="title">
<div class="submenu">
<a ui-sref="motions.category.list" class="btn btn-sm btn-default">
<i class="fa fa-angle-double-left fa-lg"></i>
<translate>Back to overview</translate>
</a>
</div>
<h1 ng-if="category.id" translate>Edit category</h1>
<h1 ng-if="!category.id" translate>New category</h1>
</div>
<h1 ng-if="model.id" translate>Edit category</h1>
<h1 ng-if="!model.id" translate>New category</h1>
<div uib-alert ng-show="alert.show" ng-class="'alert-' + (alert.type || 'warning')" close="alert={}">
{{ alert.msg }}
</div>
<div class="details">
<form name="groupForm">
<div class="form-group">
<label for="inputPrefix" translate>Prefix</label>
<input type="text" ng-model="category.prefix" class="form-control" name="inputPrefix">
</div>
<div class="form-group">
<label for="inputName" translate>Name</label>
<input type="text" ng-model="category.name" class="form-control" name="inputName" ng-required="true">
</div>
<button type="submit" ng-click="save(category)" class="btn btn-primary" translate>
<form name="categoryForm" ng-submit="save(model)">
<formly-form model="model" fields="formFields">
<button type="submit" ng-disabled="categoryForm.$invalid" class="btn btn-primary" translate>
Save
</button>
<button ui-sref="motions.category.list" class="btn btn-default" translate>
<button ng-click="closeThisDialog()" class="btn btn-default" translate>
Cancel
</button>
</formly-form>
</form>
</div>

View File

@ -5,7 +5,7 @@
<i class="fa fa-angle-double-left fa-lg"></i>
<translate>Back to overview</translate>
</a>
<a ui-sref="motions.category.create" os-perms="motions.can_manage" class="btn btn-primary btn-sm">
<a href="" os-perms="motions.can_manage" class="btn btn-primary btn-sm" ng-click="editOrCreate()">
<i class="fa fa-plus fa-lg"></i>
<translate>New</translate>
</a>
@ -43,7 +43,7 @@
<!-- sort -->
<a ui-sref="motions.category.sort({ id: category.id })" translate>Sort</a> |
<!-- edit -->
<a ui-sref="motions.category.detail.update({id: category.id })" translate>Edit</a> |
<a href="" ng-click="editOrCreate(category)" translate>Edit</a> |
<!-- delete -->
<a href="" class="text-danger"
ng-bootbox-confirm="{{ 'Are you sure you want to delete this entry?' | translate }}<br>

View File

@ -1,9 +1,9 @@
<h1 ng-if="model.id" translate>Edit change recommendation</h1>
<h1 ng-if="!model.id" translate>New change recommendation</h1>
<uib-alert ng-show="alert.show" type="{{ alert.type }}" ng-click="alert={}" close="alert={}">
<div uib-alert ng-show="alert.show" ng-class="'alert-' + (alert.type || 'warning')" close="alert={}">
{{ alert.msg }}
</uib-alert>
</div>
<form name="changeRecommendationForm" ng-submit="save(model)">
<formly-form model="model" fields="formFields">

View File

@ -1,7 +1,7 @@
<h1 ng-if="model.id" translate>Edit motion block</h1>
<h1 ng-if="!model.id" translate>New motion block</h1>
<div uib-alert ng-show="alert.show" ng-class="'alert-' + (alert.type || 'warning')" ng-click="alert={}" close="alert={}">
<div uib-alert ng-show="alert.show" ng-class="'alert-' + (alert.type || 'warning')" close="alert={}">
{{ alert.msg }}
</div>

View File

@ -2,7 +2,7 @@
<h1 ng-if="!model.id && !parent" translate>New motion</h1>
<h1 ng-if="parent"><translate>New amendment of motion</translate> {{ parent.identifier || parent.getTitle() }}</h1>
<div uib-alert ng-show="alert.show" ng-class="'alert-' + (alert.type || 'warning')" ng-click="alert={}" close="alert={}">
<div uib-alert ng-show="alert.show" ng-class="'alert-' + (alert.type || 'warning')" close="alert={}">
{{ alert.msg }}
</div>

View File

@ -10,9 +10,9 @@ class UserAccessPermissions(BaseAccessPermissions):
"""
def check_permissions(self, user):
"""
Returns True if the user has read access model instances.
Every user has read access for their model instnces.
"""
return has_perm(user, 'users.can_see_name')
return True
def get_serializer_class(self, user=None):
"""

View File

@ -62,40 +62,6 @@ angular.module('OpenSlidesApp.users.site', [
}]
}
})
// Redirects to user detail view and opens user edit form dialog, uses edit url.
// Used by $state.go(..) from core/site.js only (for edit current slide button).
// (from users list controller use UserForm factory instead to open dialog in front of
// current view without redirect)
.state('users.user.detail.update', {
onEnter: ['$stateParams', '$state', 'ngDialog',
function($stateParams, $state, ngDialog) {
ngDialog.open({
template: 'static/templates/users/user-form.html',
controller: 'UserUpdateCtrl',
className: 'ngdialog-theme-default wide-form',
closeByEscape: false,
closeByDocument: false,
resolve: {
userId: function() {return $stateParams.id;}
}
});
}
]
})
.state('users.user.detail.profile', {
views: {
'@users.user': {},
},
url: '/profile',
controller: 'UserProfileCtrl',
})
.state('users.user.detail.password', {
views: {
'@users.user': {},
},
url: '/password',
controller: 'UserPasswordCtrl',
})
.state('users.user.change-password', {
url: '/change-password/{id}',
controller: 'UserChangePasswordCtrl',
@ -417,6 +383,146 @@ angular.module('OpenSlidesApp.users.site', [
}
])
.factory('UserProfileForm', [
'gettextCatalog',
'Editor',
'Mediafile',
function (gettextCatalog, Editor, Mediafile) {
return {
// ngDialog for user form
getDialog: function (user) {
return {
template: 'static/templates/users/profile-password-form.html',
controller: 'UserProfileCtrl',
className: 'ngdialog-theme-default wide-form',
closeByEscape: false,
closeByDocument: false,
};
},
// angular-formly fields for user form
getFormFields: function (hideOnCreateForm) {
var images = Mediafile.getAllImages();
return [
{
key: 'username',
type: 'input',
templateOptions: {
label: gettextCatalog.getString('Username'),
required: true
},
},
{
className: "row",
fieldGroup: [
{
key: 'title',
type: 'input',
className: "col-xs-2 no-padding-left",
templateOptions: {
label: gettextCatalog.getString('Title')
}
},
{
key: 'first_name',
type: 'input',
className: "col-xs-5 no-padding",
templateOptions: {
label: gettextCatalog.getString('Given name')
}
},
{
key: 'last_name',
type: 'input',
className: "col-xs-5 no-padding-right",
templateOptions: {
label: gettextCatalog.getString('Surname')
}
}
]
},
{
className: "row",
fieldGroup: [
{
key: 'structure_level',
type: 'input',
className: "col-xs-9 no-padding-left",
templateOptions: {
label: gettextCatalog.getString('Structure level'),
}
},
{ key: 'number',
type: 'input',
className: "col-xs-3 no-padding-left no-padding-right",
templateOptions: {
label:gettextCatalog.getString('Participant number')
}
}
]
},
{
key: 'about_me',
type: 'editor',
templateOptions: {
label: gettextCatalog.getString('About me'),
},
data: {
ckeditorOptions: Editor.getOptions(images)
},
}
];
}
};
}
])
.factory('UserPasswordForm', [
'gettextCatalog',
function (gettextCatalog) {
return {
// ngDialog for user form
getDialog: function (user) {
return {
template: 'static/templates/users/profile-password-form.html',
controller: 'UserPasswordCtrl',
className: 'ngdialog-theme-default wide-form',
closeByEscape: false,
closeByDocument: false,
};
},
// angular-formly fields for user form
getFormFields: function (hideOnCreateForm) {
return [
{
key: 'oldPassword',
type: 'password',
templateOptions: {
label: gettextCatalog.getString('Old password'),
required: true
},
},
{
key: 'newPassword',
type: 'password',
templateOptions: {
label: gettextCatalog.getString('New password'),
required: true
},
},
{
key: 'newPassword2',
type: 'password',
templateOptions: {
label: gettextCatalog.getString('Confirm new password'),
required: true
},
},
];
}
};
}
])
.controller('UserListCtrl', [
'$scope',
'$state',
@ -761,20 +867,30 @@ angular.module('OpenSlidesApp.users.site', [
.controller('UserProfileCtrl', [
'$scope',
'$state',
'Editor',
'User',
'userId',
function($scope, $state, Editor, User, userId) {
$scope.user = angular.copy(User.get(userId));
$scope.ckeditorOptions = Editor.getOptions();
'operator',
'UserProfileForm',
'gettext',
function($scope, Editor, User, operator, UserProfileForm, gettext) {
$scope.model = angular.copy(operator.user);
$scope.title = gettext('Edit profile');
$scope.formFields = UserProfileForm.getFormFields();
$scope.save = function (user) {
User.inject(user);
User.save(user).then(
function(success) {
$state.go('users.user.list');
$scope.closeThisDialog();
},
function(error) {
$scope.formError = error;
// save error: revert all changes by restore
// (refresh) original user object from server
User.refresh(user);
var message = '';
for (var e in error.data) {
message += e + ': ' + error.data[e] + ' ';
}
$scope.alert = {type: 'danger', msg: message, show: true};
}
);
};
@ -818,24 +934,38 @@ angular.module('OpenSlidesApp.users.site', [
'$scope',
'$state',
'$http',
function($scope, $state, $http) {
$scope.save = function () {
if ($scope.newPassword != $scope.newPassword2) {
$scope.newPassword = $scope.newPassword2 = '';
$scope.formError = 'Password confirmation does not match.';
'gettext',
'UserPasswordForm',
function($scope, $state, $http, gettext, UserPasswordForm) {
$scope.title = 'Change password';
$scope.alert = {};
$scope.model = {};
$scope.formFields = UserPasswordForm.getFormFields();
$scope.save = function (data) {
if (data.newPassword != data.newPassword2) {
data.newPassword = data.newPassword2 = '';
$scope.alert = {
type: 'danger',
msg: gettext('Password confirmation does not match.'),
show: true,
};
} else {
$http.post(
'/users/setpassword/',
{'old_password': $scope.oldPassword, 'new_password': $scope.newPassword}
{'old_password': data.oldPassword, 'new_password': data.newPassword}
).then(
function (response) {
// Success.
$state.go('users.user.list');
function (success) {
$scope.closeThisDialog();
},
function (response) {
function (error) {
// Error, e. g. wrong old password.
$scope.oldPassword = $scope.newPassword = $scope.newPassword2 = '';
$scope.formError = response.data.detail;
$scope.model = {};
var message = '';
for (var e in error.data) {
message += e + ': ' + error.data[e] + ' ';
}
$scope.alert = {type: 'danger', msg: message, show: true};
}
);
}
@ -1342,13 +1472,21 @@ angular.module('OpenSlidesApp.users.site', [
'User',
'operator',
'ngDialog',
function($scope, $http, DS, User, operator, ngDialog) {
'UserProfileForm',
'UserPasswordForm',
function($scope, $http, DS, User, operator, ngDialog, UserProfileForm, UserPasswordForm) {
$scope.logout = function () {
$http.post('/users/logout/').then(function (response) {
operator.setUser(null);
window.location.reload();
});
};
$scope.editProfile = function () {
ngDialog.open(UserProfileForm.getDialog());
};
$scope.changePassword = function () {
ngDialog.open(UserPasswordForm.getDialog());
};
}
])

View File

@ -0,0 +1,16 @@
<h1>{{ title | translate }}</h1>
<div uib-alert ng-show="alert.show" ng-class="'alert-' + (alert.type || 'warning')" close="alert={}">
{{ alert.msg }}
</div>
<form name="profileForm" ng-submit="save(model)">
<formly-form model="model" fields="formFields">
<button type="submit" ng-disabled="profileForm.$invalid" class="btn btn-primary" translate>
Save
</button>
<button ng-click="closeThisDialog()" class="btn btn-default" translate>
Cancel
</button>
</formly-form>
</form>

View File

@ -1,43 +0,0 @@
<div class="header">
<div class="title">
<h1 translate>Change password</h1>
</div>
</div>
<div class="details">
<p ng-if='formError' class="text-danger">
<strong>{{ formError }}</strong>
</p>
<form name="userForm" >
<div class="form-group">
<label for="inputOldPassword" translate>Old password</label>
<input type="password"
ng-model="oldPassword"
class="form-control"
name="inputOldPassword"
required>
</div>
<div class="form-group">
<label for="inputNewPassword" translate>New password</label>
<input type="password"
ng-model="newPassword"
class="form-control"
name="inputNewPassword"
required>
</div>
<div class="form-group">
<label for="inputNewPassword2" translate>Confirm new password</label>
<input type="password"
ng-model="newPassword2"
class="form-control"
name="inputNewPassword2"
required>
</div>
<button type="submit" ng-click="save()" class="btn btn-primary" translate>
Save
</button>
<button ui-sref="users.user.list" class="btn btn-default" translate>
Cancel
</button>
</form>
</div>

View File

@ -1,50 +0,0 @@
<div class="header">
<div class="title">
<h1 translate>Edit profile</h1>
</div>
</div>
<div class="details">
<p ng-if='formError' class="text-danger">
<strong>{{ formError }}</strong>
</p>
<form name="userForm" >
<div class="form-group">
<label for="inputUsername" translate>Username</label>
<input type="text"
ng-model="user.username"
class="form-control"
name="inputUsername"
required>
</div>
<div class="form-group row">
<div class="col-xs-2">
<label for="inputTitle" translate-comment="academic degree" translate>Title</label>
<input type="text" ng-model="user.title" class="form-control" name="inputTitle">
</div>
<div class="col-xs-5">
<label for="inputFirstName" translate>Given name</label>
<input type="text" ng-model="user.first_name" class="form-control" name="inputFirstName">
</div>
<div class="col-xs-5">
<label for="inputLastName" translate>Surname</label>
<input type="text" ng-model="user.last_name" class="form-control" name="inputLastName">
</div>
</div>
<div class="form-group">
<label for="inputStructureLevel" translate>Structure level</label>
<input type="text" ng-model="user.structure_level" class="form-control" name="inputStructureLevel">
</div>
<div class="form-group">
<label for="textAbout" translate>About me</label>
<textarea ng-model="user.about_me" class="form-control" name="textAbout" />
</div>
<button type="submit" ng-click="save(user)" class="btn btn-primary" translate>
Save
</button>
<button ui-sref="users.user.list" class="btn btn-default" translate>
Cancel
</button>
</form>
</div>

View File

@ -1,7 +1,7 @@
<h1 ng-if="model.id" translate>Edit participant</h1>
<h1 ng-if="!model.id" translate>New participant</h1>
<div uib-alert ng-show="alert.show" ng-class="'alert-' + (alert.type || 'warning')" ng-click="alert={}" close="alert={}">
<div uib-alert ng-show="alert.show" ng-class="'alert-' + (alert.type || 'warning')" close="alert={}">
{{ alert.msg }}
</div>

View File

@ -1,5 +1,6 @@
from django.contrib.auth import login as auth_login
from django.contrib.auth import logout as auth_logout
from django.contrib.auth import update_session_auth_hash
from django.contrib.auth.forms import AuthenticationForm
from django.utils.encoding import force_text
from django.utils.translation import ugettext as _
@ -39,8 +40,10 @@ class UserViewSet(ModelViewSet):
"""
if self.action in ('list', 'retrieve'):
result = self.get_access_permissions().check_permissions(self.request.user)
elif self.action in ('metadata', 'update', 'partial_update'):
elif self.action == 'metadata':
result = has_perm(self.request.user, 'users.can_see_name')
elif self.action in ('update', 'partial_update'):
result = self.request.user.is_authenticated()
elif self.action in ('create', 'destroy', 'reset_password'):
result = (has_perm(self.request.user, 'users.can_see_name') and
has_perm(self.request.user, 'users.can_see_extra_data') and
@ -264,6 +267,7 @@ class SetPasswordView(APIView):
if user.check_password(request.data['old_password']):
user.set_password(request.data['new_password'])
user.save()
update_session_auth_hash(request, user)
else:
raise ValidationError({'detail': _('Old password does not match.')})
return super().post(request, *args, **kwargs)