Merge pull request #3682 from FinnStutzenstein/voting-plugin

Changes for the voting plugin
This commit is contained in:
Emanuel Schütze 2018-04-13 15:11:48 +02:00 committed by GitHub
commit b6ebc78e85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 115 additions and 35 deletions

View File

@ -125,11 +125,14 @@
</div> </div>
<h3 translate>Election result</h3> <h3 translate>Election result</h3>
<button os-perms="assignments.can_manage" ng-show="assignment.phase !== 2" ng-click="createBallot()" <template-hook hook-name="assignmentPollNewBallotButton">
class="btn btn-default btn-sm"> <button os-perms="assignments.can_manage" ng-show="assignment.phase !== 2" ng-click="createBallot()"
<i class="fa fa-bar-chart fa-lg"></i> class="btn btn-default btn-sm">
<translate>New ballot</translate> <i class="fa fa-bar-chart fa-lg"></i>
</button> <translate>New ballot</translate>
</button>
</template-hook>
<uib-tabset ng-if="assignment.polls.length > 0" class="spacer ballot-tabs" active="$parent.activeTab"> <uib-tabset ng-if="assignment.polls.length > 0" class="spacer ballot-tabs" active="$parent.activeTab">
<uib-tab ng-repeat="poll in assignment.polls | orderBy:'id'" <uib-tab ng-repeat="poll in assignment.polls | orderBy:'id'"
index="$index" heading="{{ 'Ballot' | translate }} {{ $index + 1 }}"> index="$index" heading="{{ 'Ballot' | translate }} {{ $index + 1 }}">
@ -276,7 +279,7 @@
</uib-tabset> </uib-tabset>
<!-- Workaround to prevent page scrolling up after autoupdate. --> <!-- Workaround to prevent page scrolling up after autoupdate. -->
<div style="height: 700px"><div> <div style="height: 700px"></div>
</div> </div>
<template-hook hook-name="assignmentDetailViewDetailContainer"></template-hook> <template-hook hook-name="assignmentDetailViewDetailContainer"></template-hook>

View File

@ -9,6 +9,7 @@
<i class="fa fa-tags fa-lg"></i> <i class="fa fa-tags fa-lg"></i>
<translate>Tags</translate> <translate>Tags</translate>
</a> </a>
<template-hook hook-name="assignmentListMenuButton"></template-hook>
</div> </div>
<h1 translate>Elections</h1> <h1 translate>Elections</h1>
</div> </div>

View File

@ -553,27 +553,57 @@ angular.module('OpenSlidesApp.core', [
]) ])
// Template hooks // Template hooks
// 2 possible uses: // Possible uses:
// - { Id: 'myHookId', template: '<button>click me</button>' } // 1. { id: 'myHookId', template: '<button>click me</button>' }
// - { Id: 'myHookId', templateUrl: '/static/templates/plugin_name/my-hook.html' } // 2. { id: 'myHookId', templateUrl: '/static/templates/plugin_name/my-hook.html' }
// It is possible to provide a scope, that is merged into the scope of the templateHook. // 3. { id: 'myHookId' }
// This overrides functions/values of the parent scope, but there may are conflicts //
// with other plugins defining the same function/value. E.g.: // Deprecated: Give the id with 'Id'. Please use 'id'.
//
// Option 3 is for just changing the scope (see below), but not the original content. This
// is usefull to alter a JS behavior, e.g. on a ng-click. In this case, override is false
// for this template hook.
//
// It is possible to provide a scope, that is merged into the surrounding scope.
// You can override functions or values of the surrounding scope by providing them:
// { Id: 'hookId', template: '<button ng-click="customFn()">click me</button>', // { Id: 'hookId', template: '<button ng-click="customFn()">click me</button>',
// scope: { // scope: {
// customFn: function () { /*Do something */ }, // customOrOverwritten: function () { /*Do something */ },
// }, // },
// } // }
// Or you provide a function that returns an object of functions/values to overwrite to
// get access to the scope merged in:
// { Id: 'hookId', template: '<button ng-click="customFn()">click me</button>',
// scope: function (scope) {
// return {
// customOrOverwritten: function () {
// scope.value = /* change it */;
// },
// };
// },
// }
//
// As a default, template hooks in flavour of option 1 and 2 override the content that was
// originally there. Provide 'override: false', to prevent overriding the original content.
.factory('templateHooks', [ .factory('templateHooks', [
function () { function () {
var hooks = {}; var hooks = {};
return { return {
hooks: hooks, hooks: hooks,
registerHook: function (hook) { registerHook: function (hook) {
if (hooks[hook.Id] === undefined) { // Deprecated: Set the new style 'id', if 'Id' is given.
hooks[hook.Id] = []; if (hook.id === undefined) {
hook.id = hook.Id;
} }
hooks[hook.Id].push(hook);
if (hooks[hook.id] === undefined) {
hooks[hook.id] = [];
}
// set override default
if (hook.override === undefined) {
hook.override = !!(hook.template || hook.templateUrl);
}
hooks[hook.id].push(hook);
} }
}; };
} }
@ -584,21 +614,41 @@ angular.module('OpenSlidesApp.core', [
'$http', '$http',
'$q', '$q',
'$templateCache', '$templateCache',
'$timeout',
'templateHooks', 'templateHooks',
function ($compile, $http, $q, $templateCache, templateHooks) { function ($compile, $http, $q, $templateCache, $timeout, templateHooks) {
return { return {
restrict: 'E', restrict: 'E',
template: '', template: '',
link: function (scope, iElement, iAttr) { link: function (scope, iElement, iAttr) {
var hooks = templateHooks.hooks[iAttr.hookName]; var hooks = templateHooks.hooks[iAttr.hookName];
if (hooks) { if (hooks) {
var templates = _.map(hooks, function (hook) { // Populate scopes
// Populate scope _.forEach(hooks, function (hook) {
_.forEach(hook.scope, function (value, key) { var _scope = hook.scope;
if (!scope.hasOwnProperty(key)) { // If it is a function, get the scope from the function and provide
scope[key] = value; // the original scope.
} if (typeof hook.scope === 'function') {
_scope = hook.scope(scope);
}
_.forEach(_scope, function (value, key) {
scope[key] = value;
}); });
});
// Check, if at least one hook overrides the original content.
var override = _.some(hooks, function (hook) {
return hook.override;
});
// filter hooks, that does actually have a template
hooks = _.filter(hooks, function (hook) {
return hook.template || hook.templateUrl;
});
// Get all templates
var templates = _.map(hooks, function (hook) {
// Either a template (html given as string) or a templateUrl has // Either a template (html given as string) or a templateUrl has
// to be given. If a scope is provided, the schope of this templateHook // to be given. If a scope is provided, the schope of this templateHook
// is populated with the given functions/values. // is populated with the given functions/values.
@ -608,8 +658,17 @@ angular.module('OpenSlidesApp.core', [
return $templateCache.get(hook.templateUrl); return $templateCache.get(hook.templateUrl);
} }
}); });
var html = templates.join('');
iElement.append($compile(html)(scope)); // Wait for the dom to build up, so we can retrieve the inner html of iElement.
$timeout(function () {
var html = override ? '' : iElement.html();
if (templates.length) {
html += templates.join('');
}
iElement.empty();
iElement.append($compile(html)(scope));
});
} }
} }
}; };

View File

@ -469,10 +469,12 @@
</table> </table>
</ol> </ol>
<button ng-if="motion.isAllowed('create_poll')" ng-click="create_poll()" class="btn btn-default btn-sm"> <template-hook hook-name="motionPollNewVoteButton">
<i class="fa fa-bar-chart fa-lg"></i> <button ng-if="motion.isAllowed('create_poll')" ng-click="create_poll()" class="btn btn-default btn-sm">
<translate>New vote</translate> <i class="fa fa-bar-chart fa-lg"></i>
</button> <translate>New vote</translate>
</button>
</template-hook>
<div class="spacer-top pull-right nobr"> <div class="spacer-top pull-right nobr">
<a href ng-click="gotoPersonalNote()" translate>Personal note</a> <a href ng-click="gotoPersonalNote()" translate>Personal note</a>

View File

@ -21,6 +21,7 @@
<i class="fa fa-download fa-lg"></i> <i class="fa fa-download fa-lg"></i>
<translate>Import</translate> <translate>Import</translate>
</a> </a>
<template-hook hook-name="motionListMenuButton"></template-hook>
</div> </div>
<h1 translate>Motions</h1> <h1 translate>Motions</h1>
</div> </div>

View File

@ -447,13 +447,15 @@ class MotionViewSet(ModelViewSet):
raise ValidationError({'detail': 'You can not create a poll in this motion state.'}) raise ValidationError({'detail': 'You can not create a poll in this motion state.'})
try: try:
with transaction.atomic(): with transaction.atomic():
motion.create_poll(skip_autoupdate=True) poll = motion.create_poll(skip_autoupdate=True)
except WorkflowError as e: except WorkflowError as e:
raise ValidationError({'detail': e}) raise ValidationError({'detail': e})
motion.write_log([ugettext_noop('Vote created')], request.user, skip_autoupdate=True) motion.write_log([ugettext_noop('Vote created')], request.user, skip_autoupdate=True)
inform_changed_data(motion) inform_changed_data(motion)
return Response({'detail': _('Vote created successfully.')}) return Response({
'detail': _('Vote created successfully.'),
'createdPollId': poll.pk})
class MotionPollViewSet(UpdateModelMixin, DestroyModelMixin, GenericViewSet): class MotionPollViewSet(UpdateModelMixin, DestroyModelMixin, GenericViewSet):

View File

@ -563,6 +563,9 @@ angular.module('OpenSlidesApp.users.site', [
_.forEach($scope.users, function (user) { _.forEach($scope.users, function (user) {
user.has_last_email_send = !!user.last_email_send; user.has_last_email_send = !!user.last_email_send;
}); });
if ($scope.updateUsers) {
$scope.updateUsers();
}
}); });
Group.bindAll({where: {id: {'>': 1}}}, $scope, 'groups'); Group.bindAll({where: {id: {'>': 1}}}, $scope, 'groups');
$scope.$watch(function () { $scope.$watch(function () {

View File

@ -19,6 +19,7 @@
<i class="fa fa-download fa-lg"></i> <i class="fa fa-download fa-lg"></i>
<translate>Import</translate> <translate>Import</translate>
</a> </a>
<template-hook hook-name="userListMenuButtons"></template-hook>
</div> </div>
<h1 translate>Participants</h1> <h1 translate>Participants</h1>
</div> </div>
@ -69,6 +70,8 @@
</a> </a>
</ul> </ul>
</div> </div>
<template-hook hook-name="userListSubmenuRight"></template-hook>
</div> </div>
</div> </div>
<div uib-collapse="!isSelectMode" class="row spacer"> <div uib-collapse="!isSelectMode" class="row spacer">
@ -146,6 +149,7 @@
{{ usersFiltered.length }} / {{ usersFiltered.length }} /
{{ users.length }} {{ "participants" | translate }}<span ng-if="(users|filter:{selected:true}).length > 0">, {{ users.length }} {{ "participants" | translate }}<span ng-if="(users|filter:{selected:true}).length > 0">,
{{(users|filter:{selected:true}).length}} {{ "selected" | translate }}</span> {{(users|filter:{selected:true}).length}} {{ "selected" | translate }}</span>
<template-hook hook-name="userListTableStats"></template-hook>
</div> </div>
<div class="col-md-6" ng-show="usersFiltered.length > pagination.itemsPerPage"> <div class="col-md-6" ng-show="usersFiltered.length > pagination.itemsPerPage">
<span class="pull-right"> <span class="pull-right">
@ -348,7 +352,9 @@
</div> </div>
<div os-perms="users.can_manage" ng-class="{'hiddenDiv': !user.hover}"> <div os-perms="users.can_manage" ng-class="{'hiddenDiv': !user.hover}">
<small> <small>
<a href="" ng-click="openDialog(user)" translate>Edit</a> &middot; <template-hook hook-name="userListEditButton">
<a href="" ng-click="openDialog(user)" translate>Edit</a> &middot;
</template-hook>
<a ui-sref="users.user.change-password({id: user.id})" translate>Change password</a> &middot; <a ui-sref="users.user.change-password({id: user.id})" translate>Change password</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>
@ -442,17 +448,20 @@
</div> </div>
</small> </small>
</div> </div>
<div style="width: 40%;" class="pull-right" os-perms="users.can_see_extra_data"> <div style="width: 40%;" class="pull-right">
<div os-perms="users.can_manage" ng-show="user.hover || user.is_present"> <div os-perms="users.can_see_extra_data users.can_manage"
ng-style="{'visibility': (user.hover || user.is_present) ? 'visible' : 'hidden'}">
<span class="pointer nobr" ng-click="user.is_present = !user.is_present; save(user);"> <span class="pointer nobr" ng-click="user.is_present = !user.is_present; save(user);">
<i class="fa" ng-class="user.is_present ? 'fa-check-square-o' : 'fa-square-o'"></i> <i class="fa" ng-class="user.is_present ? 'fa-check-square-o' : 'fa-square-o'"></i>
<span class="spacer-left" translate>Present</span> <span class="spacer-left" translate>Present</span>
</span> </span>
</div> </div>
<div os-perms="!users.can_manage" class="nobr" ng-show="user.is_present"> <div os-perms="!users.can_manage" class="nobr"
ng-style="{'visibility': user.is_present ? 'visible' : 'hidden'}">
<i class="fa fa-check-square-o"></i> <i class="fa fa-check-square-o"></i>
<span class="spacer-left" translate>Present</span> <span class="spacer-left" translate>Present</span>
</div> </div>
<template-hook hook-name="userListExtraContent"></template-hook>
</div> </div>
</div> </div>