Merge pull request #1691 from emanuelschuetze/motions-permissions
Updated motion permission checks
This commit is contained in:
commit
cdd1813c02
@ -482,6 +482,9 @@ class Motion(RESTModelMixin, models.Model):
|
|||||||
* unsupport
|
* unsupport
|
||||||
* change_state
|
* change_state
|
||||||
* reset_state
|
* reset_state
|
||||||
|
|
||||||
|
NOTE: If you update this function please also update the
|
||||||
|
'isAllowed' function on client side in motions/site.js.
|
||||||
"""
|
"""
|
||||||
# TODO: Remove this method and implement these things in the views.
|
# TODO: Remove this method and implement these things in the views.
|
||||||
actions = {
|
actions = {
|
||||||
|
@ -158,12 +158,15 @@ angular.module('OpenSlidesApp.motions', [])
|
|||||||
'DS',
|
'DS',
|
||||||
'MotionPoll',
|
'MotionPoll',
|
||||||
'jsDataModel',
|
'jsDataModel',
|
||||||
function(DS, MotionPoll, jsDataModel) {
|
'gettext',
|
||||||
|
'operator',
|
||||||
|
'Config',
|
||||||
|
function(DS, MotionPoll, jsDataModel, gettext, operator, Config) {
|
||||||
var name = 'motions/motion'
|
var name = 'motions/motion'
|
||||||
return DS.defineResource({
|
return DS.defineResource({
|
||||||
name: name,
|
name: name,
|
||||||
useClass: jsDataModel,
|
useClass: jsDataModel,
|
||||||
agendaSupplement: '(Motion)',
|
agendaSupplement: '(' + gettext('Motion') + ')',
|
||||||
methods: {
|
methods: {
|
||||||
getResourceName: function () {
|
getResourceName: function () {
|
||||||
return name;
|
return name;
|
||||||
@ -195,6 +198,56 @@ angular.module('OpenSlidesApp.motions', [])
|
|||||||
value = this.identifier + ' | ';
|
value = this.identifier + ' | ';
|
||||||
}
|
}
|
||||||
return value + this.getTitle();
|
return value + this.getTitle();
|
||||||
|
},
|
||||||
|
isAllowed: function (action) {
|
||||||
|
/*
|
||||||
|
* Return true if the requested user is allowed to do the specific action.
|
||||||
|
* There are the following possible actions.
|
||||||
|
* - see
|
||||||
|
* - update
|
||||||
|
* - delete
|
||||||
|
* - create_poll
|
||||||
|
* - support
|
||||||
|
* - unsupport
|
||||||
|
* - change_state
|
||||||
|
* - reset_state
|
||||||
|
*
|
||||||
|
* NOTE: If you update this function please also update the
|
||||||
|
* 'get_allowed_actions' function on server side in motions/models.py.
|
||||||
|
*/
|
||||||
|
switch (action) {
|
||||||
|
case 'see':
|
||||||
|
return (operator.hasPerms('motions.can_see') &&
|
||||||
|
(!this.state.required_permission_to_see ||
|
||||||
|
operator.hasPerms(this.state.required_permission_to_see) ||
|
||||||
|
(operator.user in this.submitters)));
|
||||||
|
case 'update':
|
||||||
|
return (operator.hasPerms('motions.can_manage') ||
|
||||||
|
(($.inArray(operator.user, this.submitters) != -1) &&
|
||||||
|
this.state.allow_submitter_edit));
|
||||||
|
case 'quickedit':
|
||||||
|
return operator.hasPerms('motions.can_manage');
|
||||||
|
case 'delete':
|
||||||
|
return operator.hasPerms('motions.can_manage');
|
||||||
|
case 'create_poll':
|
||||||
|
return (operator.hasPerms('motions.can_manage') &&
|
||||||
|
this.state.allow_create_poll);
|
||||||
|
case 'support':
|
||||||
|
return (operator.hasPerms('motions.can_support') &&
|
||||||
|
this.state.allow_support &&
|
||||||
|
Config.get('motions_min_supporters').value > 0 &&
|
||||||
|
!($.inArray(operator.user, this.submitters) != -1) &&
|
||||||
|
!($.inArray(operator.user, this.supporters) != -1));
|
||||||
|
case 'unsupport':
|
||||||
|
return (this.state.allow_support &&
|
||||||
|
($.inArray(operator.user, this.supporters) != -1));
|
||||||
|
case 'change_state':
|
||||||
|
return operator.hasPerms('motions.can_manage');
|
||||||
|
case 'reset_state':
|
||||||
|
return operator.hasPerms('motions.can_manage');
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
relations: {
|
relations: {
|
||||||
@ -246,13 +299,14 @@ angular.module('OpenSlidesApp.motions', [])
|
|||||||
// Provide generic motion form fields for create and update view
|
// Provide generic motion form fields for create and update view
|
||||||
.factory('MotionFormFieldFactory', [
|
.factory('MotionFormFieldFactory', [
|
||||||
'gettext',
|
'gettext',
|
||||||
|
'operator',
|
||||||
'Category',
|
'Category',
|
||||||
'Config',
|
'Config',
|
||||||
'Mediafile',
|
'Mediafile',
|
||||||
'Tag',
|
'Tag',
|
||||||
'User',
|
'User',
|
||||||
'Workflow',
|
'Workflow',
|
||||||
function (gettext, Category, Config, Mediafile, Tag, User, Workflow) {
|
function (gettext, operator, Category, Config, Mediafile, Tag, User, Workflow) {
|
||||||
return {
|
return {
|
||||||
getFormFields: function () {
|
getFormFields: function () {
|
||||||
return [
|
return [
|
||||||
@ -275,7 +329,8 @@ angular.module('OpenSlidesApp.motions', [])
|
|||||||
valueProp: 'id',
|
valueProp: 'id',
|
||||||
labelProp: 'full_name',
|
labelProp: 'full_name',
|
||||||
placeholder: gettext('Select or search a submitter...')
|
placeholder: gettext('Select or search a submitter...')
|
||||||
}
|
},
|
||||||
|
hide: !operator.hasPerms('motions.can_manage')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'title',
|
key: 'title',
|
||||||
@ -305,7 +360,8 @@ angular.module('OpenSlidesApp.motions', [])
|
|||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
templateOptions: {
|
templateOptions: {
|
||||||
label: gettext('Show extended fields')
|
label: gettext('Show extended fields')
|
||||||
}
|
},
|
||||||
|
hide: !operator.hasPerms('motions.can_manage')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'attachments_id',
|
key: 'attachments_id',
|
||||||
@ -394,11 +450,12 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
|||||||
|
|
||||||
.config([
|
.config([
|
||||||
'mainMenuProvider',
|
'mainMenuProvider',
|
||||||
function (mainMenuProvider) {
|
'gettext',
|
||||||
|
function (mainMenuProvider, gettext) {
|
||||||
mainMenuProvider.register({
|
mainMenuProvider.register({
|
||||||
'ui_sref': 'motions.motion.list',
|
'ui_sref': 'motions.motion.list',
|
||||||
'img_class': 'file-text',
|
'img_class': 'file-text',
|
||||||
'title': 'Motions',
|
'title': gettext('Motions'),
|
||||||
'weight': 300,
|
'weight': 300,
|
||||||
'perm': 'motions.can_see',
|
'perm': 'motions.can_see',
|
||||||
});
|
});
|
||||||
@ -657,7 +714,6 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
|||||||
}
|
}
|
||||||
$scope.unsupport = function () {
|
$scope.unsupport = function () {
|
||||||
$http.delete('/rest/motions/motion/' + motion.id + '/support/');
|
$http.delete('/rest/motions/motion/' + motion.id + '/support/');
|
||||||
console.log(motion);
|
|
||||||
}
|
}
|
||||||
$scope.update_state = function (state) {
|
$scope.update_state = function (state) {
|
||||||
$http.put('/rest/motions/motion/' + motion.id + '/set_state/', {'state': state.id});
|
$http.put('/rest/motions/motion/' + motion.id + '/set_state/', {'state': state.id});
|
||||||
@ -704,6 +760,7 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
|||||||
'$scope',
|
'$scope',
|
||||||
'$state',
|
'$state',
|
||||||
'gettext',
|
'gettext',
|
||||||
|
'operator',
|
||||||
'Motion',
|
'Motion',
|
||||||
'MotionFormFieldFactory',
|
'MotionFormFieldFactory',
|
||||||
'Category',
|
'Category',
|
||||||
@ -712,7 +769,7 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
|||||||
'Tag',
|
'Tag',
|
||||||
'User',
|
'User',
|
||||||
'Workflow',
|
'Workflow',
|
||||||
function($scope, $state, gettext, Motion, MotionFormFieldFactory, Category, Config, Mediafile, Tag, User, Workflow) {
|
function($scope, $state, gettext, operator, Motion, MotionFormFieldFactory, Category, Config, Mediafile, Tag, User, Workflow) {
|
||||||
Category.bindAll({}, $scope, 'categories');
|
Category.bindAll({}, $scope, 'categories');
|
||||||
Mediafile.bindAll({}, $scope, 'mediafiles');
|
Mediafile.bindAll({}, $scope, 'mediafiles');
|
||||||
Tag.bindAll({}, $scope, 'tags');
|
Tag.bindAll({}, $scope, 'tags');
|
||||||
@ -726,6 +783,10 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
|||||||
if ($scope.formFields[i].key == "identifier") {
|
if ($scope.formFields[i].key == "identifier") {
|
||||||
$scope.formFields[i].hide = true;
|
$scope.formFields[i].hide = true;
|
||||||
}
|
}
|
||||||
|
if ($scope.formFields[i].key == "text") {
|
||||||
|
// set preamble config value as default text
|
||||||
|
$scope.formFields[i].defaultValue = Config.get('motions_preamble').value;
|
||||||
|
}
|
||||||
if ($scope.formFields[i].key == "workflow_id") {
|
if ($scope.formFields[i].key == "workflow_id") {
|
||||||
// preselect default workflow
|
// preselect default workflow
|
||||||
$scope.formFields[i].defaultValue = Config.get('motions_workflow').value;
|
$scope.formFields[i].defaultValue = Config.get('motions_workflow').value;
|
||||||
@ -738,7 +799,6 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
@ -798,6 +858,7 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
|||||||
$state.go('motions.motion.detail', {id: motion.id});
|
$state.go('motions.motion.detail', {id: motion.id});
|
||||||
})
|
})
|
||||||
.catch(function(fallback) {
|
.catch(function(fallback) {
|
||||||
|
//TODO: show error in GUI
|
||||||
console.log(fallback);
|
console.log(fallback);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
<i class="fa fa-video-camera"></i>
|
<i class="fa fa-video-camera"></i>
|
||||||
</a>
|
</a>
|
||||||
<!-- edit -->
|
<!-- edit -->
|
||||||
<a ng-if="motion.allowed_actions.update" ui-sref="motions.motion.detail.update({id: motion.id })"
|
<a ng-if="motion.isAllowed('update')" ui-sref="motions.motion.detail.update({id: motion.id })"
|
||||||
class="btn btn-default btn-sm"
|
class="btn btn-default btn-sm"
|
||||||
title="{{ 'Edit' | translate}}">
|
title="{{ 'Edit' | translate}}">
|
||||||
<i class="fa fa-pencil"></i>
|
<i class="fa fa-pencil"></i>
|
||||||
@ -79,12 +79,12 @@
|
|||||||
{{ supporters.get_full_name() }}
|
{{ supporters.get_full_name() }}
|
||||||
</ol>
|
</ol>
|
||||||
<!-- support button -->
|
<!-- support button -->
|
||||||
<button ng-if="motion.allowed_actions.support" ng-click="support()" class="btn btn-primary btn-sm">
|
<button ng-if="motion.isAllowed('support')" ng-click="support()" class="btn btn-default btn-sm">
|
||||||
<i class="fa fa-heart"></i>
|
<i class="fa fa-heart"></i>
|
||||||
<translate>Support motion</translate>
|
<translate>Support motion</translate>
|
||||||
</button>
|
</button>
|
||||||
<!-- unsupport button -->
|
<!-- unsupport button -->
|
||||||
<button ng-if="motion.allowed_actions.unsupport" ng-click="unsupport()" class="btn btn-default btn-sm">
|
<button ng-if="motion.isAllowed('unsupport')" ng-click="unsupport()" class="btn btn-default btn-sm">
|
||||||
<i class="fa fa-heart-o"></i>
|
<i class="fa fa-heart-o"></i>
|
||||||
<translate>Unsupport motion</translate>
|
<translate>Unsupport motion</translate>
|
||||||
</button>
|
</button>
|
||||||
@ -94,13 +94,13 @@
|
|||||||
<span class="label" ng-class="'label-'+motion.state.css_class">
|
<span class="label" ng-class="'label-'+motion.state.css_class">
|
||||||
{{ motion.state.name | translate }}
|
{{ motion.state.name | translate }}
|
||||||
</span>
|
</span>
|
||||||
<div ng-if="motion.allowed_actions.change_state" class="spacer">
|
<div ng-if="motion.isAllowed('change_state')" class="spacer">
|
||||||
<div class="btn-group-vertical spacer" role="group">
|
<div class="btn-group-vertical spacer" role="group">
|
||||||
<button ng-repeat="state in motion.state.getNextStates()" ng-click="update_state(state)"
|
<button ng-repeat="state in motion.state.getNextStates()" ng-click="update_state(state)"
|
||||||
class="btn btn-default btn-sm">
|
class="btn btn-default btn-sm">
|
||||||
{{state.action_word}}
|
{{state.action_word}}
|
||||||
</button>
|
</button>
|
||||||
<button ng-if="motion.allowed_actions.reset_state" ng-click="reset_state()"
|
<button ng-if="motion.isAllowed('reset_state')" ng-click="reset_state()"
|
||||||
class="btn btn-danger btn-xs">
|
class="btn btn-danger btn-xs">
|
||||||
<i class="fa fa-exclamation-triangle"></i>
|
<i class="fa fa-exclamation-triangle"></i>
|
||||||
<translate>Reset state</translate>
|
<translate>Reset state</translate>
|
||||||
@ -227,7 +227,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ol>
|
</ol>
|
||||||
<button ng-if="motion.allowed_actions.create_poll" ng-click="create_poll()" class="btn btn-default btn-sm">
|
<button ng-if="motion.isAllowed('create_poll')" ng-click="create_poll()" class="btn btn-default btn-sm">
|
||||||
<i class="fa fa-bar-chart fa-lg"></i>
|
<i class="fa fa-bar-chart fa-lg"></i>
|
||||||
<translate>New poll</translate>
|
<translate>New poll</translate>
|
||||||
</button>
|
</button>
|
||||||
|
@ -107,14 +107,14 @@
|
|||||||
<td ng-if="!motion.quickEdit">{{ motion.identifier }}
|
<td ng-if="!motion.quickEdit">{{ motion.identifier }}
|
||||||
<td ng-if="!motion.quickEdit" ng-mouseover="motion.hover=true" ng-mouseleave="motion.hover=false">
|
<td ng-if="!motion.quickEdit" ng-mouseover="motion.hover=true" ng-mouseleave="motion.hover=false">
|
||||||
<strong><a ui-sref="motions.motion.detail({id: motion.id})">{{ motion.getTitle() }}</a></strong>
|
<strong><a ui-sref="motions.motion.detail({id: motion.id})">{{ motion.getTitle() }}</a></strong>
|
||||||
<div class="hoverActions" ng-class="{'hiddenDiv': !motion.hover}">
|
<div ng-if="motion.isAllowed('update')" class="hoverActions" ng-class="{'hiddenDiv': !motion.hover}">
|
||||||
<span ng-if="motion.allowed_actions.update">
|
<span ng-if="motion.isAllowed('update')">
|
||||||
<a ui-sref="motions.motion.detail.update({ id: motion.id })" translate>Edit</a> |
|
<a ui-sref="motions.motion.detail.update({ id: motion.id })" translate>Edit</a>
|
||||||
</span>
|
</span>
|
||||||
<span ng-if="motion.allowed_actions.update">
|
<span ng-if="motion.isAllowed('quickedit')">
|
||||||
<a href="" os-perms="motions.can_manage" ng-click="motion.quickEdit=true" translate>QuickEdit</a> |
|
| <a href="" ng-click="motion.quickEdit=true" translate>QuickEdit</a> |
|
||||||
</span>
|
</span>
|
||||||
<span ng-if="motion.allowed_actions.delete">
|
<span ng-if="motion.isAllowed('delete')">
|
||||||
<!-- TODO: translate confirm message -->
|
<!-- TODO: translate confirm message -->
|
||||||
<a href="" class="text-danger"
|
<a href="" class="text-danger"
|
||||||
ng-bootbox-confirm="Are you sure you want to delete <b>{{ motion.getTitle() }}</b>?"
|
ng-bootbox-confirm="Are you sure you want to delete <b>{{ motion.getTitle() }}</b>?"
|
||||||
@ -132,7 +132,7 @@
|
|||||||
{{ motion.state.name | translate }}
|
{{ motion.state.name | translate }}
|
||||||
</span>
|
</span>
|
||||||
<!-- quickEdit columns -->
|
<!-- quickEdit columns -->
|
||||||
<td ng-if="motion.quickEdit && motion.allowed_actions.update" colspan="5">
|
<td ng-if="motion.quickEdit && motion.isAllowed('quickedit')" colspan="5">
|
||||||
<h4>{{ motion.getTitle() }} <span class="text-muted">– Quick Edit</span></h4>
|
<h4>{{ motion.getTitle() }} <span class="text-muted">– Quick Edit</span></h4>
|
||||||
<alert ng-show="alert.show" type="{{ alert.type }}" ng-click="alert={}" close="alert={}">
|
<alert ng-show="alert.show" type="{{ alert.type }}" ng-click="alert={}" close="alert={}">
|
||||||
{{alert.msg}}
|
{{alert.msg}}
|
||||||
@ -193,10 +193,10 @@
|
|||||||
<button ng-click="motion.quickEdit=false" class="btn btn-default pull-left" translate>
|
<button ng-click="motion.quickEdit=false" class="btn btn-default pull-left" translate>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
<button ng-if="motion.allowed_actions.update" ng-click="update(motion)" class="btn btn-primary" translate>
|
<button ng-if="motion.isAllowed('update')" ng-click="update(motion)" class="btn btn-primary" translate>
|
||||||
Update
|
Update
|
||||||
</button>
|
</button>
|
||||||
<a ng-if="motion.allowed_actions.update" ui-sref="motions.motion.detail.update({id: motion.id })"
|
<a ng-if="motion.isAllowed('update')" ui-sref="motions.motion.detail.update({id: motion.id })"
|
||||||
class="pull-right" translate>Edit motion...</a>
|
class="pull-right" translate>Edit motion...</a>
|
||||||
</div>
|
</div>
|
||||||
</table>
|
</table>
|
||||||
|
@ -66,16 +66,6 @@ class MotionViewSet(ModelViewSet):
|
|||||||
result = False
|
result = False
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Customized view endpoint to retrieve a motion.
|
|
||||||
|
|
||||||
Adds the allowed actions for the motion.
|
|
||||||
"""
|
|
||||||
response = super().retrieve(request, *args, **kwargs)
|
|
||||||
response.data['allowed_actions'] = self.get_object().get_allowed_actions(request.user)
|
|
||||||
return response
|
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Customized view endpoint to create a new motion.
|
Customized view endpoint to create a new motion.
|
||||||
|
@ -172,7 +172,8 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
|
|||||||
'operator',
|
'operator',
|
||||||
'$rootScope',
|
'$rootScope',
|
||||||
'$http',
|
'$http',
|
||||||
function(operator, $rootScope, $http) {
|
'Group',
|
||||||
|
function(operator, $rootScope, $http, Group) {
|
||||||
// Put the operator into the root scope
|
// Put the operator into the root scope
|
||||||
$http.get('/users/whoami/').success(function(data) {
|
$http.get('/users/whoami/').success(function(data) {
|
||||||
operator.setUser(data.user_id);
|
operator.setUser(data.user_id);
|
||||||
|
Loading…
Reference in New Issue
Block a user