Projet

Général

Profil

0001-misc-replace-get_logger-.error-with-newer-record_err.patch

Benjamin Dauvergne, 11 septembre 2021 18:16

Télécharger (39,7 ko)

Voir les différences:

Subject: [PATCH] misc: replace get_logger().error() with newer record_error()
 (#55437)

 tests/form_pages/test_formdata.py |   7 +-
 tests/test_fc_auth.py             |   6 +-
 wcs/backoffice/management.py      | 329 ++++++++++++++++++++++++++++++
 wcs/portfolio.py                  |  10 +-
 wcs/qommon/emails.py              |  23 ++-
 wcs/qommon/ident/franceconnect.py |  23 +--
 wcs/qommon/ident/idp.py           |   3 +-
 wcs/qommon/ident/password.py      |   6 +-
 wcs/qommon/saml2.py               |   7 +-
 wcs/root.py                       |  19 +-
 wcs/wf/export_to_model.py         |   7 +-
 wcs/wf/notification.py            |  10 +-
 wcs/wf/profile.py                 |   4 +-
 wcs/wf/register_comment.py        |   7 +-
 wcs/wf/roles.py                   |  12 +-
 wcs/workflows.py                  |  17 +-
 16 files changed, 416 insertions(+), 74 deletions(-)
tests/form_pages/test_formdata.py
761 761
        http_post_request.return_value = None, 400, 'null', None  # fail
762 762
        resp = resp.form.submit('button_export_to')
763 763
        assert http_post_request.call_count == 1
764
        assert caplog.records[-1].message.startswith(
765
            "file 'template.pdf' failed to be pushed to portfolio of 'Foo"
766
        )
764
        if pub.is_using_postgresql():
765
            assert pub.loggederror_class.select()[0].summary.startswith(
766
                "file 'template.pdf' failed to be pushed to portfolio of 'Foo"
767
            )
767 768

  
768 769
    # failed to push to portfolio, but document is here
769 770
    resp = resp.follow()  # $form/$id/create_doc
tests/test_fc_auth.py
194 194
            }
195 195
        )
196 196
    )
197
    assert 'user did not authorize login' in caplog.records[-1].message
197
    if pub.is_using_postgresql():
198
        assert 'user did not authorize login' in pub.loggederror_class.select()[-1].summary
198 199
    resp = app.get(
199 200
        '/ident/fc/callback?%s'
200 201
        % urllib.parse.urlencode(
......
204 205
            }
205 206
        )
206 207
    )
207
    assert 'whatever' in caplog.records[-1].message
208
    if pub.is_using_postgresql():
209
        assert 'whatever' in pub.loggederror_class.select()[-1].summary
208 210

  
209 211
    # Login existing user
210 212
    def logme(login_url):
wcs/backoffice/management.py
139 139
    return geojson
140 140

  
141 141

  
142
class SendCodeFormdefDirectory(Directory):
143
    formdef = None
144

  
145
    def __init__(self, formdef):
146
        self.formdef = formdef
147

  
148
    def _q_lookup(self, component):
149
        html_top('management', _('Management'))
150
        formdata = self.formdef.data_class().get(component)
151

  
152
        submitter_email = formdata.formdef.get_submitter_email(formdata)
153
        mail_subject = EmailsDirectory.get_subject('tracking-code-reminder')
154
        mail_body = EmailsDirectory.get_body('tracking-code-reminder')
155

  
156
        form = Form()
157
        form.add(TextWidget, 'text', title=_('Message'), required=True, cols=60, rows=5, value=mail_body)
158
        form.add(EmailWidget, 'email', title=_('Email'), required=False, value=submitter_email)
159
        sms_class = None
160
        if get_publisher().use_sms_feature:
161
            sms_class = sms.SMS.get_sms_class()
162
            if sms_class:
163
                form.add(StringWidget, 'sms', title=_('SMS Number'), required=False)
164
                form.add(
165
                    RadiobuttonsWidget,
166
                    'method',
167
                    options=[('email', _('Email')), ('sms', _('SMS'))],
168
                    value='email',
169
                    required=True,
170
                    extra_css_class='widget-inline-radio',
171
                )
172
        form.add_submit('submit', _('Send'))
173
        form.add_submit('cancel', _('Cancel'))
174

  
175
        if not form.is_submitted() or form.has_errors():
176
            r = TemplateIO(html=True)
177
            r += htmltext('<h2>%s</h2>') % _('Send tracking code')
178
            r += form.render()
179
            return r.getvalue()
180

  
181
        if not formdata.tracking_code:
182
            tracking_code = get_publisher().tracking_code_class()
183
            tracking_code.formdata = formdata  # this stores both objects
184

  
185
        message = form.get_widget('text').parse()
186
        data = {
187
            'form_tracking_code': formdata.tracking_code,
188
            'tracking_code': formdata.tracking_code,
189
            'email': form.get_widget('email').parse(),
190
        }
191
        data.update(self.formdef.get_substitution_variables(minimal=True))
192

  
193
        if sms_class and form.get_widget('method').parse() == 'sms':
194
            # send sms
195
            sitename = get_cfg('misc', {}).get('sitename') or 'w.c.s.'
196
            sms_cfg = get_cfg('sms', {})
197
            sender = sms_cfg.get('sender', sitename)[:11]
198
            message = Template(message).render(data)
199
            try:
200
                sms_class.send(sender, [form.get_widget('sms').parse()], message)
201
            except errors.SMSError as e:
202
                get_publisher().record_error(_('Could not send SMS'), formdata=formdata, exception=e)
203
            get_session().message = ('info', _('SMS with tracking code sent to the user'))
204
        else:
205
            # send mail
206
            emails.template_email(
207
                mail_subject, message, mail_body_data=data, email_rcpt=form.get_widget('email').parse()
208
            )
209
            get_session().message = ('info', _('Email with tracking code sent to the user'))
210

  
211
        return redirect('../..')
212

  
213

  
214
class SendCodeDirectory(Directory):
215
    def _q_lookup(self, component):
216
        try:
217
            formdef = FormDef.get_by_urlname(component)
218
            if not formdef.enable_tracking_codes:
219
                raise errors.TraversalError()
220
            return SendCodeFormdefDirectory(formdef)
221
        except KeyError:
222
            raise errors.TraversalError()
223

  
224

  
225
class UserViewDirectory(Directory):
226
    _q_exports = ['', 'sendcode']
227

  
228
    sendcode = SendCodeDirectory()
229
    user = None
230

  
231
    def __init__(self, user):
232
        self.user = user
233

  
234
    def _q_index(self):
235
        get_response().breadcrumb.append(('%s/' % self.user.id, self.user.display_name))
236
        html_top('management', _('Management'))
237
        # display list of open formdata for the user
238
        formdefs = [x for x in FormDef.select(lightweight=True) if not x.skip_from_360_view]
239
        user_roles = set([logged_users_role().id] + get_request().user.get_roles())
240
        criterias = [
241
            Equal('is_at_endpoint', False),
242
            Equal('user_id', str(self.user.id)),
243
            Contains('formdef_id', [x.id for x in formdefs]),
244
        ]
245
        from wcs import sql
246

  
247
        formdatas = sql.AnyFormData.select(criterias, order_by='receipt_time')
248

  
249
        criterias = [
250
            Equal('is_at_endpoint', False),
251
            Equal('user_id', str(self.user.id)),
252
            Intersects('concerned_roles_array', user_roles),
253
        ]
254
        viewable_formdatas = sql.AnyFormData.select(criterias)
255
        viewable_formdatas_ids = {}
256
        for viewable_formdata in viewable_formdatas:
257
            viewable_formdatas_ids[(viewable_formdata.formdef.id, viewable_formdata.id)] = True
258

  
259
        r = TemplateIO(html=True)
260
        r += get_session().display_message()
261

  
262
        r += htmltext('<div class="bo-block">')
263
        r += htmltext('<h2>%s</h2>') % self.user.display_name
264
        formdef = UserFieldsFormDef()
265
        r += htmltext('<div class="form">')
266
        for field in formdef.get_all_fields():
267
            if not hasattr(field, 'get_view_value'):
268
                continue
269
            value = self.user.form_data.get(field.id)
270
            if not value:
271
                continue
272
            r += htmltext('<div class="title">')
273
            r += field.label
274
            r += htmltext('</div>')
275
            r += htmltext('<div class="StringWidget content">')
276
            r += field.get_view_value(value)
277
            r += htmltext('</div>')
278
        r += htmltext('</div>')
279
        r += htmltext('</div>')
280

  
281
        if formdatas:
282
            categories = {}
283
            formdata_by_category = {}
284
            for formdata in formdatas:
285
                if formdata.formdef.category_id not in categories:
286
                    categories[formdata.formdef.category_id] = formdata.formdef.category
287
                    formdata_by_category[formdata.formdef.category_id] = []
288
                formdata_by_category[formdata.formdef.category_id].append(formdata)
289
            cats = list(categories.values())
290
            Category.sort_by_position(cats)
291
            for cat in cats:
292
                r += htmltext('<div class="bo-block">')
293
                if len(cats) > 1:
294
                    if cat is None:
295
                        r += htmltext('<h2>%s</h2>') % _('Misc')
296
                        cat_formdatas = formdata_by_category[None]
297
                    else:
298
                        r += htmltext('<h2>%s</h2>') % cat.name
299
                        cat_formdatas = formdata_by_category[cat.id]
300
                else:
301
                    cat_formdatas = formdatas
302
                r += htmltext('<ul class="biglist c-360-user-view">')
303
                for formdata in cat_formdatas:
304
                    status_label = formdata.get_status_label()
305
                    submit_date = misc.strftime(misc.date_format(), formdata.receipt_time)
306
                    formdata_key_id = (formdata.formdef.id, formdata.id)
307
                    if formdata_key_id in viewable_formdatas_ids:
308
                        r += htmltext(
309
                            '<li><a href="%s">%s, '
310
                            '<span class="datetime">%s</span> '
311
                            '<span class="status">(%s)</span></a>'
312
                            % (
313
                                formdata.get_url(backoffice=True),
314
                                formdata.formdef.name,
315
                                submit_date,
316
                                status_label,
317
                            )
318
                        )
319
                    else:
320
                        r += htmltext(
321
                            '<li><span>%s, '
322
                            '<span class="datetime">%s</span> '
323
                            '<span class="status">(%s)</span></span>'
324
                            % (formdata.formdef.name, submit_date, status_label)
325
                        )
326
                    if formdata.formdef.enable_tracking_codes:
327
                        r += htmltext('<p class="commands">')
328
                        r += command_icon(
329
                            'sendcode/%s/%s' % (formdata.formdef.url_name, formdata.id),
330
                            'export',
331
                            label=_('Send tracking code'),
332
                            popup=True,
333
                        )
334
                        r += htmltext('</p>')
335
                r += htmltext('</ul>')
336
                r += htmltext('</div>')
337
            r += htmltext('</div>')
338

  
339
        return r.getvalue()
340

  
341

  
342
class UsersViewDirectory(Directory):
343
    _q_exports = ['']
344

  
345
    def _q_traverse(self, path):
346
        if not get_publisher().is_using_postgresql():
347
            raise errors.TraversalError()
348
        get_response().breadcrumb.append(('users', _('Per User View')))
349
        return super()._q_traverse(path)
350

  
351
    def get_search_sidebar(self, offset=None, limit=None, order_by=None):
352
        get_response().add_javascript(['wcs.listing.js'])
353

  
354
        r = TemplateIO(html=True)
355
        r += htmltext('<form id="listing-settings" action=".">')
356
        if offset or limit:
357
            if not offset:
358
                offset = 0
359
            r += htmltext('<input type="hidden" name="offset" value="%s"/>') % offset
360
        if limit:
361
            r += htmltext('<input type="hidden" name="limit" value="%s"/>') % limit
362

  
363
        if order_by is None:
364
            order_by = ''
365
        r += htmltext('<input type="hidden" name="order_by" value="%s"/>') % order_by
366

  
367
        r += htmltext('<h3>%s</h3>') % _('Search')
368
        if get_request().form.get('q'):
369
            q = force_text(get_request().form.get('q'))
370
            r += htmltext('<input name="q" value="%s">') % force_str(q)
371
        else:
372
            r += htmltext('<input name="q">')
373
        r += htmltext('<input type="submit" value="%s"/>') % _('Search')
374

  
375
        return r.getvalue()
376

  
377
    def _q_index(self):
378
        html_top('management', _('Management'))
379
        r = TemplateIO(html=True)
380

  
381
        limit = misc.get_int_or_400(
382
            get_request().form.get('limit', get_publisher().get_site_option('default-page-size')) or 20
383
        )
384
        offset = misc.get_int_or_400(get_request().form.get('offset', 0))
385
        order_by = misc.get_order_by_or_400(get_request().form.get('order_by', None)) or 'name'
386
        query = get_request().form.get('q')
387

  
388
        get_response().filter['sidebar'] = self.get_search_sidebar(
389
            limit=limit, offset=offset, order_by=order_by
390
        )
391

  
392
        if not query:
393
            r += htmltext('<div id="listing">')
394
            r += htmltext('<div class="big-msg-info">')
395
            r += htmltext('<p>%s</p>') % _('Use the search field on the right ' 'to look for an user.')
396
            r += htmltext('</div>')
397
            r += htmltext('</div>')
398
            if get_request().form.get('ajax') == 'true':
399
                get_response().filter = {'raw': True}
400
            return r.getvalue()
401

  
402
        formdef = UserFieldsFormDef()
403
        criteria_fields = [
404
            ILike('name', query),
405
            ILike('ascii_name', misc.simplify(query, ' ')),
406
            ILike('email', query),
407
        ]
408
        for field in formdef.get_all_fields():
409
            if field.type in ('string', 'text', 'email'):
410
                criteria_fields.append(ILike('f%s' % field.id, query))
411
        if get_publisher().is_using_postgresql():
412
            criteria_fields.append(FtsMatch(query))
413
        criterias = [Or(criteria_fields)]
414
        users = get_publisher().user_class.select(
415
            order_by=order_by, clause=criterias, limit=limit, offset=offset
416
        )
417
        total_count = get_publisher().user_class.count(criterias)
418

  
419
        users_cfg = get_cfg('users', {})
420
        include_name_column = not (users_cfg.get('field_name'))
421
        include_email_column = not (users_cfg.get('field_email'))
422
        r += htmltext('<div id="listing">')
423
        r += htmltext('<table class="main">')
424
        r += htmltext('<thead>')
425
        r += htmltext('<tr>')
426
        if include_name_column:
427
            r += htmltext('<th data-field-sort-key="name"><span>%s</span></th>') % _('Name')
428
        if include_email_column:
429
            r += htmltext('<th data-field-sort-key="email"><span>%s</span></th>') % _('Email')
430
        for field in formdef.get_all_fields():
431
            if field.include_in_listing:
432
                r += htmltext('<th data-field-sort-key="f%s"><span>%s</span></th>') % (field.id, field.label)
433
        r += htmltext('</tr>')
434
        r += htmltext('</thead>')
435
        r += htmltext('<tbody>')
436

  
437
        for user in users:
438
            r += htmltext('<tr data-link="%s/">') % user.id
439
            if include_name_column:
440
                r += htmltext('<td>%s</td>') % (user.name or '')
441
            if include_email_column:
442
                r += htmltext('<td>%s</td>') % (user.email or '')
443
            for field in formdef.get_all_fields():
444
                if field.include_in_listing:
445
                    r += htmltext('<td>%s</td>') % (user.form_data.get(field.id) or '')
446
            r += htmltext('</tr>')
447

  
448
        r += htmltext('</tbody>')
449
        r += htmltext('</table>')
450

  
451
        if get_publisher().is_using_postgresql():
452
            r += pagination_links(offset, limit, total_count)
453

  
454
        r += htmltext('</div>')
455

  
456
        if get_request().form.get('ajax') == 'true':
457
            get_response().filter = {'raw': True}
458
            return r.getvalue()
459

  
460
        return r.getvalue()
461

  
462
    def _q_lookup(self, component):
463
        try:
464
            user = get_publisher().user_class.get(component)
465
            return UserViewDirectory(user)
466
        except KeyError:
467
            pass
468
        raise errors.TraversalError()
469

  
470

  
142 471
class ManagementDirectory(Directory):
143 472
    _q_exports = ['', 'forms', 'listing', 'statistics', 'lookup', 'count', 'geojson', 'map']
144 473

  
wcs/portfolio.py
62 62
def push_document(user, filename, stream):
63 63
    if not user:
64 64
        return
65
    charset = get_publisher().site_charset
65
    publisher = get_publisher()
66
    charset = publisher.site_charset
66 67
    payload = {}
67 68
    if user.name_identifiers:
68 69
        payload['user_nameid'] = force_text(user.name_identifiers[0], 'ascii')
69 70
    elif user.email:
70 71
        payload['user_email'] = force_text(user.email, 'ascii')
71
    payload['origin'] = urllib.parse.urlparse(get_publisher().get_frontoffice_url()).netloc
72
    payload['origin'] = urllib.parse.urlparse(publisher.get_frontoffice_url()).netloc
72 73
    payload['file_name'] = force_text(filename, charset)
73 74
    stream.seek(0)
74 75
    payload['file_b64_content'] = force_text(base64.b64encode(stream.read()))
......
80 81
        if status == 200:
81 82
            get_logger().info('file %r pushed to portfolio of %r', filename, user.display_name)
82 83
        else:
83
            get_logger().error('file %r failed to be pushed to portfolio of %r', filename, user.display_name)
84
            publisher.record_error(
85
                _('file %(filename)r failed to be pushed to portfolio of %(display_name)r')
86
                % {'filename': filename, 'display_name': user.display_name}
87
            )
84 88

  
85 89
    if get_response():
86 90
        get_response().add_after_job(
wcs/qommon/emails.py
43 43
from django.utils.safestring import mark_safe
44 44
from quixote import get_publisher, get_request, get_response
45 45

  
46
from . import errors, force_str
46
from . import _, errors, force_str
47 47
from .admin.emails import EmailsDirectory
48 48
from .publisher import get_cfg, get_logger
49 49
from .template import Template
......
350 350

  
351 351

  
352 352
def create_smtp_server(emails_cfg, smtp_timeout=None):
353
    publisher = get_publisher()
353 354
    try:
354 355
        s = smtplib.SMTP(emails_cfg.get('smtp_server', None) or 'localhost', timeout=smtp_timeout)
355
    except socket.timeout:
356
        get_logger().error('Failed to connect to SMTP server (timeout)')
356
    except socket.timeout as e:
357
        publisher.record_error(_('Failed to connect to SMTP server (timeout)'), exception=e)
357 358
        raise errors.EmailError('Failed to connect to SMTP server (timeout)')
358 359
    except OSError:
359 360
        # XXX: write message in a queue somewhere?
360
        get_logger().error('Failed to connect to SMTP server')
361
        publisher.record_error(_('Failed to connect to SMTP server'), exception=e)
361 362
        raise errors.EmailError('Failed to connect to SMTP server')
362 363
    if not s.sock:
363
        get_logger().error('Failed to connect to SMTP server')
364
        publisher.record_error(_('Failed to connect to SMTP server'))
364 365
        raise errors.EmailError('Failed to connect to SMTP server')
365 366
    rc_code, ehlo_answer = s.ehlo()
366 367
    if rc_code != 250:
367
        get_logger().error('Failed to EHLO to SMTP server (%s)', rc_code)
368
        publisher.record_error(_('Failed to EHLO to SMTP server (%s)') % rc_code)
368 369
        raise errors.EmailError('Failed to EHLO to SMTP server (%s)' % rc_code)
369 370
    if b'STARTTLS' in ehlo_answer:
370 371
        rc_code = s.starttls()[0]
371 372
        if rc_code != 220:
372
            get_logger().error('Failed to STARTTLS to SMTP server (%s)', rc_code)
373
            publisher.record_error(_('Failed to STARTTLS to SMTP server (%s)') % rc_code)
373 374
            raise errors.EmailError('Failed to STARTTLS to SMTP server (%s)' % rc_code)
374 375
    if emails_cfg.get('smtp_login'):
375 376
        try:
376 377
            s.login(emails_cfg.get('smtp_login') or '', emails_cfg.get('smtp_password') or '')
377
        except smtplib.SMTPAuthenticationError:
378
            get_logger().error('Failed to authenticate to SMTP server')
378
        except smtplib.SMTPAuthenticationError as e:
379
            publisher.record_error(_('Failed to authenticate to SMTP server'), exception=e)
379 380
            raise errors.EmailError('Failed to authenticate to SMTP server')
380
        except smtplib.SMTPException:
381
            get_logger().error('Failed to authenticate to SMTP server, unknown error.')
381
        except smtplib.SMTPException as e:
382
            publisher.record_error(_('Failed to authenticate to SMTP server, unknown error.'), exception=e)
382 383
            raise errors.EmailError('Failed to authenticate to SMTP server, unknown error.')
383 384
    return s
384 385

  
wcs/qommon/ident/franceconnect.py
28 28
from wcs.formdata import flatten_dict
29 29
from wcs.workflows import WorkflowStatusItem
30 30

  
31
from .. import _, get_cfg, get_logger, template
31
from .. import _, get_cfg, template
32 32
from ..backoffice.menu import html_top
33 33
from ..form import (
34 34
    CompositeWidget,
......
324 324
        return False
325 325

  
326 326
    def get_access_token(self, code):
327
        logger = get_logger()
327
        publisher = get_publisher()
328 328
        session = get_session()
329 329
        fc_cfg = get_cfg('fc', {})
330 330
        client_id = fc_cfg.get('client_id')
......
345 345
            },
346 346
        )
347 347
        if status != 200:
348
            logger.error('status from FranceConnect token_url is not 200')
348
            publisher.record_error(_('Status from FranceConnect token_url is not 200'))
349 349
            return None
350 350
        result = json_loads(data)
351 351
        if 'error' in result:
352
            logger.error('FranceConnect code resolution failed: %s', result['error'])
352
            publisher.record_error(_('FranceConnect code resolution failed: %s') % result['error'])
353 353
            return None
354 354
        # check id_token nonce
355 355
        id_token = result['id_token']
......
358 358
        payload = json_loads(base64url_decode(force_bytes(payload)))
359 359
        nonce = hashlib.sha256(force_bytes(session.id)).hexdigest()
360 360
        if payload['nonce'] != nonce:
361
            logger.error('FranceConnect returned nonce did not match')
361
            publisher.record_error(_('FranceConnect returned nonce did not match'))
362 362
            return None
363 363
        return access_token, id_token
364 364

  
365 365
    def get_user_info(self, access_token):
366
        logger = get_logger()
366
        publisher = get_publisher()
367 367
        dummy, status, data, dummy = http_get_page(
368 368
            self.get_user_info_url(),
369 369
            headers={
......
371 371
            },
372 372
        )
373 373
        if status != 200:
374
            logger.error(
375
                'status from FranceConnect user_info_url is not 200 but %s and data is' ' %s',
376
                status.data[:100],
374
            publisher.record_error(
375
                _('Status from FranceConnect user_info_url is not 200 but %(status)s and data is %(data)s')
376
                % {'status': status, 'data': data[:100]}
377 377
            )
378 378
            return None
379 379
        return json_loads(data)
......
452 452
        pub = get_publisher()
453 453
        request = get_request()
454 454
        session = get_session()
455
        logger = get_logger()
456 455
        state = request.form.get('state', '')
457 456
        next_url = (session.extra_user_variables or {}).pop(
458 457
            'fc_next_url_' + state, ''
......
464 463
            if error:
465 464
                # we log only errors whose user is not responsible
466 465
                msg = self.AUTHORIZATION_REQUEST_ERRORS.get(error)
467
                logger.error(_('FranceConnect authentication failed: %s'), msg if msg else error)
466
                pub.record_error(_('FranceConnect authentication failed: %s') % msg if msg else error)
468 467
            return redirect(next_url)
469 468
        access_token, id_token = self.get_access_token(request.form['code'])
470 469
        if not access_token:
......
493 492

  
494 493
        if not (user.name and user.email):
495 494
            # we didn't get useful attributes, forget it.
496
            logger.error('failed to get name and/or email attribute from FranceConnect')
495
            pub.record_error(_('Failed to get name and/or email attribute from FranceConnect'))
497 496
            return redirect(next_url)
498 497

  
499 498
        user.store()
wcs/qommon/ident/idp.py
29 29
from quixote.directory import Directory
30 30
from quixote.html import TemplateIO, htmltext
31 31

  
32
from .. import _, errors, get_cfg, get_logger, misc, saml2utils, template, x509utils
32
from .. import _, errors, get_cfg, misc, saml2utils, template, x509utils
33 33
from ..admin.menu import command_icon
34 34
from ..backoffice.menu import html_top
35 35
from ..form import (
......
84 84
        idps = get_cfg('idp', {})
85 85

  
86 86
        if not lasso:
87
            get_logger().error('/login unavailable - lasso is not installed')
88 87
            raise Exception("lasso is missing, idp method cannot be used")
89 88

  
90 89
        if len(idps) == 0:
wcs/qommon/ident/password.py
23 23

  
24 24
from wcs.qommon.admin.texts import TextsDirectory
25 25

  
26
from .. import _, emails, errors, get_cfg, get_logger, misc, ngettext
26
from .. import _, emails, errors, get_cfg, misc, ngettext
27 27
from .. import storage as st
28 28
from .. import template, tokens
29 29
from ..admin.emails import EmailsDirectory
......
606 606

  
607 607
        if identities_cfg.get('email-confirmation', False):
608 608
            if not user.email:
609
                get_logger().error(
609
                get_publisher().record_error(
610 610
                    _(
611 611
                        'Accounts are configured to require confirmation but accounts can be created without emails'
612 612
                    )
......
624 624

  
625 625
        if passwords_cfg.get('generate', True):
626 626
            if not user.email:
627
                get_logger().error(
627
                get_publisher().record_error(
628 628
                    _(
629 629
                        'Accounts are configured to have a generated password '
630 630
                        'but accounts can be created without emails'
wcs/qommon/saml2.py
79 79
        try:
80 80
            return method(*args, **kwargs)
81 81
        except Exception as e:
82
            get_logger().error('Exception in method %r: %s; returning a SOAP error' % (method, e))
82
            get_publisher().record_error(
83
                _('Exception in method %r: returning a SOAP error') % method, exception=e
84
            )
83 85
            fault = lasso.SoapFault.newFull('Internal Server Error', str(e))
84 86
            body = lasso.SoapBody()
85 87
            body.any = [fault]
......
125 127
    def _q_traverse(self, path):
126 128
        # if lasso is not installed, hide the saml endpoints
127 129
        if lasso is None:
128
            if does_idp_authentication():
129
                rel_path = os.path.join('/saml', *path)
130
                get_logger().error('%s unavailable - lasso is not installed' % rel_path)
131 130
            raise errors.TraversalError()
132 131
        return Directory._q_traverse(self, path)
133 132

  
wcs/root.py
82 82
            method = ident_methods[0]
83 83
            try:
84 84
                return ident.login(method)
85
            except KeyError:
86
                msg = 'failed to login with method %s' % method
87
                get_logger().error(msg)
85
            except KeyError as e:
86
                msg = _('Failed to login with method %s') % method
87
                get_publisher().record_error(msg, exception=e)
88 88
                return errors.TraversalError(msg)
89 89
        else:
90 90
            form = Form(enctype='multipart/form-data')
......
108 108
                else:
109 109
                    try:
110 110
                        return ident.login(method)
111
                    except KeyError:
112
                        msg = 'failed to login with method %s' % method
113
                        get_logger().error(msg)
111
                    except KeyError as e:
112
                        get_publisher().record_error(
113
                            _('Failed to login with method %s') % method, exception=e
114
                        )
114 115
                        return errors.TraversalError()
115 116
            else:
116 117
                template.html_top(_('Login'))
......
148 149
            method = ident_methods[0]
149 150
            try:
150 151
                return ident.register(method)
151
            except KeyError:
152
                get_logger().error('failed to register with method %s' % method)
152
            except KeyError as e:
153
                get_publisher().record_error(_('Failed to register with method %s') % method, exception=e)
153 154
                return errors.TraversalError()
154 155
        else:
155 156
            form = Form(enctype='multipart/form-data')
......
332 333
                        }
333 334
                    )
334 335
                except UploadStorageError as e:
335
                    get_logger().error('upload storage error: %s' % e)
336
                    get_publisher().record_error(_('Upload storage error'), exception=e)
336 337
                    results.append({'error': _('failed to store file (system error)')})
337 338

  
338 339
        get_response().set_content_type('application/json')
wcs/wf/export_to_model.py
45 45
    template_on_formdata,
46 46
)
47 47

  
48
from ..qommon import _, ezt, force_str, get_logger, misc
48
from ..qommon import _, ezt, force_str, misc
49 49
from ..qommon.form import (
50 50
    CheckboxWidget,
51 51
    ComputedExpressionWidget,
......
547 547
                )
548 548
            )
549 549
        except TemplateError as e:
550
            url = formdata.get_url()
551
            get_logger().error('error in template for export to model [%s]: %s' % (url, str(e)))
550
            get_publisher().record_error(
551
                _('Error in template for export to model'), formdata=formdata, exception=e
552
            )
552 553
            raise TemplatingError(_('Error in template: %s') % str(e))
553 554

  
554 555
    def apply_od_template_to_formdata(self, formdata):
wcs/wf/notification.py
20 20

  
21 21
from wcs.workflows import WorkflowStatusItem, register_item_class, template_on_formdata
22 22

  
23
from ..qommon import _, get_logger
23
from ..qommon import _
24 24
from ..qommon.form import ComputedExpressionWidget, SingleSelectWidget, StringWidget, TextWidget, WidgetList
25 25
from ..qommon.template import TemplateError
26 26
from .wscall import WebserviceCallStatusItem
......
124 124
        try:
125 125
            title = template_on_formdata(formdata, self.compute(self.title, render=False), autoescape=False)
126 126
        except TemplateError as e:
127
            get_logger().error(
128
                'error in template for notification title, ' 'mail could not be generated: %s' % str(e)
127
            get_publisher().record_error(
128
                _('error in template for notification title, mail could not be generated'), exception=e
129 129
            )
130 130
            return
131 131

  
132 132
        try:
133 133
            body = template_on_formdata(formdata, self.compute(self.body, render=False), autoescape=False)
134 134
        except TemplateError as e:
135
            get_logger().error(
136
                'error in template for notification body, ' 'mail could not be generated: %s' % str(e)
135
            get_publisher().record_error(
136
                _('error in template for notification body, mail could not be generated'), exception=e
137 137
            )
138 138
            return
139 139

  
wcs/wf/profile.py
29 29
from ..qommon.form import CompositeWidget, ComputedExpressionWidget, SingleSelectWidget, WidgetListAsTable
30 30
from ..qommon.ident.idp import is_idp_managing_user_attributes
31 31
from ..qommon.misc import JSONEncoder, http_patch_request
32
from ..qommon.publisher import get_cfg, get_logger
32
from ..qommon.publisher import get_cfg
33 33

  
34 34

  
35 35
def user_ws_url(user_uuid):
......
207 207
                url, payload, headers={'Content-type': 'application/json'}
208 208
            )
209 209
            if status != 200:
210
                get_logger().error('failed to update profile for user %r', user)
210
                get_publisher().record_error(_('Failed to update profile for user %r') % user)
211 211

  
212 212
        if get_request():
213 213
            get_response().add_after_job(_('Updating user profile'), after_job)
wcs/wf/register_comment.py
25 25
    template_on_formdata,
26 26
)
27 27

  
28
from ..qommon import _, ezt, get_logger
28
from ..qommon import _, ezt
29 29
from ..qommon.form import TextWidget, WidgetListOfRoles
30 30
from ..qommon.template import TemplateError
31 31

  
......
136 136
            formdata.evolution[-1].add_part(JournalEvolutionPart(formdata, self.comment, self.to))
137 137
            formdata.store()
138 138
        except TemplateError as e:
139
            url = formdata.get_url()
140
            get_logger().error(
141
                'error in template for comment [%s], ' 'comment could not be generated: %s' % (url, str(e))
139
            get_publisher().record_error(
140
                _('Error in template, comment could not be generated'), formdata=formdata, exception=e
142 141
            )
143 142

  
144 143

  
wcs/wf/roles.py
26 26
from ..qommon.form import SingleSelectWidgetWithOther
27 27
from ..qommon.ident.idp import is_idp_managing_user_attributes
28 28
from ..qommon.misc import http_delete_request, http_post_request
29
from ..qommon.publisher import get_cfg, get_logger
29
from ..qommon.publisher import get_cfg
30 30

  
31 31

  
32 32
def roles_ws_url(role_uuid, user_uuid):
......
125 125
            signed_url = sign_ws_url(url)
126 126
            dummy, status, dummy, dummy = http_post_request(signed_url)
127 127
            if status != 201:
128
                get_logger().error('failed to add role %r to user %r', role, user)
128
                get_publisher().record_error(
129
                    _('Failed to add role %(role)r to user %(user)r') % {'role': role, 'user': user},
130
                    formdata=formdata,
131
                )
129 132

  
130 133
        if get_request():
131 134
            get_response().add_after_job(_('Adding role'), after_job)
......
196 199
            # pylint: disable=unused-variable
197 200
            response, status, data, auth_header = http_delete_request(signed_url)
198 201
            if status != 200:
199
                get_logger().error('failed to remove role %r from user %r', role, user)
202
                get_publisher().record_error(
203
                    _('Failed to remove role %(role)r from user %(user)r') % {'role': role, 'user': user},
204
                    formdata=formdata,
205
                )
200 206

  
201 207
        if get_request():
202 208
            get_response().add_after_job(_('Removing role'), after_job)
wcs/workflows.py
36 36
from .formdata import Evolution
37 37
from .formdef import FormDef, FormdefImportError
38 38
from .mail_templates import MailTemplate
39
from .qommon import _, emails, errors, ezt, force_str, get_cfg, get_logger, misc
39
from .qommon import _, emails, errors, ezt, force_str, get_cfg, misc
40 40
from .qommon.form import (
41 41
    CheckboxWidget,
42 42
    ComputedExpressionWidget,
......
3040 3040
        if not (self.subject and self.body) and not self.mail_template:
3041 3041
            return
3042 3042

  
3043
        url = formdata.get_url()
3044 3043
        body = self.body
3045 3044
        subject = self.subject
3046 3045
        extra_attachments = None
......
3063 3062
                formdata, self.compute(body, render=False), autoescape=body.startswith('<')
3064 3063
            )
3065 3064
        except TemplateError as e:
3066
            get_logger().error(
3067
                'error in template for email body [%s], ' 'mail could not be generated: %s' % (url, str(e))
3065
            get_publisher().record_error(
3066
                _('Error in body template, mail could not be generated'), formdata=formdata, exception=e
3068 3067
            )
3069 3068
            return
3070 3069

  
......
3073 3072
                formdata, self.compute(subject, render=False), autoescape=False
3074 3073
            )
3075 3074
        except TemplateError as e:
3076
            get_logger().error(
3077
                'error in template for email subject [%s], ' 'mail could not be generated: %s' % (url, str(e))
3075
            get_publisher().record_error(
3076
                _('Error in subject template, mail could not be generated'), formdata=formdata, exception=e
3078 3077
            )
3079 3078
            return
3080 3079

  
......
3247 3246
        try:
3248 3247
            sms_body = template_on_formdata(formdata, self.compute(self.body, render=False))
3249 3248
        except TemplateError as e:
3250
            get_logger().error('error in template for sms [%s], sms could not be generated' % str(e))
3249
            get_publisher().record_error(
3250
                _('Error in template, sms could not be generated'), formdata=formdata, exception=e
3251
            )
3251 3252
            return
3252 3253

  
3253 3254
        from .qommon import sms
......
3257 3258
        try:
3258 3259
            sms.SMS.get_sms_class().send(sender, destinations, sms_body)
3259 3260
        except errors.SMSError as e:
3260
            get_logger().error(e)
3261
            get_publisher().record_error(_('Could not send SMS'), formdata=formdata, exception=e)
3261 3262

  
3262 3263

  
3263 3264
register_item_class(SendSMSWorkflowStatusItem)
3264
-