Updated motion permission checks.
- Add permission checks in motion form (Fixes #1677) - Implement get_allowed_actions in motions JavaScript (Fixes #1668)
This commit is contained in:
parent
2458cc97fb
commit
c93e20b1b3
@ -482,6 +482,9 @@ class Motion(RESTModelMixin, models.Model):
|
||||
* unsupport
|
||||
* change_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.
|
||||
actions = {
|
||||
|
@ -158,12 +158,15 @@ angular.module('OpenSlidesApp.motions', [])
|
||||
'DS',
|
||||
'MotionPoll',
|
||||
'jsDataModel',
|
||||
function(DS, MotionPoll, jsDataModel) {
|
||||
'gettext',
|
||||
'operator',
|
||||
'Config',
|
||||
function(DS, MotionPoll, jsDataModel, gettext, operator, Config) {
|
||||
var name = 'motions/motion'
|
||||
return DS.defineResource({
|
||||
name: name,
|
||||
useClass: jsDataModel,
|
||||
agendaSupplement: '(Motion)',
|
||||
agendaSupplement: '(' + gettext('Motion') + ')',
|
||||
methods: {
|
||||
getResourceName: function () {
|
||||
return name;
|
||||
@ -195,6 +198,56 @@ angular.module('OpenSlidesApp.motions', [])
|
||||
value = this.identifier + ' | ';
|
||||
}
|
||||
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: {
|
||||
@ -246,13 +299,14 @@ angular.module('OpenSlidesApp.motions', [])
|
||||
// Provide generic motion form fields for create and update view
|
||||
.factory('MotionFormFieldFactory', [
|
||||
'gettext',
|
||||
'operator',
|
||||
'Category',
|
||||
'Config',
|
||||
'Mediafile',
|
||||
'Tag',
|
||||
'User',
|
||||
'Workflow',
|
||||
function (gettext, Category, Config, Mediafile, Tag, User, Workflow) {
|
||||
function (gettext, operator, Category, Config, Mediafile, Tag, User, Workflow) {
|
||||
return {
|
||||
getFormFields: function () {
|
||||
return [
|
||||
@ -275,7 +329,8 @@ angular.module('OpenSlidesApp.motions', [])
|
||||
valueProp: 'id',
|
||||
labelProp: 'full_name',
|
||||
placeholder: gettext('Select or search a submitter...')
|
||||
}
|
||||
},
|
||||
hide: !operator.hasPerms('motions.can_manage')
|
||||
},
|
||||
{
|
||||
key: 'title',
|
||||
@ -305,7 +360,8 @@ angular.module('OpenSlidesApp.motions', [])
|
||||
type: 'checkbox',
|
||||
templateOptions: {
|
||||
label: gettext('Show extended fields')
|
||||
}
|
||||
},
|
||||
hide: !operator.hasPerms('motions.can_manage')
|
||||
},
|
||||
{
|
||||
key: 'attachments_id',
|
||||
@ -394,11 +450,12 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
||||
|
||||
.config([
|
||||
'mainMenuProvider',
|
||||
function (mainMenuProvider) {
|
||||
'gettext',
|
||||
function (mainMenuProvider, gettext) {
|
||||
mainMenuProvider.register({
|
||||
'ui_sref': 'motions.motion.list',
|
||||
'img_class': 'file-text',
|
||||
'title': 'Motions',
|
||||
'title': gettext('Motions'),
|
||||
'weight': 300,
|
||||
'perm': 'motions.can_see',
|
||||
});
|
||||
@ -657,7 +714,6 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
||||
}
|
||||
$scope.unsupport = function () {
|
||||
$http.delete('/rest/motions/motion/' + motion.id + '/support/');
|
||||
console.log(motion);
|
||||
}
|
||||
$scope.update_state = function (state) {
|
||||
$http.put('/rest/motions/motion/' + motion.id + '/set_state/', {'state': state.id});
|
||||
@ -704,6 +760,7 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
||||
'$scope',
|
||||
'$state',
|
||||
'gettext',
|
||||
'operator',
|
||||
'Motion',
|
||||
'MotionFormFieldFactory',
|
||||
'Category',
|
||||
@ -712,7 +769,7 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
||||
'Tag',
|
||||
'User',
|
||||
'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');
|
||||
Mediafile.bindAll({}, $scope, 'mediafiles');
|
||||
Tag.bindAll({}, $scope, 'tags');
|
||||
@ -726,6 +783,10 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
||||
if ($scope.formFields[i].key == "identifier") {
|
||||
$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") {
|
||||
// preselect default workflow
|
||||
$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});
|
||||
})
|
||||
.catch(function(fallback) {
|
||||
//TODO: show error in GUI
|
||||
console.log(fallback);
|
||||
});
|
||||
};
|
||||
|
@ -25,7 +25,7 @@
|
||||
<i class="fa fa-video-camera"></i>
|
||||
</a>
|
||||
<!-- 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"
|
||||
title="{{ 'Edit' | translate}}">
|
||||
<i class="fa fa-pencil"></i>
|
||||
@ -79,12 +79,12 @@
|
||||
{{ supporters.get_full_name() }}
|
||||
</ol>
|
||||
<!-- 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>
|
||||
<translate>Support motion</translate>
|
||||
</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>
|
||||
<translate>Unsupport motion</translate>
|
||||
</button>
|
||||
@ -94,13 +94,13 @@
|
||||
<span class="label" ng-class="'label-'+motion.state.css_class">
|
||||
{{ motion.state.name | translate }}
|
||||
</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">
|
||||
<button ng-repeat="state in motion.state.getNextStates()" ng-click="update_state(state)"
|
||||
class="btn btn-default btn-sm">
|
||||
{{state.action_word}}
|
||||
</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">
|
||||
<i class="fa fa-exclamation-triangle"></i>
|
||||
<translate>Reset state</translate>
|
||||
@ -227,7 +227,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
<translate>New poll</translate>
|
||||
</button>
|
||||
|
@ -107,14 +107,14 @@
|
||||
<td ng-if="!motion.quickEdit">{{ motion.identifier }}
|
||||
<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>
|
||||
<div class="hoverActions" ng-class="{'hiddenDiv': !motion.hover}">
|
||||
<span ng-if="motion.allowed_actions.update">
|
||||
<a ui-sref="motions.motion.detail.update({ id: motion.id })" translate>Edit</a> |
|
||||
<div ng-if="motion.isAllowed('update')" class="hoverActions" ng-class="{'hiddenDiv': !motion.hover}">
|
||||
<span ng-if="motion.isAllowed('update')">
|
||||
<a ui-sref="motions.motion.detail.update({ id: motion.id })" translate>Edit</a>
|
||||
</span>
|
||||
<span ng-if="motion.allowed_actions.update">
|
||||
<a href="" os-perms="motions.can_manage" ng-click="motion.quickEdit=true" translate>QuickEdit</a> |
|
||||
<span ng-if="motion.isAllowed('quickedit')">
|
||||
| <a href="" ng-click="motion.quickEdit=true" translate>QuickEdit</a> |
|
||||
</span>
|
||||
<span ng-if="motion.allowed_actions.delete">
|
||||
<span ng-if="motion.isAllowed('delete')">
|
||||
<!-- TODO: translate confirm message -->
|
||||
<a href="" class="text-danger"
|
||||
ng-bootbox-confirm="Are you sure you want to delete <b>{{ motion.getTitle() }}</b>?"
|
||||
@ -132,7 +132,7 @@
|
||||
{{ motion.state.name | translate }}
|
||||
</span>
|
||||
<!-- 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>
|
||||
<alert ng-show="alert.show" type="{{ alert.type }}" ng-click="alert={}" close="alert={}">
|
||||
{{alert.msg}}
|
||||
@ -193,10 +193,10 @@
|
||||
<button ng-click="motion.quickEdit=false" class="btn btn-default pull-left" translate>
|
||||
Cancel
|
||||
</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
|
||||
</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>
|
||||
</div>
|
||||
</table>
|
||||
|
@ -66,16 +66,6 @@ class MotionViewSet(ModelViewSet):
|
||||
result = False
|
||||
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):
|
||||
"""
|
||||
Customized view endpoint to create a new motion.
|
||||
|
@ -172,7 +172,8 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
|
||||
'operator',
|
||||
'$rootScope',
|
||||
'$http',
|
||||
function(operator, $rootScope, $http) {
|
||||
'Group',
|
||||
function(operator, $rootScope, $http, Group) {
|
||||
// Put the operator into the root scope
|
||||
$http.get('/users/whoami/').success(function(data) {
|
||||
operator.setUser(data.user_id);
|
||||
|
Loading…
Reference in New Issue
Block a user