Better dialog handling. Many fixes.

This commit is contained in:
FinnStutzenstein 2017-02-10 14:51:44 +01:00
parent 9a1e45682c
commit 735dbcf109
26 changed files with 476 additions and 350 deletions

View File

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

View File

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

View File

@ -682,8 +682,9 @@ angular.module('OpenSlidesApp.core', [
.factory('Projector', [ .factory('Projector', [
'DS', 'DS',
'$http', '$http',
'$injector',
'Config', 'Config',
function(DS, $http, Config) { function(DS, $http, $injector, Config) {
return DS.defineResource({ return DS.defineResource({
name: 'core/projector', name: 'core/projector',
onConflict: 'replace', onConflict: 'replace',
@ -701,13 +702,13 @@ angular.module('OpenSlidesApp.core', [
{"action": action, "direction": direction} {"action": action, "direction": direction}
); );
}, },
getStateForCurrentSlide: function () { getFormOrStateForCurrentSlide: function () {
var return_dict; var return_dict;
angular.forEach(this.elements, function(value, key) { angular.forEach(this.elements, function(value, key) {
if (value.name == 'agenda/list-of-speakers') { if (value.name == 'agenda/list-of-speakers') {
return_dict = { return_dict = {
'state': 'agenda.item.detail', state: 'agenda.item.detail',
'param': {id: value.id} id: value.id,
}; };
} else if ( } else if (
value.name != 'agenda/item-list' && value.name != 'agenda/item-list' &&
@ -715,10 +716,15 @@ angular.module('OpenSlidesApp.core', [
value.name != 'core/countdown' && value.name != 'core/countdown' &&
value.name != 'core/projector-message' && value.name != 'core/projector-message' &&
value.name != 'agenda/current-list-of-speakers' ) { value.name != 'agenda/current-list-of-speakers' ) {
return_dict = { var formName = value.name.split('/')[1];
'state': value.name.replace('/', '.')+'.detail.update', // Hotfix for Issue 2566.
'param': {id: value.id} // 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 = {
form: $injector.get(formName),
id: value.id
};
} }
}); });
return return_dict; return return_dict;

View File

@ -370,25 +370,7 @@ angular.module('OpenSlidesApp.core.site', [
basePerm: 'core.can_manage_tags', basePerm: 'core.can_manage_tags',
}, },
}) })
.state('core.tag.list', {}) .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': {}
}
});
$locationProvider.html5Mode(true); $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 /* This factory handles the filtering of the OS-data-tables. It contains
* all logic needed for the table header filtering. Things to configure: * 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. * - 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', extends: 'textarea',
templateUrl: 'static/templates/core/editor.html', templateUrl: 'static/templates/core/editor.html',
}); });
formlyConfig.setType({
name: 'password',
extends: 'input',
templateUrl: 'static/templates/core/password.html',
});
formlyConfig.setType({ formlyConfig.setType({
name: 'select-single', name: 'select-single',
extends: 'select', extends: 'select',
@ -1122,9 +1141,13 @@ angular.module('OpenSlidesApp.core.site', [
}; };
$scope.editCurrentSlide = function (projector) { $scope.editCurrentSlide = function (projector) {
var state = projector.getStateForCurrentSlide(); var data = projector.getFormOrStateForCurrentSlide();
if (state) { if (data) {
$state.go(state.state, state.param); 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', [ .controller('ManageProjectorsCtrl', [
'$scope', '$scope',
'$http', '$http',
'$state',
'$timeout', '$timeout',
'Projector', 'Projector',
'ProjectionDefault', 'ProjectionDefault',
'Config', 'Config',
'ProjectorMessage', 'ProjectorMessage',
function ($scope, $http, $state, $timeout, Projector, ProjectionDefault, Config, ProjectorMessage) { 'ngDialog',
function ($scope, $http, $timeout, Projector, ProjectionDefault, Config,
ProjectorMessage, ngDialog) {
ProjectionDefault.bindAll({}, $scope, 'projectiondefaults'); ProjectionDefault.bindAll({}, $scope, 'projectiondefaults');
// watch for changes in projector_broadcast // watch for changes in projector_broadcast
@ -1316,9 +1340,13 @@ angular.module('OpenSlidesApp.core.site', [
} }
}; };
$scope.editCurrentSlide = function (projector) { $scope.editCurrentSlide = function (projector) {
var state = projector.getStateForCurrentSlide(); var data = projector.getFormOrStateForCurrentSlide();
if (state) { if (data) {
$state.go(state.state, state.param); if (data.form) {
ngDialog.open(data.form.getDialog({id: data.id}));
} else {
$state.go(data.state, {id: data.id});
}
} }
}; };
$scope.editName = function (projector) { $scope.editName = function (projector) {
@ -1378,8 +1406,12 @@ angular.module('OpenSlidesApp.core.site', [
.controller('TagListCtrl', [ .controller('TagListCtrl', [
'$scope', '$scope',
'Tag', 'Tag',
function($scope, Tag) { 'ngDialog',
'TagForm',
'gettext',
function($scope, Tag, ngDialog, TagForm, gettext) {
Tag.bindAll({}, $scope, 'tags'); Tag.bindAll({}, $scope, 'tags');
$scope.alert = {};
// setup table sorting // setup table sorting
$scope.sortColumn = 'name'; $scope.sortColumn = 'name';
@ -1391,40 +1423,48 @@ angular.module('OpenSlidesApp.core.site', [
} }
$scope.sortColumn = column; $scope.sortColumn = column;
}; };
// save changed tag
$scope.save = function (tag) {
Tag.save(tag);
};
$scope.delete = function (tag) { $scope.delete = function (tag) {
Tag.destroy(tag.id).then( Tag.destroy(tag.id).then(
function(success) { 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};
} }
); );
}; };
} $scope.editOrCreate = function (tag) {
]) ngDialog.open(TagForm.getDialog(tag));
};
.controller('TagDetailCtrl', [
'$scope',
'Tag',
'tagId',
function($scope, Tag, tagId) {
Tag.bindOne(tagId, $scope, 'tag');
} }
]) ])
.controller('TagCreateCtrl', [ .controller('TagCreateCtrl', [
'$scope', '$scope',
'$state',
'Tag', 'Tag',
function($scope, $state, Tag) { 'TagForm',
$scope.tag = {}; function($scope, Tag, TagForm) {
$scope.model = {};
$scope.alert = {};
$scope.formFields = TagForm.getFormFields();
$scope.save = function (tag) { $scope.save = function (tag) {
Tag.create(tag).then( Tag.create(tag).then(
function(success) { 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', [ .controller('TagUpdateCtrl', [
'$scope', '$scope',
'$state',
'Tag', 'Tag',
'tagId', 'tagId',
function($scope, $state, Tag, tagId) { 'TagForm',
$scope.tag = Tag.get(tagId); function($scope, Tag, tagId, TagForm) {
$scope.model = angular.copy(Tag.get(tagId));
$scope.alert = {};
$scope.formFields = TagForm.getFormFields();
$scope.save = function (tag) { $scope.save = function (tag) {
Tag.save(tag).then( Tag.inject(tag);
function(success) { Tag.save(tag).then(function(success) {
$state.go('core.tag.list'); $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 // increment unread messages counter for each new message
$scope.$watch('chatmessages', function (newVal, oldVal) { $scope.$watch('chatmessages', function (newVal, oldVal) {
// add new message id if there is really a new message which is not yet tracked // 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) && if ((oldVal[oldVal.length-1].id != newVal[newVal.length-1].id) &&
($.inArray(newVal[newVal.length-1].id, NewChatMessages) == -1)) { ($.inArray(newVal[newVal.length-1].id, NewChatMessages) == -1)) {
NewChatMessages.push(newVal[newVal.length-1].id); NewChatMessages.push(newVal[newVal.length-1].id);
$scope.unreadMessages = NewChatMessages.length; $scope.unreadMessages = NewChatMessages.length;
} }
} else if (newVal.length === 0) {
NewChatMessages = [];
$scope.unreadMessages = 0;
} }
}); });

View File

@ -133,7 +133,7 @@
<div class="nobr"> <div class="nobr">
<!-- edit --> <!-- edit -->
<a ng-click="editCurrentSlide(projector)" <a ng-click="editCurrentSlide(projector)"
ng-disabled="!projector.getStateForCurrentSlide()" ng-disabled="!projector.getFormOrStateForCurrentSlide()"
class="btn btn-default btn-sm" class="btn btn-default btn-sm"
title="{{ 'Edit current slide' | translate}}"> title="{{ 'Edit current slide' | translate}}">
<i class="fa fa-pencil"></i> <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"> <div os-perms="core.can_manage_projector" class="nobr">
<!-- edit --> <!-- edit -->
<a ng-click="editCurrentSlide(active_projector)" <a ng-click="editCurrentSlide(active_projector)"
ng-disabled="!active_projector.getStateForCurrentSlide()" ng-disabled="!active_projector.getFormOrStateForCurrentSlide()"
class="btn btn-default btn-sm" class="btn btn-default btn-sm"
title="{{ 'Edit current slide' | translate}}"> title="{{ 'Edit current slide' | translate}}">
<i class="fa fa-pencil"></i> <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"> <h1 ng-if="model.id" translate>Edit tag</h1>
<div class="title"> <h1 ng-if="!model.id" translate>New tag</h1>
<div class="submenu">
<a ui-sref="core.tag.list" class="btn btn-sm btn-default"> <div uib-alert ng-show="alert.show" ng-class="'alert-' + (alert.type || 'warning')" close="alert={}">
<i class="fa fa-angle-double-left fa-lg"></i> {{ alert.msg }}
<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>
</div> </div>
<div class="details"> <form name="userForm" ng-submit="save(model)">
<form name="tagForm"> <formly-form model="model" fields="formFields">
<div class="form-group"> <button type="submit" ng-disabled="userForm.$invalid" class="btn btn-primary" translate>
<label for="inputName" translate>Name</label> Save
<input type="text" ng-model="tag.name" class="form-control" name="inputName" required> </button>
</div> <button ng-click="closeThisDialog()" class="btn btn-default" translate>
Cancel
<button type="submit" ng-click="save(tag)" class="btn btn-primary" translate> </button>
Save </formly-form>
</button>
<button ui-sref="core.tag.list" class="btn btn-default" translate>
Cancel
</button>
</form> </form>
</div>

View File

@ -5,7 +5,7 @@
<i class="fa fa-angle-double-left fa-lg"></i> <i class="fa fa-angle-double-left fa-lg"></i>
<translate>Back to overview</translate> <translate>Back to overview</translate>
</a> </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> <i class="fa fa-plus fa-lg"></i>
<translate>New</translate> <translate>New</translate>
</a> </a>
@ -16,7 +16,11 @@
<div class="details"> <div class="details">
<div class="row form-group"> <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"> <div class="col-sm-4">
<input type="text" ng-model="filter.search" class="form-control" <input type="text" ng-model="filter.search" class="form-control"
placeholder="{{ 'Filter' | translate}}"> placeholder="{{ 'Filter' | translate}}">
@ -37,7 +41,7 @@
<td ng-mouseover="tag.hover=true" ng-mouseleave="tag.hover=false"> <td ng-mouseover="tag.hover=true" ng-mouseleave="tag.hover=false">
<strong>{{ tag.name }}</strong> <strong>{{ tag.name }}</strong>
<div class="hoverActions" ng-class="{'hiddenDiv': !tag.hover}"> <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" <a href="" class="text-danger"
ng-bootbox-confirm="{{ 'Are you sure you want to delete this entry?' | translate }}<br> ng-bootbox-confirm="{{ 'Are you sure you want to delete this entry?' | translate }}<br>
<b>{{ tag.name }}</b>" <b>{{ tag.name }}</b>"

View File

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

View File

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

View File

@ -103,19 +103,6 @@ angular.module('OpenSlidesApp.motions.site', [
}, },
}) })
.state('motions.category.list', {}) .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', { .state('motions.category.sort', {
url: '/sort/{id}', url: '/sort/{id}',
controller: 'CategorySortCtrl', 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 // Provide generic motionpoll form fields for poll update view
.factory('MotionPollForm', [ .factory('MotionPollForm', [
'gettextCatalog', 'gettextCatalog',
@ -1767,7 +1793,9 @@ angular.module('OpenSlidesApp.motions.site', [
.controller('CategoryListCtrl', [ .controller('CategoryListCtrl', [
'$scope', '$scope',
'Category', 'Category',
function($scope, Category) { 'ngDialog',
'CategoryForm',
function($scope, Category, ngDialog, CategoryForm) {
Category.bindAll({}, $scope, 'categories'); Category.bindAll({}, $scope, 'categories');
// setup table sorting // setup table sorting
@ -1785,28 +1813,31 @@ angular.module('OpenSlidesApp.motions.site', [
$scope.delete = function (category) { $scope.delete = function (category) {
Category.destroy(category.id); Category.destroy(category.id);
}; };
} $scope.editOrCreate = function (category) {
]) ngDialog.open(CategoryForm.getDialog(category));
};
.controller('CategoryDetailCtrl', [
'$scope',
'Category',
'categoryId',
function($scope, Category, categoryId) {
Category.bindOne(categoryId, $scope, 'category');
} }
]) ])
.controller('CategoryCreateCtrl', [ .controller('CategoryCreateCtrl', [
'$scope', '$scope',
'$state',
'Category', 'Category',
function($scope, $state, Category) { 'CategoryForm',
$scope.category = {}; function($scope, Category, CategoryForm) {
$scope.model = {};
$scope.alert = {};
$scope.formFields = CategoryForm.getFormFields();
$scope.save = function (category) { $scope.save = function (category) {
Category.create(category).then( Category.create(category).then(
function(success) { 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};
} }
); );
}; };
@ -1815,15 +1846,28 @@ angular.module('OpenSlidesApp.motions.site', [
.controller('CategoryUpdateCtrl', [ .controller('CategoryUpdateCtrl', [
'$scope', '$scope',
'$state',
'Category', 'Category',
'categoryId', 'categoryId',
function($scope, $state, Category, categoryId) { 'CategoryForm',
$scope.category = Category.get(categoryId); function($scope, Category, categoryId, CategoryForm) {
$scope.alert = {};
$scope.model = angular.copy(Category.get(categoryId));
$scope.formFields = CategoryForm.getFormFields();
$scope.save = function (category) { $scope.save = function (category) {
Category.inject(category);
Category.save(category).then( Category.save(category).then(
function(success) { 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"> <h1 ng-if="model.id" translate>Edit category</h1>
<div class="title"> <h1 ng-if="!model.id" translate>New category</h1>
<div class="submenu">
<a ui-sref="motions.category.list" class="btn btn-sm btn-default"> <div uib-alert ng-show="alert.show" ng-class="'alert-' + (alert.type || 'warning')" close="alert={}">
<i class="fa fa-angle-double-left fa-lg"></i> {{ alert.msg }}
<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>
</div> </div>
<div class="details"> <form name="categoryForm" ng-submit="save(model)">
<form name="groupForm"> <formly-form model="model" fields="formFields">
<div class="form-group"> <button type="submit" ng-disabled="categoryForm.$invalid" class="btn btn-primary" translate>
<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>
Save Save
</button> </button>
<button ui-sref="motions.category.list" class="btn btn-default" translate> <button ng-click="closeThisDialog()" class="btn btn-default" translate>
Cancel Cancel
</button> </button>
</form> </formly-form>
</div> </form>

View File

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

View File

@ -1,9 +1,9 @@
<h1 ng-if="model.id" translate>Edit change recommendation</h1> <h1 ng-if="model.id" translate>Edit change recommendation</h1>
<h1 ng-if="!model.id" translate>New 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 }} {{ alert.msg }}
</uib-alert> </div>
<form name="changeRecommendationForm" ng-submit="save(model)"> <form name="changeRecommendationForm" ng-submit="save(model)">
<formly-form model="model" fields="formFields"> <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>Edit motion block</h1>
<h1 ng-if="!model.id" translate>New 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 }} {{ alert.msg }}
</div> </div>

View File

@ -2,7 +2,7 @@
<h1 ng-if="!model.id && !parent" translate>New motion</h1> <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> <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 }} {{ alert.msg }}
</div> </div>

View File

@ -10,9 +10,9 @@ class UserAccessPermissions(BaseAccessPermissions):
""" """
def check_permissions(self, user): 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): 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', { .state('users.user.change-password', {
url: '/change-password/{id}', url: '/change-password/{id}',
controller: 'UserChangePasswordCtrl', 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', [ .controller('UserListCtrl', [
'$scope', '$scope',
'$state', '$state',
@ -761,20 +867,30 @@ angular.module('OpenSlidesApp.users.site', [
.controller('UserProfileCtrl', [ .controller('UserProfileCtrl', [
'$scope', '$scope',
'$state',
'Editor', 'Editor',
'User', 'User',
'userId', 'operator',
function($scope, $state, Editor, User, userId) { 'UserProfileForm',
$scope.user = angular.copy(User.get(userId)); 'gettext',
$scope.ckeditorOptions = Editor.getOptions(); 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) { $scope.save = function (user) {
User.inject(user);
User.save(user).then( User.save(user).then(
function(success) { function(success) {
$state.go('users.user.list'); $scope.closeThisDialog();
}, },
function(error) { 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};
} }
); );
}; };
@ -791,7 +907,7 @@ angular.module('OpenSlidesApp.users.site', [
'PasswordGenerator', 'PasswordGenerator',
function($scope, $state, $http, User, userId, gettextCatalog, PasswordGenerator) { function($scope, $state, $http, User, userId, gettextCatalog, PasswordGenerator) {
User.bindOne(userId, $scope, 'user'); User.bindOne(userId, $scope, 'user');
$scope.alert={}; $scope.alert = {};
$scope.generatePassword = function () { $scope.generatePassword = function () {
$scope.new_password = PasswordGenerator.generate(); $scope.new_password = PasswordGenerator.generate();
}; };
@ -818,24 +934,38 @@ angular.module('OpenSlidesApp.users.site', [
'$scope', '$scope',
'$state', '$state',
'$http', '$http',
function($scope, $state, $http) { 'gettext',
$scope.save = function () { 'UserPasswordForm',
if ($scope.newPassword != $scope.newPassword2) { function($scope, $state, $http, gettext, UserPasswordForm) {
$scope.newPassword = $scope.newPassword2 = ''; $scope.title = 'Change password';
$scope.formError = 'Password confirmation does not match.'; $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 { } else {
$http.post( $http.post(
'/users/setpassword/', '/users/setpassword/',
{'old_password': $scope.oldPassword, 'new_password': $scope.newPassword} {'old_password': data.oldPassword, 'new_password': data.newPassword}
).then( ).then(
function (response) { function (success) {
// Success. $scope.closeThisDialog();
$state.go('users.user.list');
}, },
function (response) { function (error) {
// Error, e. g. wrong old password. // Error, e. g. wrong old password.
$scope.oldPassword = $scope.newPassword = $scope.newPassword2 = ''; $scope.model = {};
$scope.formError = response.data.detail;
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', 'User',
'operator', 'operator',
'ngDialog', 'ngDialog',
function($scope, $http, DS, User, operator, ngDialog) { 'UserProfileForm',
'UserPasswordForm',
function($scope, $http, DS, User, operator, ngDialog, UserProfileForm, UserPasswordForm) {
$scope.logout = function () { $scope.logout = function () {
$http.post('/users/logout/').then(function (response) { $http.post('/users/logout/').then(function (response) {
operator.setUser(null); operator.setUser(null);
window.location.reload(); 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>Edit participant</h1>
<h1 ng-if="!model.id" translate>New 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 }} {{ alert.msg }}
</div> </div>

View File

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