Merge pull request #3564 from FinnStutzenstein/improvements

Fixed email translations, motion sorting, undefined in DOCX, reduced …
This commit is contained in:
Emanuel Schütze 2018-02-11 20:12:25 +01:00 committed by GitHub
commit a6cdb75093
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 29 additions and 28 deletions

View File

@ -30,6 +30,6 @@ script:
- coverage report --fail-under=44 - coverage report --fail-under=44
- DJANGO_SETTINGS_MODULE='tests.settings' coverage run ./manage.py test tests.integration - DJANGO_SETTINGS_MODULE='tests.settings' coverage run ./manage.py test tests.integration
- coverage report --fail-under=74 - coverage report --fail-under=73
- DJANGO_SETTINGS_MODULE='tests.settings' ./manage.py test tests.old - DJANGO_SETTINGS_MODULE='tests.settings' ./manage.py test tests.old

View File

@ -80,7 +80,7 @@ angular.module('OpenSlidesApp.motions.docx', ['OpenSlidesApp.core.docx'])
var getMotionShortData = function (motions) { var getMotionShortData = function (motions) {
return _.map(motions, function (motion) { return _.map(motions, function (motion) {
return { return {
identifier: motion.identifier, identifier: motion.identifier || '',
title: motion.getTitle(), title: motion.getTitle(),
}; };
}); });
@ -113,7 +113,7 @@ angular.module('OpenSlidesApp.motions.docx', ['OpenSlidesApp.core.docx'])
sequential_enabled: sequential_enabled, sequential_enabled: sequential_enabled,
// Actual data // Actual data
id: motion.id, id: motion.id,
identifier: motion.identifier, identifier: motion.identifier || '',
title: motion.getTitle(), title: motion.getTitle(),
submitters: params.include.submitters ? _.map(motion.submitters, function (submitter) { submitters: params.include.submitters ? _.map(motion.submitters, function (submitter) {
return submitter.get_full_name(); return submitter.get_full_name();

View File

@ -782,21 +782,6 @@ angular.module('OpenSlidesApp.motions.site', [
}, },
hideExpression: "model.format !== 'csv'", hideExpression: "model.format !== 'csv'",
}, },
{
key: 'include',
type: 'checkbox-buttons',
templateOptions: {
label: gettextCatalog.getString('Meta information'),
options: getMetaInformationOptions({
state: true,
votingResult: true,
motionBlock: true,
origin: true,
recommendation: true,
}),
},
hideExpression: "model.format !== 'docx'",
},
]); ]);
if (commentsAvailable) { if (commentsAvailable) {
fields.push({ fields.push({
@ -855,6 +840,7 @@ angular.module('OpenSlidesApp.motions.site', [
} }
if ($scope.params.format === 'docx') { if ($scope.params.format === 'docx') {
$scope.params.include.state = false; $scope.params.include.state = false;
$scope.params.include.submitter = true;
$scope.params.include.motionBlock = false; $scope.params.include.motionBlock = false;
$scope.params.include.origin = false; $scope.params.include.origin = false;
$scope.params.include.recommendation = false; $scope.params.include.recommendation = false;
@ -1127,12 +1113,9 @@ angular.module('OpenSlidesApp.motions.site', [
$scope.filter.propertyList = ['identifier', 'origin']; $scope.filter.propertyList = ['identifier', 'origin'];
$scope.filter.propertyFunctionList = [ $scope.filter.propertyFunctionList = [
function (motion) {return motion.getTitle();}, function (motion) {return motion.getTitle();},
function (motion) {return motion.getText();},
function (motion) {return motion.getReason();},
function (motion) {return motion.category ? motion.category.name : '';}, function (motion) {return motion.category ? motion.category.name : '';},
function (motion) {return motion.motionBlock ? motion.motionBlock.name : '';}, function (motion) {return motion.motionBlock ? motion.motionBlock.name : '';},
function (motion) {return motion.recommendation ? motion.getRecommendationName() : '';}, function (motion) {return motion.recommendation ? motion.getRecommendationName() : '';},
function (motion) {return _.filter(motion.comments).join(' ');},
]; ];
$scope.filter.propertyDict = { $scope.filter.propertyDict = {
'submitters': function (submitter) { 'submitters': function (submitter) {

View File

@ -239,7 +239,7 @@
<translate>Motion</translate> <translate>Motion</translate>
</span> </span>
<ul uib-dropdown-menu class="dropdown-menu" aria-labelledby="motion-dropdown"> <ul uib-dropdown-menu class="dropdown-menu" aria-labelledby="motion-dropdown">
<li ng-repeat="m in motions"> <li ng-repeat="m in motions | orderBy: identifier">
<a href ng-click="addMotionToRecommendationField(m)"> <a href ng-click="addMotionToRecommendationField(m)">
{{ m.identifier ? m.identifier + ':' : '' }} {{ m.getTitle() }} {{ m.identifier ? m.identifier + ':' : '' }} {{ m.getTitle() }}
</a> </a>

View File

@ -240,7 +240,7 @@ class User(RESTModelMixin, PermissionsMixin, AbstractBaseUser):
""" """
raise RuntimeError('Do not use user.has_perm() but use openslides.utils.auth.has_perm') raise RuntimeError('Do not use user.has_perm() but use openslides.utils.auth.has_perm')
def send_invitation_email(self, connection, skip_autoupdate=False): def send_invitation_email(self, connection, subject, message, skip_autoupdate=False):
""" """
Sends an invitation email to the users. Returns True on success, False on failiure. Sends an invitation email to the users. Returns True on success, False on failiure.
May raise an ValidationError, if something went wrong. May raise an ValidationError, if something went wrong.
@ -260,10 +260,10 @@ class User(RESTModelMixin, PermissionsMixin, AbstractBaseUser):
'url': config['users_pdf_url'], 'url': config['users_pdf_url'],
'username': self.username, 'username': self.username,
'password': self.default_password}) 'password': self.default_password})
message = config['users_email_body'].format(**message_format) message = message.format(**message_format)
subject_format = format_dict({'event_name': config['general_event_name']}) subject_format = format_dict({'event_name': config['general_event_name']})
subject = config['users_email_subject'].format(**subject_format) subject = subject.format(**subject_format)
# Create an email and send it. # Create an email and send it.
email = mail.EmailMessage(subject, message, config['users_email_sender'], [self.email]) email = mail.EmailMessage(subject, message, config['users_email_sender'], [self.email])

View File

@ -771,8 +771,13 @@ angular.module('OpenSlidesApp.users.site', [
return user.id; return user.id;
}) })
.value(); .value();
var subject = gettextCatalog.getString(Config.get('users_email_subject').value);
var message = gettextCatalog.getString(Config.get('users_email_body').value);
$http.post('/rest/users/user/mass_invite_email/', { $http.post('/rest/users/user/mass_invite_email/', {
user_ids: user_ids, user_ids: user_ids,
subject: subject,
message: message,
}).then(function (success) { }).then(function (success) {
var numEmails = success.data.count; var numEmails = success.data.count;
var noEmailIds = success.data.no_email_ids; var noEmailIds = success.data.no_email_ids;
@ -1833,7 +1838,7 @@ angular.module('OpenSlidesApp.users.site', [
gettext('Your login for {event_name}'); gettext('Your login for {event_name}');
gettext('You can use {event_name} as a placeholder.'); gettext('You can use {event_name} as a placeholder.');
gettext('Email body'); gettext('Email body');
gettext('Dear {name},\n\nthis is your OpenSlides login for the event "{event_name}":\n {url}\n username: {username}\n password: {password}\n\nThis email was generated automatically.'); gettext('Dear {name},\n\nthis is your OpenSlides login for the event "{event_name}":\n {url}\n\n username: {username}\n password: {password}\n\nThis email was generated automatically.');
gettext('Use these placeholders: {name}, {event_name}, {url}, {username}, {password}. The url referrs to the system url.'); gettext('Use these placeholders: {name}, {event_name}, {url}, {username}, {password}. The url referrs to the system url.');
} }
]); ]);

View File

@ -180,6 +180,14 @@ class UserViewSet(ModelViewSet):
for user_id in user_ids: for user_id in user_ids:
if not isinstance(user_id, int): if not isinstance(user_id, int):
raise ValidationError({'detail': 'User_id has to be an int.'}) raise ValidationError({'detail': 'User_id has to be an int.'})
# Get subject and body from the response. Do not use the config values
# because they might not be translated.
subject = request.data.get('subject')
message = request.data.get('message')
if not isinstance(subject, str):
raise ValidationError({'detail': 'Subject has to be a string.'})
if not isinstance(message, str):
raise ValidationError({'detail': 'Message has to be a string.'})
users = User.objects.filter(pk__in=user_ids) users = User.objects.filter(pk__in=user_ids)
# Sending Emails. Keep track, which users gets an email. # Sending Emails. Keep track, which users gets an email.
@ -199,7 +207,7 @@ class UserViewSet(ModelViewSet):
try: try:
for user in users: for user in users:
if user.email: if user.email:
if user.send_invitation_email(connection, skip_autoupdate=True): if user.send_invitation_email(connection, subject, message, skip_autoupdate=True):
success_users.append(user) success_users.append(user)
else: else:
user_pks_without_email.append(user.pk) user_pks_without_email.append(user.pk)

View File

@ -351,9 +351,14 @@ class UserSendIntivationEmail(TestCase):
self.admin.save() self.admin.save()
def test_email_sending(self): def test_email_sending(self):
data = {
'user_ids': [self.admin.pk],
'subject': config['users_email_subject'],
'message': config['users_email_body']
}
response = self.client.post( response = self.client.post(
reverse('user-mass-invite-email'), reverse('user-mass-invite-email'),
{'user_ids': [self.admin.pk]}, data,
format='json') format='json')
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['count'], 1) self.assertEqual(response.data['count'], 1)