')
return r.getvalue()
@@ -192,8 +192,8 @@ class CardDefCategoryPage(CategoryPage):
category_class = CardDefCategory
category_ui_class = CardDefCategoryUI
formdef_class = CardDef
- usage_title = N_('Card models in this category')
- empty_message = N_('no card model associated to this category')
+ usage_title = _('Card models in this category')
+ empty_message = _('no card model associated to this category')
class CategoriesDirectory(Directory):
@@ -201,7 +201,7 @@ class CategoriesDirectory(Directory):
category_class = Category
category_ui_class = CategoryUI
category_page_class = CategoryPage
- category_explanation = N_('Categories are used to sort the different forms.')
+ category_explanation = _('Categories are used to sort the different forms.')
def _q_index(self):
get_response().add_javascript(['jquery.js', 'jquery-ui.js', 'biglist.js', 'qommon.wysiwyg.js'])
@@ -214,7 +214,7 @@ class CategoriesDirectory(Directory):
r += htmltext('%s') % _('New Category')
r += htmltext('')
r += htmltext('')
- r += htmltext('
%s
') % _(self.category_explanation)
+ r += htmltext('
%s
') % self.category_explanation
categories = self.category_class.select()
r += htmltext('
')
self.category_class.sort_by_position(categories)
@@ -273,4 +273,4 @@ class CardDefCategoriesDirectory(CategoriesDirectory):
category_class = CardDefCategory
category_ui_class = CardDefCategoryUI
category_page_class = CardDefCategoryPage
- category_explanation = N_('Categories are used to sort the different card models.')
+ category_explanation = _('Categories are used to sort the different card models.')
diff --git a/wcs/admin/fields.py b/wcs/admin/fields.py
index b29e970b4..5ae5f464a 100644
--- a/wcs/admin/fields.py
+++ b/wcs/admin/fields.py
@@ -27,7 +27,7 @@ from wcs import fields
from wcs.admin import utils
from wcs.fields import get_field_options
from wcs.formdef import FormDef
-from wcs.qommon import N_, _, errors, get_cfg, misc
+from wcs.qommon import _, errors, get_cfg, misc
from wcs.qommon.admin.menu import command_icon
from wcs.qommon.backoffice.menu import html_top
from wcs.qommon.form import CheckboxWidget, Form, HtmlWidget, SingleSelectWidget, StringWidget
@@ -218,7 +218,7 @@ class FieldsDirectory(Directory):
blacklisted_types = []
page_id = None
field_var_prefix = '..._'
- readonly_message = N_('The fields are readonly.')
+ readonly_message = _('The fields are readonly.')
support_import = True
@@ -323,7 +323,7 @@ class FieldsDirectory(Directory):
if field.required:
required = ''
else:
- required = ' - ' + _('optional')
+ required = ' - %s' % _('optional')
r += htmltext('%s') % required
if getattr(field, 'condition', None):
r += htmltext(' - %s') % _('depending on condition')
@@ -348,8 +348,8 @@ class FieldsDirectory(Directory):
r += htmltext('
')
if self.objectdef.is_readonly():
- get_response().filter['sidebar'] = htmltext('
' % self.category_empty_choice))
form.add(
SingleSelectWidget,
'category_id',
@@ -573,10 +573,10 @@ class FormDefPage(Directory):
formdef_default_workflow = '_default'
options_directory_class = OptionsDirectory
- delete_message = N_('You are about to irrevocably delete this form.')
- delete_title = N_('Deleting Form:')
- overwrite_message = N_('You can replace this form by uploading a file ' 'or by pointing to a form URL.')
- overwrite_success_message = N_(
+ delete_message = _('You are about to irrevocably delete this form.')
+ delete_title = _('Deleting Form:')
+ overwrite_message = _('You can replace this form by uploading a file ' 'or by pointing to a form URL.')
+ overwrite_success_message = _(
'The form has been successfully overwritten. '
'Do note it kept its existing address and role and workflow parameters.'
)
@@ -834,7 +834,7 @@ class FormDefPage(Directory):
except KeyError:
# removed role ?
roles.append(_('Unknown role (%s)') % x)
- value = htmltext(', ').join(roles)
+ value = htmltext(', ').join([str(x) for x in roles])
else:
value = C_('roles|None')
return value
@@ -845,7 +845,7 @@ class FormDefPage(Directory):
auth_contexts = get_publisher().get_supported_authentication_contexts()
value += ' (%s)' % ', '.join(
[
- auth_contexts.get(x)
+ str(auth_contexts.get(x))
for x in self.formdef.required_authentication_contexts
if auth_contexts.get(x)
]
@@ -1180,11 +1180,11 @@ class FormDefPage(Directory):
def duplicate(self):
self.formdefui.formdef.id = None
original_name = self.formdefui.formdef.name
- self.formdefui.formdef.name = self.formdefui.formdef.name + _(' (copy)')
+ self.formdefui.formdef.name = '%s %s' % (self.formdefui.formdef.name, _('(copy)'))
formdef_names = [x.name for x in self.formdef_class.select(lightweight=True)]
no = 2
while self.formdefui.formdef.name in formdef_names:
- self.formdefui.formdef.name = _('%(name)s (copy %(no)d)') % {'name': original_name, 'no': no}
+ self.formdefui.formdef.name = str(_('%(name)s (copy %(no)d)')) % {'name': original_name, 'no': no}
no += 1
self.formdefui.formdef.url_name = None
self.formdefui.formdef.table_name = None
@@ -1212,16 +1212,16 @@ class FormDefPage(Directory):
if check_count_message:
form.widgets.append(HtmlWidget('
' % self.delete_message))
form.add_submit('delete', _('Delete'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse() or (form.is_submitted() and check_count_message):
return redirect('..')
if not form.is_submitted() or form.has_errors():
get_response().breadcrumb.append(('delete', _('Delete')))
- self.html_top(title=_(self.delete_title))
+ self.html_top(title=self.delete_title)
r = TemplateIO(html=True)
- r += htmltext('
%s %s
') % (_(self.delete_title), self.formdef.name)
+ r += htmltext('
%s %s
') % (self.delete_title, self.formdef.name)
r += form.render()
return r.getvalue()
else:
@@ -1249,7 +1249,7 @@ class FormDefPage(Directory):
self.html_top(title=_('Overwrite'))
r = TemplateIO(html=True)
r += htmltext('
%s
') % _('Overwrite')
- r += htmltext('
%s
') % _(self.overwrite_message)
+ r += htmltext('
%s
') % self.overwrite_message
r += form.render()
return r.getvalue()
@@ -1320,7 +1320,7 @@ class FormDefPage(Directory):
new_formdef.roles = self.formdef.roles
self.formdef = new_formdef
self.formdef.store(comment=_('Overwritten'))
- get_session().message = ('info', _(self.overwrite_success_message))
+ get_session().message = ('info', str(self.overwrite_success_message))
return redirect('.')
def get_incompatible_field_ids(self, new_formdef):
@@ -1539,9 +1539,12 @@ class FormDefPage(Directory):
count = self.formdef.data_class().count()
archiver = Archiver(self.formdef)
if count > 100: # Arbitrary threshold
- job = get_response().add_after_job(str(N_('Archiving forms')), archiver.archive)
- job.done_action_url = self.formdef.get_admin_url() + 'archive?job=%s' % job.id
- job.done_action_label = _('Download Archive')
+ job = get_response().add_after_job(
+ _('Archiving forms'),
+ archiver.archive,
+ done_action_url=self.formdef.get_admin_url() + 'archive?job=%(job_id)s',
+ done_action_label=_('Download Archive'),
+ )
job.store()
return redirect(job.get_processing_url())
else:
@@ -1632,9 +1635,12 @@ class FormDefPage(Directory):
count = self.formdef.data_class().count()
anonymiser = Anonymiser(self.formdef, status_ids, before_date)
if count > 100: # Arbitrary threshold
- job = get_response().add_after_job(str(N_('Anonymising forms')), anonymiser.anonymise)
- job.done_action_url = self.formdef.get_admin_url()
- job.done_action_label = _('Back')
+ job = get_response().add_after_job(
+ _('Anonymising forms'),
+ anonymiser.anonymise,
+ done_action_url=self.formdef.get_admin_url(),
+ done_action_label=_('Back'),
+ )
job.store()
return redirect(job.get_processing_url())
else:
@@ -1739,15 +1745,15 @@ class FormsDirectory(AccessControlled, Directory):
formdef_page_class = FormDefPage
formdef_ui_class = FormDefUI
- top_title = N_('Forms')
- import_title = N_('Import Form')
- import_submit_label = N_('Import Form')
- import_paragraph = N_('You can install a new form by uploading a file ' 'or by pointing to the form URL.')
- import_loading_error_message = N_('Error loading form (%s).')
- import_success_message = N_(
+ top_title = _('Forms')
+ import_title = _('Import Form')
+ import_submit_label = _('Import Form')
+ import_paragraph = _('You can install a new form by uploading a file ' 'or by pointing to the form URL.')
+ import_loading_error_message = _('Error loading form (%s).')
+ import_success_message = _(
'This form has been successfully imported. ' 'Do note it is disabled by default.'
)
- import_error_message = N_(
+ import_error_message = _(
'Imported form contained errors and has been automatically fixed, '
'you should nevertheless check everything is ok. '
'Do note it is disabled by default.'
@@ -1761,7 +1767,7 @@ class FormsDirectory(AccessControlled, Directory):
return super()._q_traverse(path)
def _q_index(self):
- self.html_top(title=_(self.top_title))
+ self.html_top(title=self.top_title)
r = TemplateIO(html=True)
get_response().add_javascript(['jquery.js', 'widget_list.js'])
r += self.form_actions()
@@ -1865,7 +1871,7 @@ class FormsDirectory(AccessControlled, Directory):
form.add(FileWidget, 'file', title=_('File'), required=False)
form.add(UrlWidget, 'url', title=_('Address'), required=False, size=50)
- form.add_submit('submit', _(self.import_submit_label))
+ form.add_submit('submit', self.import_submit_label)
form.add_submit('cancel', _('Cancel'))
if form.get_submit() == 'cancel':
@@ -1878,10 +1884,10 @@ class FormsDirectory(AccessControlled, Directory):
pass
get_response().breadcrumb.append(('import', _('Import')))
- self.html_top(title=_(self.import_title))
+ self.html_top(title=self.import_title)
r = TemplateIO(html=True)
- r += htmltext('
%s
') % _(self.import_title)
- r += htmltext('
%s
') % _(self.import_paragraph)
+ r += htmltext('
%s
') % self.import_title
+ r += htmltext('
%s
') % self.import_paragraph
r += form.render()
return r.getvalue()
@@ -1894,7 +1900,7 @@ class FormsDirectory(AccessControlled, Directory):
try:
fp = misc.urlopen(url)
except misc.ConnectionError as e:
- form.set_error('url', _(self.import_loading_error_message) % str(e))
+ form.set_error('url', self.import_loading_error_message % str(e))
raise ValueError()
else:
form.set_error('file', _('You have to enter a file or a URL.'))
@@ -1904,11 +1910,11 @@ class FormsDirectory(AccessControlled, Directory):
try:
try:
formdef = self.formdef_class.import_from_xml(fp)
- get_session().message = ('info', _(self.import_success_message))
+ get_session().message = ('info', str(self.import_success_message))
except FormdefImportRecoverableError as e:
fp.seek(0)
formdef = self.formdef_class.import_from_xml(fp, fix_on_error=True)
- get_session().message = ('info', _(self.import_error_message))
+ get_session().message = ('info', str(self.import_error_message))
except FormdefImportError as e:
error = True
reason = _(e.msg) % e.msg_args
@@ -1936,7 +1942,7 @@ class FormsDirectory(AccessControlled, Directory):
class UpdateDigestAfterJob(AfterJob):
- label = N_('Updating digests')
+ label = _('Updating digests')
def __init__(self, formdef):
super().__init__(formdef_class=formdef.__class__, formdef_id=formdef.id)
diff --git a/wcs/admin/settings.py b/wcs/admin/settings.py
index 5396f620f..a6a028d9e 100644
--- a/wcs/admin/settings.py
+++ b/wcs/admin/settings.py
@@ -38,7 +38,7 @@ from wcs.blocks import BlockDef
from wcs.carddef import CardDef
from wcs.data_sources import NamedDataSource
from wcs.formdef import FormDef
-from wcs.qommon import N_, _, errors, get_cfg, ident, misc, template
+from wcs.qommon import _, errors, get_cfg, ident, misc, template
from wcs.qommon.admin.cfg import cfg_submit
from wcs.qommon.admin.emails import EmailsDirectory
from wcs.qommon.admin.logger import LoggerDirectory
@@ -706,13 +706,13 @@ class SettingsDirectory(QommonSettingsDirectory):
permission_keys = []
admin_sections = [
- ('forms', N_('Forms')),
- ('cards', N_('Card Models')),
- ('workflows', N_('Workflows')),
- ('users', N_('Users')),
- ('roles', N_('Roles')),
- ('categories', N_('Categories')),
- ('settings', N_('Settings')),
+ ('forms', _('Forms')),
+ ('cards', _('Card Models')),
+ ('workflows', _('Workflows')),
+ ('users', _('Users')),
+ ('roles', _('Roles')),
+ ('categories', _('Categories')),
+ ('settings', _('Settings')),
]
for k, v in admin_sections:
if k == 'cards' and not StudioDirectory.is_visible():
@@ -1128,10 +1128,13 @@ class SettingsDirectory(QommonSettingsDirectory):
exporter = Exporter(dirs, settings=form.get_widget('settings').parse())
- job = get_response().add_after_job(N_('Exporting site settings'), exporter.export)
- job.done_action_url = get_request().get_url() + '?download=%s' % job.id
- job.done_action_label = _('Download Export')
- job.done_button_attributes = {'download': 'export.wcs'}
+ job = get_response().add_after_job(
+ _('Exporting site settings'),
+ exporter.export,
+ done_action_url=get_request().get_url() + '?download=%(job_id)s',
+ done_action_label=_('Download Export'),
+ done_button_attributes={'download': 'export.wcs'},
+ )
job.store()
return redirect(job.get_processing_url())
diff --git a/wcs/admin/users.py b/wcs/admin/users.py
index 7c4e31cdb..440049e54 100644
--- a/wcs/admin/users.py
+++ b/wcs/admin/users.py
@@ -19,7 +19,7 @@ from quixote.directory import Directory
from quixote.html import TemplateIO, htmltext
import wcs.qommon.storage as st
-from wcs.qommon import N_, _, errors, force_str, get_cfg, ident, misc
+from wcs.qommon import _, errors, force_str, get_cfg, ident, misc
from wcs.qommon.admin.emails import EmailsDirectory
from wcs.qommon.admin.menu import error_page
from wcs.qommon.backoffice.listing import pagination_links
@@ -537,11 +537,11 @@ class UsersDirectory(Directory):
EmailsDirectory.register(
'email_with_token',
- N_('Identification token'),
- N_('Available variables: token, token_url, sitename'),
- category=N_('Identification'),
- default_subject=N_('Access to [sitename]'),
- default_body=N_(
+ _('Identification token'),
+ _('Available variables: token, token_url, sitename'),
+ category=_('Identification'),
+ default_subject=_('Access to [sitename]'),
+ default_body=_(
'''\
Hello,
diff --git a/wcs/admin/workflows.py b/wcs/admin/workflows.py
index a3f40e724..a8b026854 100644
--- a/wcs/admin/workflows.py
+++ b/wcs/admin/workflows.py
@@ -32,7 +32,7 @@ from wcs.backoffice.studio import StudioDirectory
from wcs.carddef import CardDef
from wcs.formdata import Evolution
from wcs.formdef import FormDef
-from wcs.qommon import N_, _, errors, force_str, get_logger, misc, template
+from wcs.qommon import _, errors, force_str, get_logger, misc, template
from wcs.qommon.admin.menu import command_icon
from wcs.qommon.backoffice.menu import html_top
from wcs.qommon.form import (
@@ -356,7 +356,7 @@ class WorkflowItemPage(Directory):
if not self.workflow.is_readonly():
submit_label = _('Submit')
if hasattr(self.item, 'submit_button_label'):
- submit_label = _(self.item.submit_button_label)
+ submit_label = self.item.submit_button_label
form.add_submit('submit', submit_label)
form.add_submit('cancel', _('Cancel'))
@@ -955,7 +955,7 @@ class WorkflowVariablesFieldsDirectory(FieldsDirectory):
support_import = False
blacklisted_types = ['page', 'blocks', 'computed']
field_var_prefix = 'form_option_'
- readonly_message = N_('This workflow is readonly.')
+ readonly_message = _('This workflow is readonly.')
def index_top(self):
r = TemplateIO(html=True)
@@ -978,7 +978,7 @@ class WorkflowBackofficeFieldsDirectory(FieldsDirectory):
blacklisted_types = ['page', 'blocks', 'computed']
blacklisted_attributes = ['condition']
field_var_prefix = 'form_var_'
- readonly_message = N_('This workflow is readonly.')
+ readonly_message = _('This workflow is readonly.')
def index_top(self):
r = TemplateIO(html=True)
@@ -1746,11 +1746,14 @@ class WorkflowPage(Directory):
def duplicate(self):
self.workflow_ui.workflow.id = None
original_name = self.workflow_ui.workflow.name
- self.workflow_ui.workflow.name = self.workflow_ui.workflow.name + _(' (copy)')
+ self.workflow_ui.workflow.name = '%s %s' % (self.workflow_ui.workflow.name, _('(copy)'))
workflow_names = [x.name for x in Workflow.select()]
no = 2
while self.workflow_ui.workflow.name in workflow_names:
- self.workflow_ui.workflow.name = _('%(name)s (copy %(no)d)') % {'name': original_name, 'no': no}
+ self.workflow_ui.workflow.name = str(_('%(name)s (copy %(no)d)')) % {
+ 'name': original_name,
+ 'no': no,
+ }
no += 1
self.workflow_ui.workflow.store()
return redirect('../%s/' % self.workflow_ui.workflow.id)
diff --git a/wcs/api.py b/wcs/api.py
index d7e4b1bbe..1f9854cd1 100644
--- a/wcs/api.py
+++ b/wcs/api.py
@@ -1261,7 +1261,7 @@ def validate_expression(request, *args, **kwargs):
if expression and re.match(r'^=.*\[[a-zA-Z_]\w*\]', expression):
hint['klass'] = 'warning'
hint['msg'] = _('Make sure you want a Python expression, not a simple template string.')
- return HttpResponse(json.dumps(hint), content_type='application/json')
+ return JsonResponse(hint)
def validate_condition(request, *args, **kwargs):
@@ -1274,7 +1274,7 @@ def validate_condition(request, *args, **kwargs):
except ValidationError as e:
hint['klass'] = 'error'
hint['msg'] = str(e)
- return HttpResponse(json.dumps(hint), content_type='application/json')
+ return JsonResponse(hint)
def provisionning(request):
diff --git a/wcs/backoffice/cards.py b/wcs/backoffice/cards.py
index 8430c0f10..944b675d1 100644
--- a/wcs/backoffice/cards.py
+++ b/wcs/backoffice/cards.py
@@ -28,7 +28,7 @@ from wcs.carddef import CardDef
from wcs.categories import CardDefCategory
from wcs.workflows import Workflow
-from ..qommon import N_, _
+from ..qommon import _
from ..qommon.misc import C_
from ..qommon.storage import NotEqual, Null
@@ -40,7 +40,7 @@ class CardDefUI(FormDefUI):
class CardDefOptionsDirectory(OptionsDirectory):
category_class = CardDefCategory
- category_empty_choice = N_('Select a category for this card model')
+ category_empty_choice = _('Select a category for this card model')
class CardDefPage(FormDefPage):
@@ -51,12 +51,12 @@ class CardDefPage(FormDefPage):
options_directory_class = CardDefOptionsDirectory
- delete_message = N_('You are about to irrevocably delete this card model.')
- delete_title = N_('Deleting Card Model:')
- overwrite_message = N_(
+ delete_message = _('You are about to irrevocably delete this card model.')
+ delete_title = _('Deleting Card Model:')
+ overwrite_message = _(
'You can replace this card model by uploading a file ' 'or by pointing to a form URL.'
)
- overwrite_success_message = N_(
+ overwrite_success_message = _(
'The card model has been successfully overwritten. '
'Do note it kept its existing address and role and workflow parameters.'
)
@@ -250,15 +250,15 @@ class CardsDirectory(FormsDirectory):
formdef_page_class = CardDefPage
formdef_ui_class = CardDefUI
- top_title = N_('Card Models')
- import_title = N_('Import Card Model')
- import_submit_label = N_('Import Card Model')
- import_paragraph = N_(
+ top_title = _('Card Models')
+ import_title = _('Import Card Model')
+ import_submit_label = _('Import Card Model')
+ import_paragraph = _(
'You can install a new card model by uploading a file ' 'or by pointing to the card model URL.'
)
- import_loading_error_message = N_('Error loading card model (%s).')
- import_success_message = N_('This card model has been successfully imported. ')
- import_error_message = N_(
+ import_loading_error_message = _('Error loading card model (%s).')
+ import_success_message = _('This card model has been successfully imported. ')
+ import_error_message = _(
'Imported card model contained errors and has been automatically fixed, '
'you should nevertheless check everything is ok. '
)
diff --git a/wcs/backoffice/data_management.py b/wcs/backoffice/data_management.py
index 578cc3a61..ae3cef0ed 100644
--- a/wcs/backoffice/data_management.py
+++ b/wcs/backoffice/data_management.py
@@ -26,7 +26,7 @@ from wcs import fields
from wcs.carddef import CardDef
from wcs.categories import CardDefCategory
-from ..qommon import N_, _, errors, template
+from ..qommon import _, errors, template
from ..qommon.afterjobs import AfterJob
from ..qommon.backoffice.menu import html_top
from ..qommon.form import FileWidget, Form
@@ -99,7 +99,7 @@ class CardPage(FormPage):
]
admin_permission = 'cards'
formdef_class = CardDef
- search_label = N_('Search in card content')
+ search_label = _('Search in card content')
@property
def add(self):
@@ -331,8 +331,8 @@ class CardFillPage(FormFillPage):
class CardBackOfficeStatusPage(FormBackOfficeStatusPage):
form_page_class = CardFillPage
- sidebar_recorded_message = N_('The card has been recorded on %(date)s with the number %(number)s.')
- sidebar_recorded_by_agent_message = N_(
+ sidebar_recorded_message = _('The card has been recorded on %(date)s with the number %(number)s.')
+ sidebar_recorded_by_agent_message = _(
'The card has been recorded on %(date)s with the number %(number)s by %(agent)s.'
)
diff --git a/wcs/backoffice/management.py b/wcs/backoffice/management.py
index edc2d1595..f5823b370 100644
--- a/wcs/backoffice/management.py
+++ b/wcs/backoffice/management.py
@@ -44,7 +44,7 @@ from wcs.roles import logged_users_role
from wcs.variables import LazyFieldVar
from wcs.workflows import WorkflowStatusItem, template_on_formdata
-from ..qommon import N_, _, emails, errors, ezt, force_str, get_cfg, get_logger, misc, ngettext, ods, sms
+from ..qommon import _, emails, errors, ezt, force_str, get_cfg, get_logger, misc, ngettext, ods, sms
from ..qommon.admin.emails import EmailsDirectory
from ..qommon.admin.menu import command_icon
from ..qommon.afterjobs import AfterJob
@@ -127,7 +127,7 @@ def geojson_formdatas(formdatas, geoloc_key='base', fields=None):
'url': formdata_backoffice_url,
'status_name': str(htmlescape(status.name)),
'status_colour': '#%s' % status_colour,
- 'view_label': _('View'),
+ 'view_label': force_text(_('View')),
},
'geometry': {
'type': 'Point',
@@ -1060,7 +1060,7 @@ class ManagementDirectory(Directory):
FakeField('status', 'status', _('Status')),
]
get_response().set_content_type('application/json')
- return json.dumps(geojson_formdatas(formdatas, fields=fields))
+ return json.dumps(geojson_formdatas(formdatas, fields=fields), cls=misc.JSONEncoder)
def map(self):
if not get_publisher().is_using_postgresql():
@@ -1107,7 +1107,7 @@ class FormPage(Directory):
use_default_view = False
admin_permission = 'forms'
formdef_class = FormDef
- search_label = N_('Search in form content')
+ search_label = _('Search in form content')
WCS_SYNC_EXPORT_LIMIT = 100 # Arbitrary threshold
def __init__(self, component=None, formdef=None, view=None, update_breadcrumbs=True):
@@ -1568,7 +1568,7 @@ class FormPage(Directory):
r += htmltext('') % order_by
if get_publisher().is_using_postgresql():
- r += htmltext('
%s
') % _(self.search_label)
+ r += htmltext('
%s
') % self.search_label
if get_request().form.get('q'):
q = force_text(get_request().form.get('q'))
r += htmltext('') % force_str(q)
@@ -2331,7 +2331,7 @@ class FormPage(Directory):
fields, selected_filter, user=user, query=query, criterias=criterias
)[0]
- return json.dumps(geojson_formdatas(items, fields=fields))
+ return json.dumps(geojson_formdatas(items, fields=fields), cls=misc.JSONEncoder)
def ics(self):
if get_request().has_anonymised_data_api_restriction():
@@ -2716,8 +2716,8 @@ class FormBackOfficeStatusPage(FormStatusPage):
]
form_page_class = FormFillPage
- sidebar_recorded_message = N_('The form has been recorded on %(date)s with the number %(number)s.')
- sidebar_recorded_by_agent_message = N_(
+ sidebar_recorded_message = _('The form has been recorded on %(date)s with the number %(number)s.')
+ sidebar_recorded_by_agent_message = _(
'The form has been recorded on %(date)s with the number %(number)s by %(agent)s.'
)
@@ -2811,13 +2811,13 @@ class FormBackOfficeStatusPage(FormStatusPage):
agent_user = get_publisher().user_class.get(formdata.submission_agent_id, ignore_errors=True)
if agent_user:
- r += _(self.sidebar_recorded_by_agent_message) % {
+ r += self.sidebar_recorded_by_agent_message % {
'date': tm,
'number': formdata.get_display_id(),
'agent': agent_user.get_display_name(),
}
else:
- r += _(self.sidebar_recorded_message) % {'date': tm, 'number': formdata.get_display_id()}
+ r += self.sidebar_recorded_message % {'date': tm, 'number': formdata.get_display_id()}
r += htmltext('
')
try:
status_colour = formdata.get_status().colour
@@ -3441,7 +3441,7 @@ class FakeField:
def __init__(self, id, type_, label, addable=True):
self.id = id
self.type = type_
- self.label = label
+ self.label = force_text(label)
self.fake = True
self.varname = id.replace('-', '_')
self.store_display_value = None
@@ -3545,10 +3545,10 @@ var month_line = %(month_line)s;
var year_line = %(year_line)s;
'''
% {
- 'weekday_line': json.dumps(weekday_totals),
- 'hour_line': json.dumps(hour_totals),
- 'month_line': json.dumps(monthly_totals),
- 'year_line': json.dumps(yearly_totals),
+ 'weekday_line': json.dumps(weekday_totals, cls=misc.JSONEncoder),
+ 'hour_line': json.dumps(hour_totals, cls=misc.JSONEncoder),
+ 'month_line': json.dumps(monthly_totals, cls=misc.JSONEncoder),
+ 'year_line': json.dumps(yearly_totals, cls=misc.JSONEncoder),
}
)
@@ -3735,7 +3735,7 @@ class MassActionAfterJob(AfterJob):
class CsvExportAfterJob(AfterJob):
- label = N_('Exporting forms in CSV')
+ label = _('Exporting to CSV file')
def __init__(self, formdef, **kwargs):
super().__init__(formdef_class=formdef.__class__, formdef_id=formdef.id, **kwargs)
@@ -3799,6 +3799,8 @@ class CsvExportAfterJob(AfterJob):
class OdsExportAfterJob(CsvExportAfterJob):
+ label = _('Exporting to ODS file')
+
def __init__(self, formdef, **kwargs):
super().__init__(formdef=formdef, **kwargs)
self.file_name = '%s.ods' % formdef.url_name
diff --git a/wcs/backoffice/root.py b/wcs/backoffice/root.py
index 73d79292e..a4bb6066c 100644
--- a/wcs/backoffice/root.py
+++ b/wcs/backoffice/root.py
@@ -28,7 +28,7 @@ import wcs.admin.users
import wcs.admin.workflows
from wcs.formdef import FormDef
-from ..qommon import N_, _, errors, get_cfg, misc, template
+from ..qommon import _, errors, get_cfg, misc, template
from ..qommon.afterjobs import AfterJob
from ..qommon.backoffice import BackofficeRootDirectory
from ..qommon.backoffice.menu import html_top
@@ -54,16 +54,16 @@ class RootDirectory(BackofficeRootDirectory):
submission = SubmissionDirectory()
menu_items = [
- ('submission/', N_('Submission')),
- ('management/', N_('Management')),
- ('data/', N_('Cards'), {'check_display_function': studio.is_visible}),
- ('studio/', N_('Studio'), {'check_display_function': studio.is_visible}),
- ('forms/', N_('Forms Workshop'), {'sub': True}),
- ('cards/', N_('Card Models'), {'sub': True, 'check_display_function': studio.is_visible}),
- ('workflows/', N_('Workflows Workshop'), {'sub': True}),
- ('users/', N_('Users'), {'check_display_function': roles.is_visible}),
- ('roles/', N_('Roles'), {'check_display_function': roles.is_visible}),
- ('settings/', N_('Settings')),
+ ('submission/', _('Submission')),
+ ('management/', _('Management')),
+ ('data/', _('Cards'), {'check_display_function': studio.is_visible}),
+ ('studio/', _('Studio'), {'check_display_function': studio.is_visible}),
+ ('forms/', _('Forms Workshop'), {'sub': True}),
+ ('cards/', _('Card Models'), {'sub': True, 'check_display_function': studio.is_visible}),
+ ('workflows/', _('Workflows Workshop'), {'sub': True}),
+ ('users/', _('Users'), {'check_display_function': roles.is_visible}),
+ ('roles/', _('Roles'), {'check_display_function': roles.is_visible}),
+ ('settings/', _('Settings')),
]
def _q_traverse(self, path):
diff --git a/wcs/blocks.py b/wcs/blocks.py
index 7934b0c08..934efa6ec 100644
--- a/wcs/blocks.py
+++ b/wcs/blocks.py
@@ -21,7 +21,7 @@ from quixote import get_publisher, get_request
from quixote.html import htmltag, htmltext
from . import data_sources, fields
-from .qommon import N_, _, misc
+from .qommon import _, misc
from .qommon.form import CompositeWidget, WidgetList
from .qommon.storage import StorableObject
from .qommon.template import Template
@@ -136,7 +136,7 @@ class BlockDef(StorableObject):
unknown_datasources.add(data_source.get('type'))
if unknown_datasources:
raise BlockdefImportError(
- N_('Unknown datasources'), details=', '.join(sorted(unknown_datasources))
+ _('Unknown datasources'), details=', '.join(sorted(unknown_datasources))
)
return blockdef
@@ -146,7 +146,7 @@ class BlockDef(StorableObject):
charset = 'utf-8'
blockdef = cls()
if tree.find('name') is None or not tree.find('name').text:
- raise BlockdefImportError(N_('Missing name'))
+ raise BlockdefImportError(_('Missing name'))
# if the tree we get is actually a ElementTree for real, we get its
# root element and go on happily.
@@ -154,7 +154,7 @@ class BlockDef(StorableObject):
tree = tree.getroot()
if tree.tag != cls.xml_root_node:
- raise BlockdefImportError(N_('Unexpected root node'))
+ raise BlockdefImportError(_('Unexpected root node'))
if include_id and tree.attrib.get('id'):
blockdef.id = tree.attrib.get('id')
@@ -169,7 +169,7 @@ class BlockDef(StorableObject):
try:
field_o = fields.get_field_class_by_type(field.findtext('type'))()
except KeyError:
- raise BlockdefImportError(N_('Unknown field type'), details=field.findtext('type'))
+ raise BlockdefImportError(_('Unknown field type'), details=field.findtext('type'))
field_o.init_with_xml(field, charset, include_id=True)
blockdef.fields.append(field_o)
diff --git a/wcs/carddef.py b/wcs/carddef.py
index 5cd8e90a8..ab1d2eed5 100644
--- a/wcs/carddef.py
+++ b/wcs/carddef.py
@@ -23,7 +23,7 @@ from wcs.carddata import CardData
from wcs.categories import CardDefCategory
from wcs.formdef import FormDef, get_formdefs_of_all_kinds
-from .qommon import N_, _, misc
+from .qommon import _, force_text, misc
from .qommon.storage import Equal, ILike, NotEqual
from .qommon.template import Template
@@ -36,8 +36,8 @@ class CardDef(FormDef):
data_sql_prefix = 'carddata'
pickle_module_name = 'carddef'
xml_root_node = 'carddef'
- verbose_name = N_('Card model')
- verbose_name_plural = N_('Card models')
+ verbose_name = _('Card model')
+ verbose_name_plural = _('Card models')
confirmation = False
@@ -93,19 +93,19 @@ class CardDef(FormDef):
from wcs.wf.remove import RemoveWorkflowStatusItem
from wcs.workflows import ChoiceWorkflowStatusItem, EditableWorkflowStatusItem, Workflow
- workflow = Workflow(name=_('Default (cards)'))
+ workflow = Workflow(name=force_text(_('Default (cards)')))
workflow.id = '_carddef_default'
workflow.roles = {
- '_viewer': _('Viewer'),
- '_editor': _('Editor'),
+ '_viewer': force_text(_('Viewer')),
+ '_editor': force_text(_('Editor')),
}
- status = workflow.add_status(_('Recorded'), 'recorded')
- deleted_status = workflow.add_status(_('Deleted'), 'deleted')
+ status = workflow.add_status(force_text(_('Recorded')), 'recorded')
+ deleted_status = workflow.add_status(force_text(_('Deleted')), 'deleted')
editable = EditableWorkflowStatusItem()
editable.id = '_editable'
editable.by = ['_editor']
- editable.label = _('Edit Card')
+ editable.label = force_text(_('Edit Card'))
editable.status = status.id
editable.parent = status
status.items.append(editable)
@@ -113,7 +113,7 @@ class CardDef(FormDef):
action_delete = ChoiceWorkflowStatusItem()
action_delete.id = '_action_delete'
action_delete.by = ['_editor']
- action_delete.label = _('Delete Card')
+ action_delete.label = force_text(_('Delete Card'))
action_delete.status = deleted_status.id
action_delete.require_confirmation = True
action_delete.parent = status
diff --git a/wcs/categories.py b/wcs/categories.py
index 76b9afb52..1d03b19b1 100644
--- a/wcs/categories.py
+++ b/wcs/categories.py
@@ -17,7 +17,7 @@
from quixote import get_publisher
from quixote.html import htmltext
-from .qommon import N_
+from .qommon import _
from .qommon.misc import simplify
from .qommon.storage import StorableObject
from .qommon.substitution import Substitutions
@@ -128,6 +128,6 @@ class CardDefCategory(Category):
XML_NODES = [('name', 'str'), ('url_name', 'str'), ('description', 'str'), ('position', 'int')]
-Substitutions.register('category_name', category=N_('General'), comment=N_('Category Name'))
-Substitutions.register('category_description', category=N_('General'), comment=N_('Category Description'))
-Substitutions.register('category_id', category=N_('General'), comment=N_('Category Identifier'))
+Substitutions.register('category_name', category=_('General'), comment=_('Category Name'))
+Substitutions.register('category_description', category=_('General'), comment=_('Category Description'))
+Substitutions.register('category_id', category=_('General'), comment=_('Category Identifier'))
diff --git a/wcs/data_sources.py b/wcs/data_sources.py
index 2135de4ae..7b25ecb47 100644
--- a/wcs/data_sources.py
+++ b/wcs/data_sources.py
@@ -26,7 +26,7 @@ from quixote import get_publisher, get_request, get_session
from quixote.html import TemplateIO
from .api_utils import sign_url_auto_orig
-from .qommon import N_, _, force_str, get_logger, misc
+from .qommon import _, force_str, get_logger, misc
from .qommon.afterjobs import AfterJob
from .qommon.cron import CronJob
from .qommon.form import CompositeWidget, OptGroup, SingleSelectWidget, StringWidget
@@ -916,7 +916,7 @@ def build_agenda_datasources(publisher):
class RefreshAgendas(AfterJob):
- label = N_('Refreshing agendas')
+ label = _('Refreshing agendas')
def execute(self):
build_agenda_datasources(get_publisher())
diff --git a/wcs/fields.py b/wcs/fields.py
index 8b1fa8cad..cdb9680a5 100644
--- a/wcs/fields.py
+++ b/wcs/fields.py
@@ -201,7 +201,7 @@ class PrefillSelectionWidget(CompositeWidget):
attrs={
'data-dynamic-display-child-of': 'prefill$type',
'data-dynamic-display-value-in': '|'.join(
- [x[1] for x in options if x[0] not in ('none', 'geolocation')]
+ [str(x[1]) for x in options if x[0] not in ('none', 'geolocation')]
),
'inline_title': _('Locked'),
},
@@ -268,7 +268,7 @@ class Field:
pass
def get_type_label(self):
- return _(self.description)
+ return self.description
@property
def include_in_listing(self):
@@ -822,7 +822,7 @@ def register_field_class(klass):
class TitleField(Field):
key = 'title'
- description = N_('Title')
+ description = _('Title')
html_tag = 'h3'
display_locations = ['validation', 'summary']
@@ -885,7 +885,7 @@ register_field_class(TitleField)
class SubtitleField(TitleField):
key = 'subtitle'
- description = N_('Subtitle')
+ description = _('Subtitle')
html_tag = 'h4'
@@ -894,7 +894,7 @@ register_field_class(SubtitleField)
class CommentField(Field):
key = 'comment'
- description = N_('Comment')
+ description = _('Comment')
display_locations = []
def get_text(self):
@@ -995,7 +995,7 @@ def is_datasource_advanced(value):
class StringField(WidgetField):
key = 'string'
- description = N_('Text (line)')
+ description = _('Text (line)')
widget_class = WcsExtraStringWidget
size = None
@@ -1091,7 +1091,7 @@ register_field_class(StringField)
class TextField(WidgetField):
key = 'text'
- description = N_('Long Text')
+ description = _('Long Text')
widget_class = TextWidget
cols = None
@@ -1156,7 +1156,7 @@ register_field_class(TextField)
class EmailField(WidgetField):
key = 'email'
- description = N_('Email')
+ description = _('Email')
widget_class = EmailWidget
@@ -1181,7 +1181,7 @@ register_field_class(EmailField)
class BoolField(WidgetField):
key = 'bool'
- description = N_('Check Box (single choice)')
+ description = _('Check Box (single choice)')
allow_complex = True
widget_class = CheckboxWidget
@@ -1202,9 +1202,9 @@ class BoolField(WidgetField):
def get_view_value(self, value, **kwargs):
if value is True or value == 'True':
- return _('Yes')
+ return str(_('Yes'))
elif value is False or value == 'False':
- return _('No')
+ return str(_('No'))
else:
return ''
@@ -1278,7 +1278,7 @@ register_field_class(BoolField)
class FileField(WidgetField):
key = 'file'
- description = N_('File Upload')
+ description = _('File Upload')
allow_complex = True
document_type = None
@@ -1497,7 +1497,7 @@ class FileField(WidgetField):
# self.file_type is a combination of file type, we create a
# virtual one from them
if parts and len(parts) > 1:
- label = ', '.join(parts)
+ label = ', '.join([str(x) for x in parts])
else:
label = ','.join(file_type)
self.document_type = {
@@ -1534,7 +1534,7 @@ register_field_class(FileField)
class DateField(WidgetField):
key = 'date'
- description = N_('Date')
+ description = _('Date')
widget_class = DateWidget
minimum_date = None
@@ -1807,7 +1807,7 @@ class ItemFieldMixin:
class ItemField(WidgetField, MapOptionsMixin, ItemFieldMixin):
key = 'item'
- description = N_('List')
+ description = _('List')
allow_complex = True
items = []
@@ -2163,7 +2163,7 @@ register_field_class(ItemField)
class ItemsField(WidgetField, ItemFieldMixin):
key = 'items'
- description = N_('Multiple choice list')
+ description = _('Multiple choice list')
allow_complex = True
items = []
@@ -2501,7 +2501,7 @@ class PageCondition(Condition):
class PageField(Field):
key = 'page'
- description = N_('Page')
+ description = _('Page')
post_conditions = None
@@ -2595,7 +2595,7 @@ register_field_class(PageField)
class TableField(WidgetField):
key = 'table'
- description = N_('Table')
+ description = _('Table')
allow_complex = True
rows = None
@@ -2764,7 +2764,7 @@ register_field_class(TableField)
class TableSelectField(TableField):
key = 'table-select'
- description = N_('Table of Lists')
+ description = _('Table of Lists')
allow_complex = True
items = None
@@ -2813,7 +2813,7 @@ register_field_class(TableSelectField)
class TableRowsField(WidgetField):
key = 'tablerows'
- description = N_('Table with rows')
+ description = _('Table with rows')
allow_complex = True
total_row = True
@@ -2956,7 +2956,7 @@ register_field_class(TableRowsField)
class MapField(WidgetField, MapOptionsMixin):
key = 'map'
- description = N_('Map')
+ description = _('Map')
default_position = None
init_with_geoloc = False
@@ -3054,7 +3054,7 @@ register_field_class(MapField)
class RankedItemsField(WidgetField):
key = 'ranked-items'
- description = N_('Ranked Items')
+ description = _('Ranked Items')
allow_complex = True
items = []
@@ -3141,7 +3141,7 @@ register_field_class(RankedItemsField)
class PasswordField(WidgetField):
key = 'password'
- description = N_('Password')
+ description = _('Password')
min_length = 0
max_length = 0
@@ -3465,13 +3465,13 @@ def get_field_options(blacklisted_types):
if klass.key in blacklisted_types:
continue
if issubclass(klass, WidgetField):
- widgets.append((klass.key, _(klass.description), klass.key))
+ widgets.append((klass.key, klass.description, klass.key))
else:
- non_widgets.append((klass.key, _(klass.description), klass.key))
+ non_widgets.append((klass.key, klass.description, klass.key))
options = widgets + [('', '—', '')] + non_widgets
# add computed field in its own "section"
- options.extend([('', '—', ''), (ComputedField.key, _(ComputedField.description), ComputedField.key)])
+ options.extend([('', '—', ''), (ComputedField.key, ComputedField.description, ComputedField.key)])
if get_publisher().has_site_option('fields-blocks') and (
not blacklisted_types or 'blocks' not in blacklisted_types
diff --git a/wcs/formdata.py b/wcs/formdata.py
index 59fcf1b04..f0b0e9b5e 100644
--- a/wcs/formdata.py
+++ b/wcs/formdata.py
@@ -25,7 +25,7 @@ import time
from quixote import get_publisher, get_request, get_session
from quixote.http_request import Upload
-from .qommon import N_, _, misc
+from .qommon import _, misc
from .qommon.evalutils import make_datetime
from .qommon.publisher import get_cfg
from .qommon.storage import Contains, Intersects, Null, StorableObject
@@ -1421,14 +1421,14 @@ class FormData(StorableObject):
self.__dict__ = dict
-Substitutions.register('form_receipt_date', category=N_('Form'), comment=N_('Form Receipt Date'))
-Substitutions.register('form_receipt_time', category=N_('Form'), comment=N_('Form Receipt Time'))
-Substitutions.register('form_number', category=N_('Form'), comment=N_('Form Number'))
-Substitutions.register('form_details', category=N_('Form'), comment=N_('Form Details'))
-Substitutions.register('form_url', category=N_('Form'), comment=N_('Form URL'))
-Substitutions.register('form_url_backoffice', category=N_('Form'), comment=N_('Form URL (backoffice)'))
-Substitutions.register('form_status_url', category=N_('Form'), comment=N_('Form Status URL'))
-Substitutions.register('form_tracking_code', category=N_('Form'), comment=N_('Form Tracking Code'))
-Substitutions.register('form_user_display_name', category=N_('Form'), comment=N_('Form Submitter Name'))
-Substitutions.register('form_user_email', category=N_('Form'), comment=N_('Form Submitter Email'))
+Substitutions.register('form_receipt_date', category=_('Form'), comment=_('Form Receipt Date'))
+Substitutions.register('form_receipt_time', category=_('Form'), comment=_('Form Receipt Time'))
+Substitutions.register('form_number', category=_('Form'), comment=_('Form Number'))
+Substitutions.register('form_details', category=_('Form'), comment=_('Form Details'))
+Substitutions.register('form_url', category=_('Form'), comment=_('Form URL'))
+Substitutions.register('form_url_backoffice', category=_('Form'), comment=_('Form URL (backoffice)'))
+Substitutions.register('form_status_url', category=_('Form'), comment=_('Form Status URL'))
+Substitutions.register('form_tracking_code', category=_('Form'), comment=_('Form Tracking Code'))
+Substitutions.register('form_user_display_name', category=_('Form'), comment=_('Form Submitter Name'))
+Substitutions.register('form_user_email', category=_('Form'), comment=_('Form Submitter Email'))
Substitutions.register_dynamic_source(FormData)
diff --git a/wcs/formdef.py b/wcs/formdef.py
index 7d94c2794..1003ab9a0 100644
--- a/wcs/formdef.py
+++ b/wcs/formdef.py
@@ -35,7 +35,7 @@ from quixote.http_request import Upload
from . import data_sources, fields
from .categories import Category
from .formdata import FormData
-from .qommon import N_, PICKLE_KWARGS, _, force_str, get_cfg
+from .qommon import PICKLE_KWARGS, _, force_str, get_cfg
from .qommon.admin.emails import EmailsDirectory
from .qommon.cron import CronJob
from .qommon.form import Form, HtmlWidget, UploadedFile
@@ -84,8 +84,8 @@ class FormDef(StorableObject):
data_sql_prefix = 'formdata'
pickle_module_name = 'formdef'
xml_root_node = 'formdef'
- verbose_name = N_('Form')
- verbose_name_plural = N_('Forms')
+ verbose_name = _('Form')
+ verbose_name_plural = _('Forms')
name = None
description = None
@@ -916,7 +916,7 @@ class FormDef(StorableObject):
try:
field_o = fields.get_field_class_by_type(field.get('type'))()
except KeyError:
- raise FormdefImportError(N_('Unknown field type'), details=field.findtext('type'))
+ raise FormdefImportError(_('Unknown field type'), details=field.findtext('type'))
field_o.init_with_json(field, include_id=True)
if not field_o.id:
# this assumes all fields will have id, or none of them
@@ -1100,7 +1100,7 @@ class FormDef(StorableObject):
known_field_ids = set()
for field in formdef.fields:
if field.id in known_field_ids:
- raise FormdefImportRecoverableError(N_('Duplicated field identifiers'))
+ raise FormdefImportRecoverableError(_('Duplicated field identifiers'))
known_field_ids.add(field.id)
return formdef
@@ -1116,7 +1116,7 @@ class FormDef(StorableObject):
assert charset == 'utf-8'
formdef = cls()
if tree.find('name') is None or not tree.find('name').text:
- raise FormdefImportError(N_('Missing name'))
+ raise FormdefImportError(_('Missing name'))
# if the tree we get is actually a ElementTree for real, we get its
# root element and go on happily.
@@ -1124,7 +1124,7 @@ class FormDef(StorableObject):
tree = tree.getroot()
if tree.tag != cls.xml_root_node:
- raise FormdefImportError(N_('Unexpected root node'))
+ raise FormdefImportError(_('Unexpected root node'))
if include_id and tree.attrib.get('id'):
formdef.id = tree.attrib.get('id')
@@ -1145,7 +1145,7 @@ class FormDef(StorableObject):
try:
field_o = fields.get_field_class_by_type(field.findtext('type'))()
except KeyError:
- raise FormdefImportError(N_('Unknown field type'), details=field.findtext('type'))
+ raise FormdefImportError(_('Unknown field type'), details=field.findtext('type'))
field_o.init_with_xml(field, charset, include_id=True)
if fix_on_error or not field_o.id:
# this assumes all fields will have id, or none of them
@@ -1299,7 +1299,7 @@ class FormDef(StorableObject):
if unknown_datasources:
raise FormdefImportError(
- N_('Unknown datasources'), details=', '.join(sorted(unknown_datasources))
+ _('Unknown datasources'), details=', '.join(sorted(unknown_datasources))
)
return formdef
@@ -1340,7 +1340,7 @@ class FormDef(StorableObject):
else:
details.append('%s' % field.get_rst_view_value(value, indent=' '))
details.append('')
- return '\n'.join(details)
+ return '\n'.join([str(x) for x in details])
def get_submitter_email(self, formdata):
users_cfg = get_cfg('users', {})
@@ -1434,7 +1434,7 @@ class FormDef(StorableObject):
details.append(' %s' % formdata.get_status_label())
if evo.comment:
details.append('\n%s\n' % evo.comment)
- return '\n\n----\n\n' + '\n'.join(details)
+ return '\n\n----\n\n' + '\n'.join([str(x) for x in details])
def is_of_concern_for_role_id(self, role_id):
if not self.workflow_roles:
@@ -1618,11 +1618,11 @@ class FormDef(StorableObject):
EmailsDirectory.register(
'new_user',
- N_('Notification of creation to user'),
+ _('Notification of creation to user'),
enabled=False,
- category=N_('Workflow'),
- default_subject=N_('New form ({{ form_name }})'),
- default_body=N_(
+ category=_('Workflow'),
+ default_subject=_('New form ({{ form_name }})'),
+ default_body=_(
'''\
Hello,
@@ -1642,10 +1642,10 @@ For reference, here are the details:
EmailsDirectory.register(
'change_user',
- N_('Notification of change to user'),
- category=N_('Workflow'),
- default_subject=N_('Form status change ({{ form_name }})'),
- default_body=N_(
+ _('Notification of change to user'),
+ category=_('Workflow'),
+ default_subject=_('Form status change ({{ form_name }})'),
+ default_body=_(
'''\
Hello,
@@ -1669,11 +1669,11 @@ You can consult it with this link: {{ form_url }}
EmailsDirectory.register(
'new_receiver',
- N_('Notification of creation to receiver'),
+ _('Notification of creation to receiver'),
enabled=False,
- category=N_('Workflow'),
- default_subject=N_('New form ({{ form_name }})'),
- default_body=N_(
+ category=_('Workflow'),
+ default_subject=_('New form ({{ form_name }})'),
+ default_body=_(
'''\
Hello,
@@ -1692,10 +1692,10 @@ For reference, here are the details:
EmailsDirectory.register(
'change_receiver',
- N_('Notification of change to receiver'),
- category=N_('Workflow'),
- default_subject=N_('Form status change ({{ form_name }})'),
- default_body=N_(
+ _('Notification of change to receiver'),
+ category=_('Workflow'),
+ default_subject=_('Form status change ({{ form_name }})'),
+ default_body=_(
'''\
Hello,
@@ -1715,7 +1715,7 @@ Status of the form just changed (from "{{ form_previous_status }}" to "{{ form_s
),
)
-Substitutions.register('form_name', category=N_('Form'), comment=N_('Form Name'))
+Substitutions.register('form_name', category=_('Form'), comment=_('Form Name'))
def clean_drafts(publisher):
diff --git a/wcs/forms/actions.py b/wcs/forms/actions.py
index 3544d25fe..3a56292a6 100644
--- a/wcs/forms/actions.py
+++ b/wcs/forms/actions.py
@@ -22,14 +22,14 @@ from wcs.formdef import FormDef
from wcs.forms.common import FormTemplateMixin
from wcs.wf.jump import jump_and_perform
-from ..qommon import N_, errors, misc, template, tokens
+from ..qommon import _, errors, misc, template, tokens
from ..qommon.form import Form
class MissingOrExpiredToken(PublishError):
status_code = 404
- title = N_('Error')
- description = N_('This action link has already been used or has expired.')
+ title = _('Error')
+ description = _('This action link has already been used or has expired.')
class ActionsDirectory(Directory):
diff --git a/wcs/forms/root.py b/wcs/forms/root.py
index bd2c930e9..764cccab4 100644
--- a/wcs/forms/root.py
+++ b/wcs/forms/root.py
@@ -44,7 +44,7 @@ from wcs.roles import logged_users_role
from wcs.variables import LazyFormDef
from wcs.workflows import Workflow, WorkflowBackofficeFieldsFormDef, WorkflowStatusItem
-from ..qommon import N_, _, emails, errors, get_cfg, get_logger, misc, template
+from ..qommon import _, emails, errors, get_cfg, get_logger, misc, template
from ..qommon.admin.emails import EmailsDirectory
from ..qommon.form import CheckboxWidget, EmailWidget, Form, HiddenErrorWidget, HtmlWidget, StringWidget
from ..qommon.template import TemplateError
@@ -1962,14 +1962,14 @@ class PublicFormStatusPage(FormStatusPage):
)
-TextsDirectory.register('welcome-logged', N_('Welcome text on home page for logged users'))
+TextsDirectory.register('welcome-logged', _('Welcome text on home page for logged users'))
-TextsDirectory.register('welcome-unlogged', N_('Welcome text on home page for unlogged users'))
+TextsDirectory.register('welcome-unlogged', _('Welcome text on home page for unlogged users'))
TextsDirectory.register(
'captcha-page',
- N_('Explanation text before the CAPTCHA'),
- default=N_(
+ _('Explanation text before the CAPTCHA'),
+ default=_(
'''
Verification
@@ -1981,44 +1981,44 @@ In order to submit the form you need to complete this simple question.
TextsDirectory.register(
'form-recorded',
- N_('Message when a form has been recorded'),
- category=N_('Forms'),
- default=N_(
- 'The form has been recorded on {{ form_receipt_datetime }} with the number {{ form_number }}.'
- ),
+ _('Message when a form has been recorded'),
+ category=_('Forms'),
+ default=_('The form has been recorded on {{ form_receipt_datetime }} with the number {{ form_number }}.'),
)
TextsDirectory.register(
'form-recorded-allow-one',
- N_('Message when a form has been recorded, and the form is set to only allow one per user'),
- category=N_('Forms'),
- default=N_('The form has been recorded on {{ form_receipt_datetime }}.'),
+ _('Message when a form has been recorded, and the form is set to only allow one per user'),
+ category=_('Forms'),
+ default=_('The form has been recorded on {{ form_receipt_datetime }}.'),
)
TextsDirectory.register(
'check-before-submit',
- N_('Message when a form is displayed before validation'),
- category=N_('Forms'),
- default=N_('Check values then click submit.'),
+ _('Message when a form is displayed before validation'),
+ category=_('Forms'),
+ default=_('Check values then click submit.'),
)
TextsDirectory.register(
'tracking-code-email-dialog',
- N_('Message in tracking code popup dialog'),
- category=N_('Forms'),
- default=N_('You can get a reminder of the tracking code by email.'),
+ _('Message in tracking code popup dialog'),
+ category=_('Forms'),
+ default=_('You can get a reminder of the tracking code by email.'),
)
TextsDirectory.register(
- 'tracking-code-short-text', N_('Short text in the tracking code box'), category=N_('Forms')
+ 'tracking-code-short-text',
+ _('Short text in the tracking code box'),
+ category=_('Forms'),
)
EmailsDirectory.register(
'tracking-code-reminder',
- N_('Tracking Code'),
- category=N_('Miscellaneous'),
- default_subject=N_('Tracking Code reminder'),
- default_body=N_(
+ _('Tracking Code'),
+ category=_('Miscellaneous'),
+ default_subject=_('Tracking Code reminder'),
+ default_body=_(
'''\
Hello,
diff --git a/wcs/logged_errors.py b/wcs/logged_errors.py
index fcc25f46f..1dd7fb1a5 100644
--- a/wcs/logged_errors.py
+++ b/wcs/logged_errors.py
@@ -59,7 +59,7 @@ class LoggedError:
exception=None,
):
error = cls()
- error.summary = error_summary
+ error.summary = str(error_summary)
error.traceback = plain_error_msg
error.expression = expression
error.expression_type = expression_type
diff --git a/wcs/portfolio.py b/wcs/portfolio.py
index 52354daf7..f76899a2b 100644
--- a/wcs/portfolio.py
+++ b/wcs/portfolio.py
@@ -25,7 +25,7 @@ from quixote.html import TemplateIO, htmltext
from wcs.api_utils import get_secret_and_orig, sign_url
-from .qommon import N_, get_logger
+from .qommon import _, get_logger
from .qommon.misc import http_post_request, json_loads, urlopen
@@ -84,7 +84,7 @@ def push_document(user, filename, stream):
if get_response():
get_response().add_after_job(
- N_('Sending file %(filename)s in portfolio of %(user_name)s')
+ _('Sending file %(filename)s in portfolio of %(user_name)s')
% {'filename': filename, 'user_name': user.display_name},
afterjob,
)
diff --git a/wcs/qommon/__init__.py b/wcs/qommon/__init__.py
index 3d33d46a2..abfd22085 100644
--- a/wcs/qommon/__init__.py
+++ b/wcs/qommon/__init__.py
@@ -21,6 +21,7 @@ import threading
import django.apps
from django.conf import settings
from django.utils.encoding import force_text
+from django.utils.functional import lazy
from quixote import get_publisher
try:
@@ -37,7 +38,7 @@ force_str = force_text
PICKLE_KWARGS = {'encoding': 'bytes', 'fix_imports': True}
-def _(message):
+def gettext(message):
pub = get_publisher()
if pub is None:
return message
@@ -55,6 +56,8 @@ def N_(x):
return x
+_ = lazy(gettext, str)
+
from . import publisher # noqa pylint: disable=wrong-import-position
from .publisher import get_cfg # noqa pylint: disable=wrong-import-position
from .publisher import get_logger # noqa pylint: disable=wrong-import-position
diff --git a/wcs/qommon/admin/emails.py b/wcs/qommon/admin/emails.py
index 6b022b2a6..ef70f06b4 100644
--- a/wcs/qommon/admin/emails.py
+++ b/wcs/qommon/admin/emails.py
@@ -61,9 +61,7 @@ class EmailsDirectory(Directory):
emails_cfg = get_cfg('emails', {})
cfg_key = 'email-%s' % email_key
default_subject = cls.emails_dict[email_key].get('default_subject')
- if default_subject:
- default_subject = _(default_subject)
- real_subject = emails_cfg.get(cfg_key + '_subject') or default_subject
+ real_subject = emails_cfg.get(cfg_key + '_subject') or str(default_subject)
return real_subject
@classmethod
@@ -71,9 +69,7 @@ class EmailsDirectory(Directory):
emails_cfg = get_cfg('emails', {})
cfg_key = 'email-%s' % email_key
default_body = cls.emails_dict[email_key].get('default_body')
- if default_body:
- default_body = _(default_body)
- real_body = emails_cfg.get(cfg_key) or default_body
+ real_body = emails_cfg.get(cfg_key) or str(default_body)
return real_body
def options(self):
@@ -193,7 +189,7 @@ class EmailsDirectory(Directory):
categories = {}
for k, v in self.emails_dict.items():
if v.get('category'):
- translated_category = _(v.get('category'))
+ translated_category = v.get('category')
else:
translated_category = _('Miscellaneous')
if translated_category not in categories:
@@ -205,11 +201,11 @@ class EmailsDirectory(Directory):
r += htmltext('
') % (email_key, email_values['description'])
r += htmltext('
')
r += htmltext('
')
@@ -221,8 +217,8 @@ class EmailsDirectory(Directory):
emails_cfg = get_cfg('emails', {})
cfg_key = 'email-%s' % email_key
- default_subject = _(self.emails_dict[email_key].get('default_subject'))
- default_body = _(self.emails_dict[email_key].get('default_body'))
+ default_subject = self.emails_dict[email_key].get('default_subject')
+ default_body = self.emails_dict[email_key].get('default_body')
displayed_subject = emails_cfg.get(cfg_key + '_subject') or default_subject
displayed_body = emails_cfg.get(cfg_key) or default_body
@@ -273,8 +269,8 @@ class EmailsDirectory(Directory):
if check_template and not check_template(template):
return False
- default_subject = _(self.emails_dict[email_key].get('default_subject'))
- default_body = _(self.emails_dict[email_key].get('default_body'))
+ default_subject = self.emails_dict[email_key].get('default_subject')
+ default_body = self.emails_dict[email_key].get('default_body')
if template == default_body:
template = None
@@ -298,8 +294,8 @@ class EmailsDirectory(Directory):
return self.email(
component,
- _(self.emails_dict[component]['description']),
- _(self.emails_dict[component]['hint']),
+ self.emails_dict[component]['description'],
+ self.emails_dict[component]['hint'],
self.emails_dict[component]['enabled'],
)
diff --git a/wcs/qommon/admin/menu.py b/wcs/qommon/admin/menu.py
index 90b16a7d3..45f8106a4 100644
--- a/wcs/qommon/admin/menu.py
+++ b/wcs/qommon/admin/menu.py
@@ -19,7 +19,7 @@ import re
from quixote import get_publisher, get_request
from quixote.html import TemplateIO, htmltext
-from .. import N_, _
+from .. import _
from ..backoffice.menu import html_top
@@ -113,16 +113,16 @@ def get_vc_version():
def command_icon(url, type, label=None, popup=False):
labels = {
- 'edit': N_('Edit'),
- 'remove': N_('Remove'),
- 'duplicate': N_('Duplicate'),
- 'view': N_('View'),
+ 'edit': _('Edit'),
+ 'remove': _('Remove'),
+ 'duplicate': _('Duplicate'),
+ 'view': _('View'),
}
if label:
klass = 'button'
else:
klass = ''
- label = _(labels[type])
+ label = labels[type]
root_url = get_publisher().get_application_static_files_root_url()
rel = ''
if popup:
diff --git a/wcs/qommon/admin/texts.py b/wcs/qommon/admin/texts.py
index 99ee3d8a2..e043eeca8 100644
--- a/wcs/qommon/admin/texts.py
+++ b/wcs/qommon/admin/texts.py
@@ -41,7 +41,7 @@ class TextsDirectory(Directory):
if os.path.exists(filepath):
return htmltext(open(filepath).read())
return ''
- text = _(default)
+ text = str(default) # make sure translation is applied
if not text.startswith('<'):
text = '
%s
' % text
@@ -79,7 +79,7 @@ class TextsDirectory(Directory):
categories = {}
for k, v in self.texts_dict.items():
if v.get('category'):
- translated_category = _(v.get('category'))
+ translated_category = v.get('category')
else:
translated_category = _('Miscellaneous')
if translated_category not in categories:
@@ -91,11 +91,11 @@ class TextsDirectory(Directory):
r += htmltext('
') % ADMIN_TITLE
r += get_session().display_message()
@@ -958,7 +958,7 @@ class MethodUserDirectory(Directory):
return actions
def email(self):
- html_top('users', title=_(ADMIN_TITLE))
+ html_top('users', title=ADMIN_TITLE)
r = TemplateIO(html=True)
get_response().breadcrumb.append(('email', 'Email Password'))
r += htmltext('
%s
') % _('Email Password')
@@ -1065,11 +1065,11 @@ class PasswordAuthMethod(AuthMethod):
EmailsDirectory.register(
'password-subscription-notification',
- N_('Subscription notification for password account'),
- N_('Available variables: email, website, token_url, token, admin_email, username, password'),
- category=N_('Identification'),
- default_subject=N_('Subscription Confirmation'),
- default_body=N_(
+ _('Subscription notification for password account'),
+ _('Available variables: email, website, token_url, token, admin_email, username, password'),
+ category=_('Identification'),
+ default_subject=_('Subscription Confirmation'),
+ default_body=_(
'''\
We have received a request for subscription of your email address,
"[email]", to the [website] web site.
@@ -1089,11 +1089,11 @@ to [admin_email].
EmailsDirectory.register(
'change-password-request',
- N_('Request for password change'),
- N_('Available variables: change_url, cancel_url, token, time'),
- category=N_('Identification'),
- default_subject=N_('Change Password Request'),
- default_body=N_(
+ _('Request for password change'),
+ _('Available variables: change_url, cancel_url, token, time'),
+ category=_('Identification'),
+ default_subject=_('Change Password Request'),
+ default_body=_(
"""\
You have (or someone impersonating you has) requested to change your
password. To complete the change, visit the following link:
@@ -1114,11 +1114,11 @@ If you do nothing, the request will lapse after 3 days (precisely on
EmailsDirectory.register(
'new-generated-password',
- N_('New generated password'),
- N_('Available variables: username, password, hostname'),
- category=N_('Identification'),
- default_subject=N_('Your new password'),
- default_body=N_(
+ _('New generated password'),
+ _('Available variables: username, password, hostname'),
+ category=_('Identification'),
+ default_subject=_('Your new password'),
+ default_body=_(
'''\
Hello,
@@ -1134,11 +1134,11 @@ account details:
EmailsDirectory.register(
'new-account-approved',
- N_('Approval of new account'),
- N_('Available variables: username, password'),
- category=N_('Identification'),
- default_subject=N_('Your account has been approved'),
- default_body=N_(
+ _('Approval of new account'),
+ _('Available variables: username, password'),
+ category=_('Identification'),
+ default_subject=_('Your account has been approved'),
+ default_body=_(
'''\
Your account has been approved.
@@ -1152,11 +1152,11 @@ Account details:
EmailsDirectory.register(
'warning-about-unused-account',
- N_('Warning about unusued account'),
- N_('Available variables: username'),
- category=N_('Identification'),
- default_subject=N_('Your account is unused'),
- default_body=N_(
+ _('Warning about unusued account'),
+ _('Available variables: username'),
+ category=_('Identification'),
+ default_subject=_('Your account is unused'),
+ default_body=_(
'''\
Your account ([username]) is not being used.
'''
@@ -1165,11 +1165,11 @@ Your account ([username]) is not being used.
EmailsDirectory.register(
'notification-of-removed-account',
- N_('Notification of removal of unused account'),
- N_('Available variables: username'),
- category=N_('Identification'),
- default_subject=N_('Your account has been removed'),
- default_body=N_(
+ _('Notification of removal of unused account'),
+ _('Available variables: username'),
+ category=_('Identification'),
+ default_subject=_('Your account has been removed'),
+ default_body=_(
'''\
Your account ([username]) was not being used, it has therefore been removed.
'''
@@ -1178,11 +1178,11 @@ Your account ([username]) was not being used, it has therefore been removed.
EmailsDirectory.register(
'new-registration-admin-notification',
- N_('Notification of new registration to administrators'),
- N_('Available variables: hostname, email_as_username, username'),
- category=N_('Identification'),
- default_subject=N_('New Registration'),
- default_body=N_(
+ _('Notification of new registration to administrators'),
+ _('Available variables: hostname, email_as_username, username'),
+ category=_('Identification'),
+ default_subject=_('New Registration'),
+ default_body=_(
'''\
Hello,
@@ -1196,11 +1196,11 @@ A new account has been created on [hostname].
EmailsDirectory.register(
'new-account-generated-password',
- N_('Welcome email, with generated password'),
- N_('Available variables: hostname, username, password, email_as_username'),
- category=N_('Identification'),
- default_subject=N_('Welcome to [hostname]'),
- default_body=N_(
+ _('Welcome email, with generated password'),
+ _('Available variables: hostname, username, password, email_as_username'),
+ category=_('Identification'),
+ default_subject=_('Welcome to [hostname]'),
+ default_body=_(
'''\
Welcome to [hostname],
@@ -1211,11 +1211,11 @@ Your password is: [password]
EmailsDirectory.register(
'password-email-create-anew',
- N_('Email with a new password for the user'),
- N_('Available variables: hostname, name, username, password'),
- category=N_('Identification'),
- default_subject=N_('Your new password for [hostname]'),
- default_body=N_(
+ _('Email with a new password for the user'),
+ _('Available variables: hostname, name, username, password'),
+ category=_('Identification'),
+ default_subject=_('Your new password for [hostname]'),
+ default_body=_(
'''\
Hello [name],
@@ -1226,11 +1226,11 @@ Here is your new password for [hostname]: [password]
EmailsDirectory.register(
'password-email-current',
- N_('Email with current password for the user'),
- N_('Available variables: hostname, name, username, password'),
- category=N_('Identification'),
- default_subject=N_('Your password for [hostname]'),
- default_body=N_(
+ _('Email with current password for the user'),
+ _('Available variables: hostname, name, username, password'),
+ category=_('Identification'),
+ default_subject=_('Your password for [hostname]'),
+ default_body=_(
'''\
Hello [name],
@@ -1242,9 +1242,9 @@ Here is your password for [hostname]: [password]
TextsDirectory.register(
'account-created',
- N_('Text when account confirmed by user'),
- category=N_('Identification'),
- default=N_(
+ _('Text when account confirmed by user'),
+ category=_('Identification'),
+ default=_(
'''
Your account has been created.
'''
@@ -1253,9 +1253,9 @@ Your account has been created.
TextsDirectory.register(
'password-forgotten-token-sent',
- N_('Text when an email with a change password token has been sent'),
- category=N_('Identification'),
- default=N_(
+ _('Text when an email with a change password token has been sent'),
+ category=_('Identification'),
+ default=_(
'''
A token for changing your password has been emailed to you. Follow the instructions in that email to change your password.
@@ -1267,9 +1267,9 @@ A token for changing your password has been emailed to you. Follow the instructi
TextsDirectory.register(
'new-password-sent-by-email',
- N_('Text when new password has been sent'),
- category=N_('Identification'),
- default=N_(
+ _('Text when new password has been sent'),
+ category=_('Identification'),
+ default=_(
'''
Your new password has been sent to you by email.
@@ -1279,13 +1279,13 @@ Your new password has been sent to you by email.
),
)
-TextsDirectory.register('new-account', N_('Text on top of registration form'), category=N_('Identification'))
+TextsDirectory.register('new-account', _('Text on top of registration form'), category=_('Identification'))
TextsDirectory.register(
'password-forgotten-link',
- N_('Text on login page, linking to the forgotten password request page'),
- category=N_('Identification'),
- default=N_(
+ _('Text on login page, linking to the forgotten password request page'),
+ category=_('Identification'),
+ default=_(
'''
If you have an account, but have forgotten your password, you should go
to the Lost password page and submit a request
@@ -1296,9 +1296,9 @@ to change your password.
TextsDirectory.register(
'password-forgotten-enter-username',
- N_('Text on forgotten password request page'),
- category=N_('Identification'),
- default=N_(
+ _('Text on forgotten password request page'),
+ category=_('Identification'),
+ default=_(
'''
If you have an account, but have forgotten your password, enter your user name
below and submit a request to change your password.
@@ -1308,10 +1308,10 @@ below and submit a request to change your password.
TextsDirectory.register(
'password-account-link-to-register-page',
- N_('Text linking the login page to the account creation page'),
- hint=N_('Available variable: register_url'),
- category=N_('Identification'),
- default=N_(
+ _('Text linking the login page to the account creation page'),
+ hint=_('Available variable: register_url'),
+ category=_('Identification'),
+ default=_(
'''
If you do not have an account, you should go to the
New Account page.
@@ -1321,22 +1321,22 @@ New Account page.
TextsDirectory.register(
'invalid-password-token',
- N_('Text when an invalid password token is used'),
- category=N_('Identification'),
- default=N_(
+ _('Text when an invalid password token is used'),
+ category=_('Identification'),
+ default=_(
'''
Sorry, the token you used is invalid, or has already been used.
'''
),
)
-TextsDirectory.register('top-of-login', N_('Text on top of the login page'), category=N_('Identification'))
+TextsDirectory.register('top-of-login', _('Text on top of the login page'), category=_('Identification'))
TextsDirectory.register(
'email-sent-confirm-creation',
- N_('Text when a mail for confirmation of an account creation has been sent'),
- category=N_('Identification'),
- default=N_('An email has been sent to you so you can confirm your account creation.'),
+ _('Text when a mail for confirmation of an account creation has been sent'),
+ category=_('Identification'),
+ default=_('An email has been sent to you so you can confirm your account creation.'),
)
diff --git a/wcs/qommon/misc.py b/wcs/qommon/misc.py
index 127b240cb..1984cb83b 100644
--- a/wcs/qommon/misc.py
+++ b/wcs/qommon/misc.py
@@ -600,6 +600,10 @@ class JSONEncoder(json.JSONEncoder):
'content': base64.b64encode(obj.get_content()),
}
+ if obj.__class__.__name__ == '__proxy__':
+ # lazy gettext
+ return str(obj)
+
# Let the base class default method raise the TypeError
return json.JSONEncoder.default(self, obj)
@@ -620,7 +624,7 @@ def json_response(data):
get_response().set_header('Access-Control-Allow-Origin', get_request().get_environ('HTTP_ORIGIN'))
get_response().set_header('Access-Control-Allow-Credentials', 'true')
get_response().set_header('Access-Control-Allow-Headers', 'x-requested-with')
- json_str = json.dumps(data)
+ json_str = json.dumps(data, cls=JSONEncoder)
for variable in ('jsonpCallback', 'callback'):
if variable in get_request().form:
get_response().set_content_type('application/javascript')
@@ -1002,6 +1006,7 @@ def get_document_types(current_document_type):
document_types.update(get_cfg('filetypes', {}))
for key, document_type in document_types.items():
document_type['id'] = key
+ document_type['label'] = str(document_type['label'])
# add current file type if it does not exist anymore in the settings
cur_dt = current_document_type
if cur_dt and cur_dt['id'] not in document_types:
diff --git a/wcs/qommon/myspace.py b/wcs/qommon/myspace.py
index 1a226b99f..1f4087410 100644
--- a/wcs/qommon/myspace.py
+++ b/wcs/qommon/myspace.py
@@ -20,7 +20,7 @@ from quixote.html import TemplateIO, htmltext
from wcs.qommon.admin.texts import TextsDirectory
-from . import N_, _, errors, get_cfg, template
+from . import _, errors, get_cfg, template
from .form import Form, HtmlWidget, PasswordWidget
from .ident.password import check_password
from .ident.password_accounts import PasswordAccount
@@ -194,4 +194,4 @@ class MyspaceDirectory(Directory):
return form.render()
-TextsDirectory.register('top-of-profile', N_('Text on top of the profile page'))
+TextsDirectory.register('top-of-profile', _('Text on top of the profile page'))
diff --git a/wcs/qommon/publisher.py b/wcs/qommon/publisher.py
index fce616661..a32a86a00 100644
--- a/wcs/qommon/publisher.py
+++ b/wcs/qommon/publisher.py
@@ -45,7 +45,7 @@ from django.utils import translation
from django.utils.encoding import force_bytes, force_text
from quixote.publish import Publisher, get_publisher, get_request, get_response
-from . import N_, _, errors, force_str, logger, storage, template
+from . import _, errors, force_str, logger, storage, template
from .cron import CronJob
from .http_request import HTTPRequest
from .http_response import AfterJob, HTTPResponse
@@ -185,7 +185,13 @@ class QommonPublisher(Publisher):
request.response.headers['WWW-Authenticate'] = 'Basic realm="%s"' % exc.realm
if request.is_json():
request.response.set_content_type('application/json')
- return json.dumps({'err': 1, 'err_class': exc.title, 'err_desc': exc.public_msg})
+ return json.dumps(
+ {
+ 'err': 1,
+ 'err_class': str(exc.title),
+ 'err_desc': str(exc.public_msg) if exc.public_msg else None,
+ }
+ )
if isinstance(exc, errors.TraversalError):
raise Http404()
output = self.format_publish_error(exc)
@@ -1003,9 +1009,9 @@ def get_publisher_class():
return builtins.__dict__.get('__publisher_class')
-Substitutions.register('site_name', category=N_('General'), comment=N_('Site Name'))
-Substitutions.register('site_theme', category=N_('General'), comment=N_('Current Theme Name'))
-Substitutions.register('site_url', category=N_('General'), comment=N_('Site URL'))
-Substitutions.register('site_url_backoffice', category=N_('General'), comment=N_('Site URL (backoffice)'))
-Substitutions.register('today', category=N_('General'), comment=N_('Current Date'))
-Substitutions.register('now', category=N_('General'), comment=N_('Current Date & Time'))
+Substitutions.register('site_name', category=_('General'), comment=_('Site Name'))
+Substitutions.register('site_theme', category=_('General'), comment=_('Current Theme Name'))
+Substitutions.register('site_url', category=_('General'), comment=_('Site URL'))
+Substitutions.register('site_url_backoffice', category=_('General'), comment=_('Site URL (backoffice)'))
+Substitutions.register('today', category=_('General'), comment=_('Current Date'))
+Substitutions.register('now', category=_('General'), comment=_('Current Date & Time'))
diff --git a/wcs/qommon/sessions.py b/wcs/qommon/sessions.py
index 3729d5c69..68e3ea3a9 100644
--- a/wcs/qommon/sessions.py
+++ b/wcs/qommon/sessions.py
@@ -153,6 +153,9 @@ class Session(QommonSession, CaptchaSession, StorableObject):
return odict
def store(self, *args, **kwargs):
+ if self.message:
+ # escape lazy gettext
+ self.message = (self.message[0], str(self.message[1]))
current_dict = copy.copy(self.__dict__)
orig_dict = current_dict.pop('__orig_dict__', {})
current_dict.pop('_access_time', None)
diff --git a/wcs/qommon/substitution.py b/wcs/qommon/substitution.py
index b2bee529e..b0cd41117 100644
--- a/wcs/qommon/substitution.py
+++ b/wcs/qommon/substitution.py
@@ -144,7 +144,7 @@ class Substitutions:
% (_('Category'), _('Variable'), _('Comment'))
)
r += htmltext('')
- vars = [(_(y.get('category')), x, _(y.get('comment'))) for x, y in cls.substitutions_dict.items()]
+ vars = [(y.get('category'), x, y.get('comment')) for x, y in cls.substitutions_dict.items()]
for dynamic_source in cls.dynamic_sources:
vars.extend(dynamic_source.get_substitution_variables_list())
vars.sort()
diff --git a/wcs/qommon/template.py b/wcs/qommon/template.py
index b78ca7bb2..45310bfff 100644
--- a/wcs/qommon/template.py
+++ b/wcs/qommon/template.py
@@ -458,7 +458,7 @@ def ezt_raises(exception, on_parse=False):
message = _('syntax error in ezt template: %s')
else:
message = _('failure to render ezt template: %s')
- raise TemplateError(message % ' '.join(parts))
+ raise TemplateError(message % ' '.join([str(x) for x in parts]))
class Template:
diff --git a/wcs/root.py b/wcs/root.py
index 6c370e51e..e1ada4799 100644
--- a/wcs/root.py
+++ b/wcs/root.py
@@ -33,7 +33,7 @@ from .forms import root
from .forms.actions import ActionsDirectory
from .forms.preview import PreviewDirectory
from .myspace import MyspaceDirectory
-from .qommon import _, errors, get_cfg, get_logger, ident, saml2, template
+from .qommon import _, errors, get_cfg, get_logger, ident, misc, saml2, template
from .qommon.afterjobs import AfterJobStatusDirectory
from .qommon.form import Form, RadiobuttonsWidget
from .qommon.pages import PagesDirectory
@@ -92,7 +92,7 @@ class LoginDirectory(Directory):
RadiobuttonsWidget,
'method',
options=[
- (x.key, _(x.description)) for x in ident.get_method_classes() if x.key in ident_methods
+ (x.key, x.description) for x in ident.get_method_classes() if x.key in ident_methods
],
delim=htmltext(' '),
)
@@ -157,7 +157,7 @@ class RegisterDirectory(Directory):
RadiobuttonsWidget,
'method',
options=[
- (x.key, _(x.description)) for x in ident.get_method_classes() if x.key in ident_methods
+ (x.key, x.description) for x in ident.get_method_classes() if x.key in ident_methods
],
delim=htmltext(' '),
)
@@ -344,7 +344,7 @@ class RootDirectory(Directory):
# they would propose the returned json content for download if
# it was served with the appropriate content type :/
get_response().set_content_type('text/plain')
- return json.dumps(results)
+ return json.dumps(results, cls=misc.JSONEncoder)
def feed_substitution_parts(self):
get_publisher().substitutions.feed(get_session())
@@ -446,7 +446,7 @@ class RootDirectory(Directory):
'email_domain_suggest': _('Did you want to write'),
'email_domain_fix': _('Apply fix'),
}
- return 'WCS_I18N = %s;\n' % json.dumps(strings)
+ return 'WCS_I18N = %s;\n' % json.dumps(strings, cls=misc.JSONEncoder)
admin = None
backoffice = None
diff --git a/wcs/snapshots.py b/wcs/snapshots.py
index 4eab8ad14..d514272a3 100644
--- a/wcs/snapshots.py
+++ b/wcs/snapshots.py
@@ -25,7 +25,7 @@ from wcs.qommon.storage import Null
class UnknownUser:
def __str__(self):
- return _('unknown user')
+ return str(_('unknown user'))
class Snapshot:
@@ -53,7 +53,7 @@ class Snapshot:
if get_session():
obj.user_id = get_session().user
obj.serialization = ET.tostring(instance.export_to_xml(include_id=True)).decode('utf-8')
- obj.comment = comment
+ obj.comment = str(comment) if comment else None
obj.label = label
latest = cls.get_latest(obj.object_type, obj.object_id)
if label is not None or latest is None or obj.serialization != latest.serialization:
diff --git a/wcs/sql.py b/wcs/sql.py
index e3fee9037..793af6686 100644
--- a/wcs/sql.py
+++ b/wcs/sql.py
@@ -2645,6 +2645,9 @@ class Session(SqlMixin, wcs.sessions.BasicSession):
@guard_postgres
def store(self):
+ if self.message:
+ # escape lazy gettext
+ self.message = (self.message[0], str(self.message[1]))
sql_dict = {
'id': self.id,
'session_data': bytearray(pickle.dumps(self.__dict__, protocol=2)),
diff --git a/wcs/templates/wcs/backoffice/logged-error.html b/wcs/templates/wcs/backoffice/logged-error.html
index 502b5857e..33ccaa117 100644
--- a/wcs/templates/wcs/backoffice/logged-error.html
+++ b/wcs/templates/wcs/backoffice/logged-error.html
@@ -11,7 +11,7 @@
{% trans "Latest occurence:" %} {{ error.latest_occurence_timestamp }}